From a0e453ebe54f419ee2721134d33938a890eb7e40 Mon Sep 17 00:00:00 2001 From: Marco Hugentobler Date: Fri, 28 Jul 2017 14:17:40 +0200 Subject: [PATCH 001/364] Show toplevel wms group in browser --- src/providers/wms/qgswmsdataitems.cpp | 34 +++++---------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/src/providers/wms/qgswmsdataitems.cpp b/src/providers/wms/qgswmsdataitems.cpp index d44c11150e8..00b52ffd19e 100644 --- a/src/providers/wms/qgswmsdataitems.cpp +++ b/src/providers/wms/qgswmsdataitems.cpp @@ -94,37 +94,15 @@ QVector QgsWMSConnectionItem::createChildren() QgsWmsCapabilitiesProperty capabilitiesProperty = caps.capabilitiesProperty(); const QgsWmsCapabilityProperty &capabilityProperty = capabilitiesProperty.capability; - // If we have several top-level layers, or if we just have one single top-level layer, - // then use those top-level layers directly - if ( capabilityProperty.layers.size() > 1 || - ( capabilityProperty.layers.size() == 1 && capabilityProperty.layers[0].layer.size() == 0 ) ) + Q_FOREACH ( const QgsWmsLayerProperty &layerProperty, capabilityProperty.layers ) { - Q_FOREACH ( const QgsWmsLayerProperty &layerProperty, capabilityProperty.layers ) - { - // Attention, the name may be empty - QgsDebugMsg( QString::number( layerProperty.orderId ) + ' ' + layerProperty.name + ' ' + layerProperty.title ); - QString pathName = layerProperty.name.isEmpty() ? QString::number( layerProperty.orderId ) : layerProperty.name; + // Attention, the name may be empty + QgsDebugMsg( QString::number( layerProperty.orderId ) + ' ' + layerProperty.name + ' ' + layerProperty.title ); + QString pathName = layerProperty.name.isEmpty() ? QString::number( layerProperty.orderId ) : layerProperty.name; - QgsWMSLayerItem *layer = new QgsWMSLayerItem( this, layerProperty.title, mPath + '/' + pathName, capabilitiesProperty, uri, layerProperty ); + QgsWMSLayerItem *layer = new QgsWMSLayerItem( this, layerProperty.title, mPath + '/' + pathName, capabilitiesProperty, uri, layerProperty ); - children << layer; - } - } - // Otherwise if we have just one single top-level layers with children, then - // skip this top-level layer and iterate directly on its children - // Note (E. Rouault): this was the historical behavior before fixing #13762 - else if ( capabilityProperty.layers.size() == 1 ) - { - Q_FOREACH ( const QgsWmsLayerProperty &layerProperty, capabilityProperty.layers[0].layer ) - { - // Attention, the name may be empty - QgsDebugMsg( QString::number( layerProperty.orderId ) + ' ' + layerProperty.name + ' ' + layerProperty.title ); - QString pathName = layerProperty.name.isEmpty() ? QString::number( layerProperty.orderId ) : layerProperty.name; - - QgsWMSLayerItem *layer = new QgsWMSLayerItem( this, layerProperty.title, mPath + '/' + pathName, capabilitiesProperty, uri, layerProperty ); - - children << layer; - } + children << layer; } } From 4b5d81b3703e8b97701b6cfc089fe9bf9e9ce3f5 Mon Sep 17 00:00:00 2001 From: "arnaud.morvan@camptocamp.com" Date: Thu, 4 May 2017 13:38:14 +0200 Subject: [PATCH 002/364] [processing] Add Aggregate algorithm --- python/plugins/processing/algs/help/qgis.yaml | 11 + .../plugins/processing/algs/qgis/Aggregate.py | 269 ++++++++++++++++++ .../algs/qgis/QGISAlgorithmProvider.py | 2 + .../algs/qgis/ui/AggregatesPanel.py | 184 ++++++++++++ .../algs/qgis/ui/FieldsMappingPanel.py | 5 +- .../tests/testdata/expected/aggregate_all.gfs | 38 +++ .../tests/testdata/expected/aggregate_all.gml | 22 ++ .../testdata/expected/aggregate_field.gfs | 38 +++ .../testdata/expected/aggregate_field.gml | 56 ++++ .../testdata/expected/aggregate_lines.gfs | 22 ++ .../testdata/expected/aggregate_lines.gml | 19 ++ .../testdata/expected/aggregate_points.gfs | 33 +++ .../testdata/expected/aggregate_points.gml | 37 +++ .../expected/aggregate_two_fields.gfs | 38 +++ .../expected/aggregate_two_fields.gml | 63 ++++ .../tests/testdata/qgis_algorithm_tests.yaml | 198 +++++++++++++ 16 files changed, 1031 insertions(+), 4 deletions(-) create mode 100644 python/plugins/processing/algs/qgis/Aggregate.py create mode 100644 python/plugins/processing/algs/qgis/ui/AggregatesPanel.py create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_all.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_all.gml create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_field.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_field.gml create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_lines.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_lines.gml create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_points.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_points.gml create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gml diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index 9f3e6816b6e..0eb07954e2b 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -20,6 +20,17 @@ qgis:adduniquevalueindexfield: > qgis:advancedpythonfieldcalculator: > This algorithm adds a new attribute to a vector layer, with values resulting from applying an expression to each feature. The expression is defined as a Python function. +qgis:aggregate: > + This algorithm take a vector or table layer and aggregate features based on a group by expression. Features for which group by expression return the same value are grouped together. + + It is possible to group all source features together using constant value in group by parameter, example: NULL. + + It is also possible to group features using multiple fields using Array function, example: Array("Field1", "Field2"). + + Geometries (if present) are combined into one multipart geometry for each group. + + Output attributes are computed depending on each given aggregate definition. + qgis:barplot: qgis:basicstatisticsforfields: > diff --git a/python/plugins/processing/algs/qgis/Aggregate.py b/python/plugins/processing/algs/qgis/Aggregate.py new file mode 100644 index 00000000000..60c84b11e86 --- /dev/null +++ b/python/plugins/processing/algs/qgis/Aggregate.py @@ -0,0 +1,269 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + Aggregate.py + --------------------- + Date : February 2017 + Copyright : (C) 2017 by Arnaud Morvan + Email : arnaud dot morvan at camptocamp 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. * +* * +*************************************************************************** +""" + +__author__ = 'Arnaud Morvan' +__date__ = 'February 2017' +__copyright__ = '(C) 2017, Arnaud Morvan' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from qgis.core import ( + QgsDistanceArea, + QgsExpression, + QgsExpressionContextUtils, + QgsFeature, + QgsFeatureSink, + QgsField, + QgsFields, + QgsGeometry, + QgsProcessingParameterDefinition, + QgsProcessingParameterExpression, + QgsProcessingParameterFeatureSink, + QgsProcessingParameterFeatureSource, + QgsProcessingException, + QgsProcessingUtils, + QgsWkbTypes, +) + +from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm + + +class Aggregate(QgisAlgorithm): + + INPUT = 'INPUT' + GROUP_BY = 'GROUP_BY' + AGGREGATES = 'AGGREGATES' + DISSOLVE = 'DISSOLVE' + OUTPUT = 'OUTPUT' + + def group(self): + return self.tr('Vector geometry tools') + + def name(self): + return 'aggregate' + + def displayName(self): + return self.tr('Aggregate') + + def initAlgorithm(self, config=None): + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterExpression(self.GROUP_BY, + self.tr('Group by expression (NULL to group all features)'), + defaultValue='NULL', + optional=False, + parentLayerParameterName=self.INPUT)) + + class ParameterAggregates(QgsProcessingParameterDefinition): + + def __init__(self, name, description, parentLayerParameterName='INPUT'): + super().__init__(name, description) + self._parentLayerParameter = parentLayerParameterName + + def type(self): + return 'aggregates' + + def checkValueIsAcceptable(self, value, context=None): + if not isinstance(value, list): + return False + for field_def in value: + if not isinstance(field_def, dict): + return False + if not field_def.get('input', False): + return False + if not field_def.get('aggregate', False): + return False + if not field_def.get('name', False): + return False + if not field_def.get('type', False): + return False + return True + + def valueAsPythonString(self, value, context): + return str(value) + + def asScriptCode(self): + raise NotImplementedError() + + @classmethod + def fromScriptCode(cls, name, description, isOptional, definition): + raise NotImplementedError() + + def parentLayerParameter(self): + return self._parentLayerParameter + + self.addParameter(ParameterAggregates(self.AGGREGATES, + description=self.tr('Aggregates'))) + self.parameterDefinition(self.AGGREGATES).setMetadata({ + 'widget_wrapper': 'processing.algs.qgis.ui.AggregatesPanel.AggregatesWidgetWrapper' + }) + + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('Aggregated'))) + + def parameterAsAggregates(self, parameters, name, context): + return parameters[name] + + def prepareAlgorithm(self, parameters, context, feedback): + source = self.parameterAsSource(parameters, self.INPUT, context) + group_by = self.parameterAsExpression(parameters, self.GROUP_BY, context) + aggregates = self.parameterAsAggregates(parameters, self.AGGREGATES, context) + + da = QgsDistanceArea() + da.setSourceCrs(source.sourceCrs()) + da.setEllipsoid(context.project().ellipsoid()) + + self.source = source + self.group_by = group_by + self.group_by_expr = self.createExpression(group_by, da, context) + self.geometry_expr = self.createExpression('collect($geometry, {})'.format(group_by), da, context) + + self.fields = QgsFields() + self.fields_expr = [] + for field_def in aggregates: + self.fields.append(QgsField(name=field_def['name'], + type=field_def['type'], + typeName="", + len=field_def['length'], + prec=field_def['precision'])) + aggregate = field_def['aggregate'] + if aggregate == 'first_value': + expression = field_def['input'] + elif aggregate == 'concatenate': + expression = ('{}({}, {}, {}, \'{}\')' + .format(field_def['aggregate'], + field_def['input'], + group_by, + 'TRUE', + field_def['delimiter'])) + else: + expression = '{}({}, {})'.format(field_def['aggregate'], + field_def['input'], + group_by) + expr = self.createExpression(expression, da, context) + self.fields_expr.append(expr) + return True + + def processAlgorithm(self, parameters, context, feedback): + expr_context = self.createExpressionContext(parameters, context) + self.group_by_expr.prepare(expr_context) + + # Group features in memory layers + source = self.source + count = self.source.featureCount() + if count: + progress_step = 50.0 / count + current = 0 + groups = {} + keys = [] # We need deterministic order for the tests + feature = QgsFeature() + for feature in self.source.getFeatures(): + expr_context.setFeature(feature) + group_by_value = self.evaluateExpression(self.group_by_expr, expr_context) + + # Get an hashable key for the dict + key = group_by_value + if isinstance(key, list): + key = tuple(key) + + group = groups.get(key, None) + if group is None: + sink, id = QgsProcessingUtils.createFeatureSink( + 'memory:', + context, + source.fields(), + source.wkbType(), + source.sourceCrs()) + layer = QgsProcessingUtils.mapLayerFromString(id, context) + group = { + 'sink': sink, + 'layer': layer, + 'feature': feature + } + groups[key] = group + keys.append(key) + + group['sink'].addFeature(feature, QgsFeatureSink.FastInsert) + + current += 1 + feedback.setProgress(int(current * progress_step)) + if feedback.isCanceled(): + return + + (sink, dest_id) = self.parameterAsSink(parameters, + self.OUTPUT, + context, + self.fields, + QgsWkbTypes.multiType(source.wkbType()), + source.sourceCrs()) + + # Calculate aggregates on memory layers + if len(keys): + progress_step = 50.0 / len(keys) + for current, key in enumerate(keys): + group = groups[key] + expr_context = self.createExpressionContext(parameters, context) + expr_context.appendScope(QgsExpressionContextUtils.layerScope(group['layer'])) + expr_context.setFeature(group['feature']) + + geometry = self.evaluateExpression(self.geometry_expr, expr_context) + if geometry is not None and not geometry.isEmpty(): + geometry = QgsGeometry.unaryUnion(geometry.asGeometryCollection()) + if geometry.isEmpty(): + raise QgsProcessingException( + 'Impossible to combine geometries for {} = {}' + .format(self.group_by, group_by_value)) + + attrs = [] + for fields_expr in self.fields_expr: + attrs.append(self.evaluateExpression(fields_expr, expr_context)) + + # Write output feature + outFeat = QgsFeature() + if geometry is not None: + outFeat.setGeometry(geometry) + outFeat.setAttributes(attrs) + sink.addFeature(outFeat, QgsFeatureSink.FastInsert) + + feedback.setProgress(50 + int(current * progress_step)) + if feedback.isCanceled(): + return + + return {self.OUTPUT: dest_id} + + def createExpression(self, text, da, context): + expr = QgsExpression(text) + expr.setGeomCalculator(da) + expr.setDistanceUnits(context.project().distanceUnits()) + expr.setAreaUnits(context.project().areaUnits()) + if expr.hasParserError(): + raise QgsProcessingException( + self.tr(u'Parser error in expression "{}": {}') + .format(text, expr.parserErrorString())) + return expr + + def evaluateExpression(self, expr, context): + value = expr.evaluate(context) + if expr.hasEvalError(): + raise QgsProcessingException( + self.tr(u'Evaluation error in expression "{}": {}') + .format(expr.expression(), expr.evalErrorString())) + return value diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 83614f28225..84b0ec3039f 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -41,6 +41,7 @@ from processing.script.ScriptUtils import ScriptUtils from .QgisAlgorithm import QgisAlgorithm from .AddTableField import AddTableField +from .Aggregate import Aggregate from .Aspect import Aspect from .AutoincrementalField import AutoincrementalField from .BasicStatistics import BasicStatisticsForField @@ -204,6 +205,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # ExecuteSQL(), FindProjection(), # ] algs = [AddTableField(), + Aggregate(), Aspect(), AutoincrementalField(), BasicStatisticsForField(), diff --git a/python/plugins/processing/algs/qgis/ui/AggregatesPanel.py b/python/plugins/processing/algs/qgis/ui/AggregatesPanel.py new file mode 100644 index 00000000000..1a321b592e9 --- /dev/null +++ b/python/plugins/processing/algs/qgis/ui/AggregatesPanel.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + AggregatesPanel.py + --------------------- + Date : February 2017 + Copyright : (C) 2017 by Arnaud Morvan + Email : arnaud dot morvan at camptocamp 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. * +* * +*************************************************************************** +""" + +__author__ = 'Arnaud Morvan' +__date__ = 'February 2017' +__copyright__ = '(C) 2017, Arnaud Morvan' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from qgis.PyQt.QtCore import ( + QItemSelectionModel, + QAbstractTableModel, + QModelIndex, + QVariant, + Qt, + pyqtSlot, +) +from qgis.PyQt.QtGui import QBrush +from qgis.PyQt.QtWidgets import ( + QComboBox, + QHeaderView, + QLineEdit, + QSpacerItem, + QMessageBox, + QSpinBox, + QStyledItemDelegate, +) + +from qgis.core import QgsExpression + +from processing.algs.qgis.ui.FieldsMappingPanel import ( + ExpressionDelegate, + FieldsMappingModel, + FieldsMappingPanel, + FieldsMappingWidgetWrapper, + FieldTypeDelegate, +) + + +AGGREGATES = ['first_value'] +for function in QgsExpression.Functions(): + if function.name()[0] == '_': + continue + if function.isDeprecated(): + continue + # if ( func->isContextual() ): + if "Aggregates" in function.groups(): + if function.name() in ('aggregate', + 'relation_aggregate'): + continue + AGGREGATES.append(function.name()) +AGGREGATES = sorted(AGGREGATES) + + +class AggregatesModel(FieldsMappingModel): + + def configure(self): + self.columns = [{ + 'name': 'input', + 'type': QgsExpression, + 'header': self.tr("Input expression"), + 'persistentEditor': True + }, { + 'name': 'aggregate', + 'type': QVariant.String, + 'header': self.tr("Aggregate function"), + 'persistentEditor': True + }, { + 'name': 'delimiter', + 'type': QVariant.String, + 'header': self.tr("Delimiter") + }, { + 'name': 'name', + 'type': QVariant.String, + 'header': self.tr("Output field name") + }, { + 'name': 'type', + 'type': QVariant.Type, + 'header': self.tr("Type"), + 'persistentEditor': True + }, { + 'name': 'length', + 'type': QVariant.Int, + 'header': self.tr("Length") + }, { + 'name': 'precision', + 'type': QVariant.Int, + 'header': self.tr("Precision") + }] + + def newField(self, field=None): + if field is None: + return { + 'input': '', + 'aggregate': '', + 'delimiter': '', + 'name': '', + 'type': QVariant.Invalid, + 'length': 0, + 'precision': 0, + } + + default_aggregate = '' + if field.type() in (QVariant.Int, + QVariant.Double, + QVariant.LongLong): + default_aggregate = 'sum' + if field.type() == QVariant.DateTime: + default_aggregate = '' + if field.type() == QVariant.String: + default_aggregate = 'concatenate' + + return { + 'input': QgsExpression.quotedColumnRef(field.name()), + 'aggregate': default_aggregate, + 'delimiter': ',', + 'name': field.name(), + 'type': field.type(), + 'length': field.length(), + 'precision': field.precision(), + } + + +class AggregateDelegate(QStyledItemDelegate): + + def __init__(self, parent=None): + super(AggregateDelegate, self).__init__(parent) + + def createEditor(self, parent, option, index): + editor = QComboBox(parent) + for function in AGGREGATES: + editor.addItem(function, function) + return editor + + def setEditorData(self, editor, index): + if not editor: + return + value = index.model().data(index, Qt.EditRole) + editor.setCurrentIndex(editor.findData(value)) + + def setModelData(self, editor, model, index): + if not editor: + return + value = editor.currentData() + if value is None: + value = QVariant.Invalid + model.setData(index, value) + + +class AggregatesPanel(FieldsMappingPanel): + + def configure(self): + self.model = AggregatesModel() + self.fieldsView.setModel(self.model) + self.model.rowsInserted.connect(self.on_model_rowsInserted) + + self.setDelegate('input', ExpressionDelegate(self)) + self.setDelegate('aggregate', AggregateDelegate(self)) + self.setDelegate('type', FieldTypeDelegate(self)) + + +class AggregatesWidgetWrapper(FieldsMappingWidgetWrapper): + + def createWidget(self, parentLayerParameterName='INPUT'): + self._parentLayerParameter = parentLayerParameterName + return AggregatesPanel() diff --git a/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py b/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py index 1956b6c282e..0c3b1417c7e 100644 --- a/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py +++ b/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py @@ -27,11 +27,9 @@ __copyright__ = '(C) 2014, Arnaud Morvan' __revision__ = '$Format:%H$' import os - from collections import OrderedDict from qgis.PyQt import uic - from qgis.PyQt.QtCore import ( QItemSelectionModel, QAbstractTableModel, @@ -40,7 +38,6 @@ from qgis.PyQt.QtCore import ( Qt, pyqtSlot, ) -from qgis.PyQt.QtGui import QBrush from qgis.PyQt.QtWidgets import ( QComboBox, QHeaderView, @@ -65,6 +62,7 @@ from qgis.gui import QgsFieldExpressionWidget from processing.gui.wrappers import WidgetWrapper, DIALOG_STANDARD, DIALOG_MODELER from processing.tools import dataobjects + pluginPath = os.path.dirname(__file__) WIDGET, BASE = uic.loadUiType( os.path.join(pluginPath, 'fieldsmappingpanelbase.ui')) @@ -83,7 +81,6 @@ class FieldsMappingModel(QAbstractTableModel): def __init__(self, parent=None): super(FieldsMappingModel, self).__init__(parent) self._mapping = [] - self._errors = [] self._layer = None self.configure() diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_all.gfs b/python/plugins/processing/tests/testdata/expected/aggregate_all.gfs new file mode 100644 index 00000000000..4d68b54e498 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_all.gfs @@ -0,0 +1,38 @@ + + + aggregate_all + aggregate_all + + 6 + EPSG:4326 + + 1 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + fids + fids + String + 79 + + + name + name + String + 27 + + + intval + intval + Integer + + + floatval + floatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_all.gml b/python/plugins/processing/tests/testdata/expected/aggregate_all.gml new file mode 100644 index 00000000000..37a6c1187fc --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_all.gml @@ -0,0 +1,22 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + 8.16295585412668,2.73877159309021 8.16295585412668,3.73877159309021 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 8.16295585412668,2.738771593090216.24145873320538,-0.054510556621882 7.24145873320538,-1.05451055662188 6.0,-1.05451055662188 6,-3 2,-1 -1,-1 -1,3 3,3 3,2 6,1 6.0,-0.295969289827257 6.24145873320538,-0.0545105566218824.17255278310941,4.82264875239923 4.17255278310941,5.82264875239923 5.17255278310941,5.82264875239923 5.17255278310941,4.82264875239923 4.17255278310941,4.822648752399232.44337811900192,4.42360844529751 2.44337811900192,5.0 2,5 2,6 2.62072936660269,6.0 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 3.62072936660269,5.08867562380038 3.44337811900192,5.08867562380038 3.44337811900192,4.42360844529751 2.44337811900192,4.42360844529751 + polys.0,polys.1,polys.2,polys.3,polys.4,polys.5,polys.6,polys.7,polys.9,polys.8 + aa,dd,bb,,aa,bb,bb,cc,dd,bb + 127 + -11138.1515193333 + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_field.gfs b/python/plugins/processing/tests/testdata/expected/aggregate_field.gfs new file mode 100644 index 00000000000..51ce108576c --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_field.gfs @@ -0,0 +1,38 @@ + + + aggregate_field + aggregate_field + + 6 + EPSG:4326 + + 5 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + fids + fids + String + 31 + + + name + name + String + 2 + + + intval + intval + Integer + + + floatval + floatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_field.gml b/python/plugins/processing/tests/testdata/expected/aggregate_field.gml new file mode 100644 index 00000000000..9a37f0823f2 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_field.gml @@ -0,0 +1,56 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + 3,2 6,1 6,-3 2,-1 -1,-1 -1,3 3,3 3,2 + polys.0,polys.4 + aa + 2 + 23.726728 + + + + + 6.24145873320538,-0.054510556621882 7.24145873320538,-1.05451055662188 5.24145873320538,-1.05451055662188 6.24145873320538,-0.054510556621882 + polys.1,polys.9 + dd + 0 + 0 + + + + + 4.17255278310941,4.82264875239923 4.17255278310941,5.82264875239923 5.17255278310941,5.82264875239923 5.17255278310941,4.82264875239923 4.17255278310941,4.822648752399232.44337811900192,4.42360844529751 2.44337811900192,5.0 2,5 2,6 2.62072936660269,6.0 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 3.62072936660269,5.08867562380038 3.44337811900192,5.08867562380038 3.44337811900192,4.42360844529751 2.44337811900192,4.42360844529751 + polys.2,polys.5,polys.6,polys.8 + bb + 5 + 0.123 + + + + + polys.3 + 120 + -100291.43213 + + + + + 8.16295585412668,2.73877159309021 8.16295585412668,3.73877159309021 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 8.16295585412668,2.73877159309021 + polys.7 + cc + 0 + 0.123 + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_lines.gfs b/python/plugins/processing/tests/testdata/expected/aggregate_lines.gfs new file mode 100644 index 00000000000..d11631b4421 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_lines.gfs @@ -0,0 +1,22 @@ + + + aggregate_lines + aggregate_lines + + 5 + EPSG:4326 + + 1 + -1.00000 + 11.00000 + -3.00000 + 5.00000 + + + fids + fids + String + 55 + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_lines.gml b/python/plugins/processing/tests/testdata/expected/aggregate_lines.gml new file mode 100644 index 00000000000..a112bba196d --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_lines.gml @@ -0,0 +1,19 @@ + + + + + -1-3 + 115 + + + + + -1,-1 1,-17,-3 10,-36,2 9,2 9,3 11,56,-3 10,13,1 5,12,0 2,2 3,2 3,3 + lines.0,lines.1,lines.2,lines.3,lines.4,lines.5,lines.6 + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_points.gfs b/python/plugins/processing/tests/testdata/expected/aggregate_points.gfs new file mode 100644 index 00000000000..bc00d0ed2d6 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_points.gfs @@ -0,0 +1,33 @@ + + + aggregate_points + aggregate_points + + 4 + EPSG:4326 + + 3 + 0.00000 + 8.00000 + -5.00000 + 3.00000 + + + fids + fids + String + 44 + + + ids + ids + String + 9 + + + id2 + id2 + Integer + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_points.gml b/python/plugins/processing/tests/testdata/expected/aggregate_points.gml new file mode 100644 index 00000000000..b875968433f --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_points.gml @@ -0,0 +1,37 @@ + + + + + 0-5 + 83 + + + + + 1,15,2 + points.0,points.3 + 1,4 + 2 + + + + + 3,34,1 + points.1,points.4 + 2,5 + 1 + + + + + 0,-50,-12,27,-18,-1 + points.2,points.5,points.6,points.7,points.8 + 3,6,7,8,9 + 0 + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gfs b/python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gfs new file mode 100644 index 00000000000..f6bd169e644 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gfs @@ -0,0 +1,38 @@ + + + aggregate_two_fields + aggregate_two_fields + + 6 + EPSG:4326 + + 6 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + fids + fids + String + 23 + + + name + name + String + 2 + + + intval + intval + Integer + + + floatval + floatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gml b/python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gml new file mode 100644 index 00000000000..4159a48786e --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/aggregate_two_fields.gml @@ -0,0 +1,63 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + 3,2 6,1 6,-3 2,-1 -1,-1 -1,3 3,3 3,2 + polys.0,polys.4 + aa + 1 + 23.726728 + + + + + 6.24145873320538,-0.054510556621882 7.24145873320538,-1.05451055662188 5.24145873320538,-1.05451055662188 6.24145873320538,-0.054510556621882 + polys.1,polys.9 + dd + 0 + + + + + 4.17255278310941,4.82264875239923 4.17255278310941,5.82264875239923 5.17255278310941,5.82264875239923 5.17255278310941,4.82264875239923 4.17255278310941,4.822648752399232.44337811900192,4.42360844529751 2.44337811900192,5.0 2,5 2,6 3,6 3.0,5.42360844529751 3.44337811900192,5.42360844529751 3.44337811900192,4.42360844529751 2.44337811900192,4.42360844529751 + polys.2,polys.5,polys.6 + bb + 1 + 0.123 + + + + + polys.3 + 120 + -100291.43213 + + + + + 8.16295585412668,2.73877159309021 8.16295585412668,3.73877159309021 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 8.16295585412668,2.73877159309021 + polys.7 + cc + 0.123 + + + + + 2.62072936660269,5.08867562380038 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 3.62072936660269,5.08867562380038 2.62072936660269,5.08867562380038 + polys.8 + bb + 2 + 0.123 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 2a97476caf1..898ea7f1ef6 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -16,6 +16,204 @@ tests: geometry: precision: 7 + - name: Aggregate all + algorithm: qgis:aggregate + params: + INPUT: + name: dissolve_polys.gml + type: vector + GROUP_BY: 'NULL' + AGGREGATES: + [{ + input: 'fid', + aggregate: 'concatenate', + delimiter: ',', + name: 'fids', + type: 10, + length: 255, + precision: 0 + }, { + input: 'name', + aggregate: 'concatenate', + delimiter: ',', + name: 'name', + type: 10, + length: 255, + precision: 0 + }, { + input: 'intval', + aggregate: 'sum', + delimiter: '', + name: 'intval', + type: 2, + length: 0, + precision: 0 + }, { + aggregate: 'mean', + input: 'floatval', + type: 6, + delimiter: '', + name: 'floatval', + length: 0, + precision: 0 + }] + results: + OUTPUT: + name: expected/aggregate_all.gml + type: vector + + - name: Aggregate using field + algorithm: qgis:aggregate + params: + INPUT: + name: dissolve_polys.gml + type: vector + GROUP_BY: '"name"' + AGGREGATES: + [{ + input: 'fid', + aggregate: 'concatenate', + delimiter: ',', + name: 'fids', + type: 10, + length: 50, + precision: 0 + }, { + input: 'name', + aggregate: 'first_value', + delimiter: ',', + name: 'name', + type: 10, + length: 2, + precision: 0 + }, { + input: 'intval', + aggregate: 'sum', + delimiter: '', + name: 'intval', + type: 2, + length: 0, + precision: 0 + }, { + input: 'floatval', + aggregate: 'mean', + delimiter: '', + name: 'floatval', + type: 6, + length: 0, + precision: 0 + }] + results: + OUTPUT: + name: expected/aggregate_field.gml + type: vector + + - algorithm: qgis:aggregate + name: Aggregate using two fields + params: + INPUT: + name: dissolve_polys.gml + type: vector + GROUP_BY: array("intval", "name") + AGGREGATES: + [{ + input: 'fid', + aggregate: 'concatenate', + delimiter: ',', + name: 'fids', + type: 10, + length: 80, + precision: 0 + }, { + input: 'name', + aggregate: 'first_value', + delimiter: ',', + name: 'name', + type: 10, + length: 2, + precision: 0 + }, { + input: 'intval', + aggregate: 'first_value', + delimiter: '', + name: 'intval', + type: 2, + length: 0, + precision: 0 + }, { + input: 'floatval', + aggregate: 'mean', + delimiter: '', + name: 'floatval', + type: 6, + length: 0, + precision: 0 + }] + results: + OUTPUT: + name: expected/aggregate_two_fields.gml + type: vector + + - name: Aggregate points + algorithm: qgis:aggregate + params: + INPUT: + name: points.gml + type: vector + GROUP_BY: '"id2"' + AGGREGATES: + [{ + input: 'fid', + aggregate: 'concatenate', + delimiter: ',', + name: 'fids', + type: 10, + length: 50, + precision: 0 + }, { + input: 'to_string("id")', + aggregate: 'concatenate', + delimiter: ',', + name: 'ids', + type: 10, + length: 50, + precision: 0 + }, { + input: 'id2', + aggregate: 'first_value', + delimiter: '', + name: 'id2', + type: 6, + length: 0, + precision: 0 + }] + results: + OUTPUT: + name: expected/aggregate_points.gml + type: vector + + - name: Aggregate lines + algorithm: qgis:aggregate + params: + INPUT: + name: lines.gml + type: vector + GROUP_BY: 'NULL' + AGGREGATES: + [{ + input: 'fid', + aggregate: 'concatenate', + delimiter: ',', + name: 'fids', + type: 10, + length: 255, + precision: 0 + }] + results: + OUTPUT: + name: expected/aggregate_lines.gml + type: vector + - name: Delete Holes algorithm: qgis:deleteholes params: From d81a1a3b5f9c9f5144bdaf37ceb3561b730de355 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 20 Jun 2017 14:09:34 +1000 Subject: [PATCH 003/364] Return std::unique_ptrs where possible When a class isn't exposed to Python, we should return a std::unique_ptr whenever a returned pointer value is owned by the caller. --- src/core/auth/qgsauthmanager.cpp | 2 +- src/core/auth/qgsauthmethodregistry.cpp | 11 +-- src/core/auth/qgsauthmethodregistry.h | 5 +- src/core/geometry/qgsgeometry.cpp | 42 ++++---- src/core/geometry/qgsgeometrycollection.cpp | 11 +-- src/core/geometry/qgsgeometryeditutils.cpp | 4 +- src/core/geometry/qgsgeometryeditutils.h | 3 +- src/core/geometry/qgsgeometryfactory.cpp | 101 ++++++++++---------- src/core/geometry/qgsgeometryfactory.h | 22 ++--- src/core/qgsvectordataprovider.cpp | 18 ++-- tests/src/core/testqgsdistancearea.cpp | 10 +- tests/src/core/testqgsgeometry.cpp | 6 +- 12 files changed, 113 insertions(+), 122 deletions(-) diff --git a/src/core/auth/qgsauthmanager.cpp b/src/core/auth/qgsauthmanager.cpp index 15abd87ebf2..e918b5dd083 100644 --- a/src/core/auth/qgsauthmanager.cpp +++ b/src/core/auth/qgsauthmanager.cpp @@ -782,7 +782,7 @@ bool QgsAuthManager::registerCoreAuthMethods() mAuthMethods.clear(); Q_FOREACH ( const QString &authMethodKey, QgsAuthMethodRegistry::instance()->authMethodList() ) { - mAuthMethods.insert( authMethodKey, QgsAuthMethodRegistry::instance()->authMethod( authMethodKey ) ); + mAuthMethods.insert( authMethodKey, QgsAuthMethodRegistry::instance()->authMethod( authMethodKey ).release() ); } return !mAuthMethods.isEmpty(); diff --git a/src/core/auth/qgsauthmethodregistry.cpp b/src/core/auth/qgsauthmethodregistry.cpp index c97010083d5..d62f091467c 100644 --- a/src/core/auth/qgsauthmethodregistry.cpp +++ b/src/core/auth/qgsauthmethodregistry.cpp @@ -260,7 +260,7 @@ void QgsAuthMethodRegistry::setLibraryDirectory( const QDir &path ) // typedef for the QgsDataProvider class factory typedef QgsAuthMethod *classFactoryFunction_t(); -QgsAuthMethod *QgsAuthMethodRegistry::authMethod( const QString &authMethodKey ) +std::unique_ptr QgsAuthMethodRegistry::authMethod( const QString &authMethodKey ) { // load the plugin QString lib = library( authMethodKey ); @@ -299,7 +299,7 @@ QgsAuthMethod *QgsAuthMethodRegistry::authMethod( const QString &authMethodKey ) return nullptr; } - QgsAuthMethod *authMethod = classFactory(); + std::unique_ptr< QgsAuthMethod > authMethod( classFactory() ); if ( !authMethod ) { QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the auth method plugin %1" ).arg( lib ) ); @@ -342,9 +342,9 @@ QFunctionPointer QgsAuthMethodRegistry::function( QString const &authMethodKey, } } -QLibrary *QgsAuthMethodRegistry::authMethodLibrary( const QString &authMethodKey ) const +std::unique_ptr QgsAuthMethodRegistry::authMethodLibrary( const QString &authMethodKey ) const { - QLibrary *myLib = new QLibrary( library( authMethodKey ) ); + std::unique_ptr< QLibrary > myLib( new QLibrary( library( authMethodKey ) ) ); QgsDebugMsg( "Library name is " + myLib->fileName() ); @@ -352,9 +352,6 @@ QLibrary *QgsAuthMethodRegistry::authMethodLibrary( const QString &authMethodKey return myLib; QgsDebugMsg( "Cannot load library: " + myLib->errorString() ); - - delete myLib; - return nullptr; } diff --git a/src/core/auth/qgsauthmethodregistry.h b/src/core/auth/qgsauthmethodregistry.h index c8905015c2f..62ce354095b 100644 --- a/src/core/auth/qgsauthmethodregistry.h +++ b/src/core/auth/qgsauthmethodregistry.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "qgis_core.h" @@ -67,7 +68,7 @@ class CORE_EXPORT QgsAuthMethodRegistry \param authMethodKey identificator of the auth method \returns instance of auth method or nullptr on error */ - QgsAuthMethod *authMethod( const QString &authMethodKey ); + std::unique_ptr< QgsAuthMethod > authMethod( const QString &authMethodKey ); /** Return the auth method capabilities \param authMethodKey identificator of the auth method @@ -89,7 +90,7 @@ class CORE_EXPORT QgsAuthMethodRegistry const QString &functionName ); //! Return the library object associated with an auth method key - QLibrary *authMethodLibrary( const QString &authMethodKey ) const; + std::unique_ptr< QLibrary > authMethodLibrary( const QString &authMethodKey ) const; //! Return list of available auth methods by their keys QStringList authMethodList() const; diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 0ddbf5d8ba7..d7a040b7377 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -134,70 +134,70 @@ bool QgsGeometry::isNull() const QgsGeometry QgsGeometry::fromWkt( const QString &wkt ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::geomFromWkt( wkt ); + std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkt( wkt ); if ( !geom ) { return QgsGeometry(); } - return QgsGeometry( geom ); + return QgsGeometry( geom.release() ); } QgsGeometry QgsGeometry::fromPoint( const QgsPointXY &point ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::fromPoint( point ); + std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::fromPoint( point ) ); if ( geom ) { - return QgsGeometry( geom ); + return QgsGeometry( geom.release() ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromPolyline( const QgsPolyline &polyline ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::fromPolyline( polyline ); + std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolyline( polyline ); if ( geom ) { - return QgsGeometry( geom ); + return QgsGeometry( geom.release() ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromPolygon( const QgsPolygon &polygon ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::fromPolygon( polygon ); + std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolygon( polygon ); if ( geom ) { - return QgsGeometry( geom ); + return QgsGeometry( geom.release() ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromMultiPoint( const QgsMultiPoint &multipoint ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::fromMultiPoint( multipoint ); + std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromMultiPoint( multipoint ); if ( geom ) { - return QgsGeometry( geom ); + return QgsGeometry( geom.release() ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromMultiPolyline( const QgsMultiPolyline &multiline ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::fromMultiPolyline( multiline ); + std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromMultiPolyline( multiline ); if ( geom ) { - return QgsGeometry( geom ); + return QgsGeometry( geom.release() ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromMultiPolygon( const QgsMultiPolygon &multipoly ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::fromMultiPolygon( multipoly ); + std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromMultiPolygon( multipoly ); if ( geom ) { - return QgsGeometry( geom ); + return QgsGeometry( geom.release() ); } return QgsGeometry(); } @@ -246,7 +246,7 @@ void QgsGeometry::fromWkb( unsigned char *wkb, int length ) delete d->geometry; } QgsConstWkbPtr ptr( wkb, length ); - d->geometry = QgsGeometryFactory::geomFromWkb( ptr ); + d->geometry = QgsGeometryFactory::geomFromWkb( ptr ).release(); delete [] wkb; } @@ -259,7 +259,7 @@ void QgsGeometry::fromWkb( const QByteArray &wkb ) delete d->geometry; } QgsConstWkbPtr ptr( wkb ); - d->geometry = QgsGeometryFactory::geomFromWkb( ptr ); + d->geometry = QgsGeometryFactory::geomFromWkb( ptr ).release(); } GEOSGeometry *QgsGeometry::exportToGeos( double precision ) const @@ -1155,8 +1155,8 @@ bool QgsGeometry::convertToMultiType() return true; } - QgsGeometryCollection *multiGeom = qgsgeometry_cast - ( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) ); + std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ); + QgsGeometryCollection *multiGeom = qgsgeometry_cast( geom.get() ); if ( !multiGeom ) { return false; @@ -1164,7 +1164,7 @@ bool QgsGeometry::convertToMultiType() detach( true ); multiGeom->addGeometry( d->geometry ); - d->geometry = multiGeom; + d->geometry = geom.release(); return true; } @@ -2026,11 +2026,11 @@ int QgsGeometry::avoidIntersections( const QList &avoidInterse return 1; } - QgsAbstractGeometry *diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, ignoreFeatures ); + std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, ignoreFeatures ); if ( diffGeom ) { detach( false ); - d->geometry = diffGeom; + d->geometry = diffGeom.release(); } return 0; } diff --git a/src/core/geometry/qgsgeometrycollection.cpp b/src/core/geometry/qgsgeometrycollection.cpp index 4f7c6fd6328..254e59b4309 100644 --- a/src/core/geometry/qgsgeometrycollection.cpp +++ b/src/core/geometry/qgsgeometrycollection.cpp @@ -219,10 +219,10 @@ bool QgsGeometryCollection::fromWkb( QgsConstWkbPtr &wkbPtr ) mGeometries.clear(); for ( int i = 0; i < nGeometries; ++i ) { - QgsAbstractGeometry *geom = QgsGeometryFactory::geomFromWkb( wkbPtr ); // also updates wkbPtr + std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr if ( geom ) { - if ( !addGeometry( geom ) ) + if ( !addGeometry( geom.release() ) ) { qDeleteAll( mGeometries ); mGeometries = geometryListBackup; @@ -597,11 +597,10 @@ bool QgsGeometryCollection::hasCurvedSegments() const QgsAbstractGeometry *QgsGeometryCollection::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const { - QgsAbstractGeometry *geom = QgsGeometryFactory::geomFromWkbType( mWkbType ); - QgsGeometryCollection *geomCollection = qgsgeometry_cast( geom ); + std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( mWkbType ) ); + QgsGeometryCollection *geomCollection = qgsgeometry_cast( geom.get() ); if ( !geomCollection ) { - delete geom; return clone(); } @@ -610,7 +609,7 @@ QgsAbstractGeometry *QgsGeometryCollection::segmentize( double tolerance, Segmen { geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) ); } - return geomCollection; + return geom.release(); } double QgsGeometryCollection::vertexAngle( QgsVertexId vertex ) const diff --git a/src/core/geometry/qgsgeometryeditutils.cpp b/src/core/geometry/qgsgeometryeditutils.cpp index 044a75f24c5..e7aedc1e1b7 100644 --- a/src/core/geometry/qgsgeometryeditutils.cpp +++ b/src/core/geometry/qgsgeometryeditutils.cpp @@ -221,7 +221,7 @@ bool QgsGeometryEditUtils::deletePart( QgsAbstractGeometry *geom, int partNum ) return c->removeGeometry( partNum ); } -QgsAbstractGeometry *QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom, +std::unique_ptr QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom, const QList &avoidIntersectionsLayers, QHash > ignoreFeatures ) { @@ -281,7 +281,7 @@ QgsAbstractGeometry *QgsGeometryEditUtils::avoidIntersections( const QgsAbstract return nullptr; } - QgsAbstractGeometry *diffGeom = geomEngine->difference( combinedGeometries ); + std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries ) ); delete combinedGeometries; return diffGeom; diff --git a/src/core/geometry/qgsgeometryeditutils.h b/src/core/geometry/qgsgeometryeditutils.h index 359f6c6e153..85d08684516 100644 --- a/src/core/geometry/qgsgeometryeditutils.h +++ b/src/core/geometry/qgsgeometryeditutils.h @@ -26,6 +26,7 @@ class QgsVectorLayer; #include "qgsfeature.h" #include "qgsgeometry.h" #include +#include /** \ingroup core * \class QgsGeometryEditUtils @@ -69,7 +70,7 @@ class QgsGeometryEditUtils * \param avoidIntersectionsLayers list of layers to check for intersections * \param ignoreFeatures map of layer to feature id of features to ignore */ - static QgsAbstractGeometry *avoidIntersections( const QgsAbstractGeometry &geom, + static std::unique_ptr< QgsAbstractGeometry > avoidIntersections( const QgsAbstractGeometry &geom, const QList &avoidIntersectionsLayers, QHash > ignoreFeatures = ( QHash >() ) ); }; diff --git a/src/core/geometry/qgsgeometryfactory.cpp b/src/core/geometry/qgsgeometryfactory.cpp index 257f1bf2e11..7287777baea 100644 --- a/src/core/geometry/qgsgeometryfactory.cpp +++ b/src/core/geometry/qgsgeometryfactory.cpp @@ -30,7 +30,7 @@ #include "qgswkbtypes.h" #include "qgslogger.h" -QgsAbstractGeometry *QgsGeometryFactory::geomFromWkb( QgsConstWkbPtr &wkbPtr ) +std::unique_ptr QgsGeometryFactory::geomFromWkb( QgsConstWkbPtr &wkbPtr ) { if ( !wkbPtr ) return nullptr; @@ -49,9 +49,7 @@ QgsAbstractGeometry *QgsGeometryFactory::geomFromWkb( QgsConstWkbPtr &wkbPtr ) } wkbPtr -= 1 + sizeof( int ); - QgsAbstractGeometry *geom = nullptr; - - geom = geomFromWkbType( type ); + std::unique_ptr< QgsAbstractGeometry > geom = geomFromWkbType( type ); if ( geom ) { @@ -63,86 +61,83 @@ QgsAbstractGeometry *QgsGeometryFactory::geomFromWkb( QgsConstWkbPtr &wkbPtr ) { Q_UNUSED( e ); QgsDebugMsg( "WKB exception: " + e.what() ); - delete geom; - geom = nullptr; } } return geom; } -QgsAbstractGeometry *QgsGeometryFactory::geomFromWkt( const QString &text ) +std::unique_ptr QgsGeometryFactory::geomFromWkt( const QString &text ) { QString trimmed = text.trimmed(); - QgsAbstractGeometry *geom = nullptr; + std::unique_ptr< QgsAbstractGeometry> geom; if ( trimmed.startsWith( QLatin1String( "Point" ), Qt::CaseInsensitive ) ) { - geom = new QgsPoint(); + geom.reset( new QgsPoint() ); } else if ( trimmed.startsWith( QLatin1String( "LineString" ), Qt::CaseInsensitive ) ) { - geom = new QgsLineString(); + geom.reset( new QgsLineString() ); } else if ( trimmed.startsWith( QLatin1String( "CircularString" ), Qt::CaseInsensitive ) ) { - geom = new QgsCircularString(); + geom.reset( new QgsCircularString() ); } else if ( trimmed.startsWith( QLatin1String( "CompoundCurve" ), Qt::CaseInsensitive ) ) { - geom = new QgsCompoundCurve(); + geom.reset( new QgsCompoundCurve() ); } else if ( trimmed.startsWith( QLatin1String( "Polygon" ), Qt::CaseInsensitive ) ) { - geom = new QgsPolygonV2(); + geom.reset( new QgsPolygonV2() ); } else if ( trimmed.startsWith( QLatin1String( "CurvePolygon" ), Qt::CaseInsensitive ) ) { - geom = new QgsCurvePolygon(); + geom.reset( new QgsCurvePolygon() ); } else if ( trimmed.startsWith( QLatin1String( "MultiPoint" ), Qt::CaseInsensitive ) ) { - geom = new QgsMultiPointV2(); + geom.reset( new QgsMultiPointV2() ); } else if ( trimmed.startsWith( QLatin1String( "MultiCurve" ), Qt::CaseInsensitive ) ) { - geom = new QgsMultiCurve(); + geom.reset( new QgsMultiCurve() ); } else if ( trimmed.startsWith( QLatin1String( "MultiLineString" ), Qt::CaseInsensitive ) ) { - geom = new QgsMultiLineString(); + geom.reset( new QgsMultiLineString() ); } else if ( trimmed.startsWith( QLatin1String( "MultiSurface" ), Qt::CaseInsensitive ) ) { - geom = new QgsMultiSurface(); + geom.reset( new QgsMultiSurface() ); } else if ( trimmed.startsWith( QLatin1String( "MultiPolygon" ), Qt::CaseInsensitive ) ) { - geom = new QgsMultiPolygonV2(); + geom.reset( new QgsMultiPolygonV2() ); } else if ( trimmed.startsWith( QLatin1String( "GeometryCollection" ), Qt::CaseInsensitive ) ) { - geom = new QgsGeometryCollection(); + geom.reset( new QgsGeometryCollection() ); } if ( geom ) { if ( !geom->fromWkt( text ) ) { - delete geom; return nullptr; } } return geom; } -QgsAbstractGeometry *QgsGeometryFactory::fromPoint( const QgsPointXY &point ) +std::unique_ptr< QgsAbstractGeometry > QgsGeometryFactory::fromPoint( const QgsPointXY &point ) { - return new QgsPoint( point.x(), point.y() ); + return std::unique_ptr< QgsAbstractGeometry >( new QgsPoint( point.x(), point.y() ) ); } -QgsAbstractGeometry *QgsGeometryFactory::fromMultiPoint( const QgsMultiPoint &multipoint ) +std::unique_ptr QgsGeometryFactory::fromMultiPoint( const QgsMultiPoint &multipoint ) { - QgsMultiPointV2 *mp = new QgsMultiPointV2(); + std::unique_ptr< QgsMultiPointV2 > mp( new QgsMultiPointV2() ); QgsMultiPoint::const_iterator ptIt = multipoint.constBegin(); for ( ; ptIt != multipoint.constEnd(); ++ptIt ) { @@ -152,55 +147,55 @@ QgsAbstractGeometry *QgsGeometryFactory::fromMultiPoint( const QgsMultiPoint &mu return mp; } -QgsAbstractGeometry *QgsGeometryFactory::fromPolyline( const QgsPolyline &polyline ) +std::unique_ptr QgsGeometryFactory::fromPolyline( const QgsPolyline &polyline ) { return linestringFromPolyline( polyline ); } -QgsAbstractGeometry *QgsGeometryFactory::fromMultiPolyline( const QgsMultiPolyline &multiline ) +std::unique_ptr QgsGeometryFactory::fromMultiPolyline( const QgsMultiPolyline &multiline ) { - QgsMultiLineString *mLine = new QgsMultiLineString(); + std::unique_ptr< QgsMultiLineString > mLine( new QgsMultiLineString() ); for ( int i = 0; i < multiline.size(); ++i ) { - mLine->addGeometry( fromPolyline( multiline.at( i ) ) ); + mLine->addGeometry( fromPolyline( multiline.at( i ) ).release() ); } return mLine; } -QgsAbstractGeometry *QgsGeometryFactory::fromPolygon( const QgsPolygon &polygon ) +std::unique_ptr QgsGeometryFactory::fromPolygon( const QgsPolygon &polygon ) { - QgsPolygonV2 *poly = new QgsPolygonV2(); + std::unique_ptr< QgsPolygonV2 > poly( new QgsPolygonV2() ); QList holes; for ( int i = 0; i < polygon.size(); ++i ) { - QgsLineString *l = linestringFromPolyline( polygon.at( i ) ); + std::unique_ptr< QgsLineString > l = linestringFromPolyline( polygon.at( i ) ); l->close(); if ( i == 0 ) { - poly->setExteriorRing( l ); + poly->setExteriorRing( l.release() ); } else { - holes.push_back( l ); + holes.push_back( l.release() ); } } poly->setInteriorRings( holes ); return poly; } -QgsAbstractGeometry *QgsGeometryFactory::fromMultiPolygon( const QgsMultiPolygon &multipoly ) +std::unique_ptr< QgsAbstractGeometry > QgsGeometryFactory::fromMultiPolygon( const QgsMultiPolygon &multipoly ) { - QgsMultiPolygonV2 *mp = new QgsMultiPolygonV2(); + std::unique_ptr< QgsMultiPolygonV2 > mp( new QgsMultiPolygonV2() ); for ( int i = 0; i < multipoly.size(); ++i ) { - mp->addGeometry( fromPolygon( multipoly.at( i ) ) ); + mp->addGeometry( fromPolygon( multipoly.at( i ) ).release() ); } return mp; } -QgsAbstractGeometry *QgsGeometryFactory::fromRect( const QgsRectangle &rect ) +std::unique_ptr QgsGeometryFactory::fromRect( const QgsRectangle &rect ) { QgsPolyline ring; ring.append( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) ); @@ -215,7 +210,7 @@ QgsAbstractGeometry *QgsGeometryFactory::fromRect( const QgsRectangle &rect ) return fromPolygon( polygon ); } -QgsLineString *QgsGeometryFactory::linestringFromPolyline( const QgsPolyline &polyline ) +std::unique_ptr QgsGeometryFactory::linestringFromPolyline( const QgsPolyline &polyline ) { QVector< double > x; x.reserve( polyline.size() ); @@ -227,39 +222,39 @@ QgsLineString *QgsGeometryFactory::linestringFromPolyline( const QgsPolyline &po x << it->x(); y << it->y(); } - QgsLineString *line = new QgsLineString( x, y ); + std::unique_ptr< QgsLineString > line( new QgsLineString( x, y ) ); return line; } -QgsAbstractGeometry *QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::Type t ) +std::unique_ptr QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::Type t ) { QgsWkbTypes::Type type = QgsWkbTypes::flatType( t ); switch ( type ) { case QgsWkbTypes::Point: - return new QgsPoint(); + return std::unique_ptr( new QgsPoint() ); case QgsWkbTypes::LineString: - return new QgsLineString(); + return std::unique_ptr( new QgsLineString() ); case QgsWkbTypes::CircularString: - return new QgsCircularString(); + return std::unique_ptr( new QgsCircularString() ); case QgsWkbTypes::CompoundCurve: - return new QgsCompoundCurve(); + return std::unique_ptr( new QgsCompoundCurve() ); case QgsWkbTypes::Polygon: - return new QgsPolygonV2(); + return std::unique_ptr( new QgsPolygonV2() ); case QgsWkbTypes::CurvePolygon: - return new QgsCurvePolygon(); + return std::unique_ptr( new QgsCurvePolygon() ); case QgsWkbTypes::MultiLineString: - return new QgsMultiLineString(); + return std::unique_ptr( new QgsMultiLineString() ); case QgsWkbTypes::MultiPolygon: - return new QgsMultiPolygonV2(); + return std::unique_ptr( new QgsMultiPolygonV2() ); case QgsWkbTypes::MultiPoint: - return new QgsMultiPointV2(); + return std::unique_ptr( new QgsMultiPointV2() ); case QgsWkbTypes::MultiCurve: - return new QgsMultiCurve(); + return std::unique_ptr( new QgsMultiCurve() ); case QgsWkbTypes::MultiSurface: - return new QgsMultiSurface(); + return std::unique_ptr( new QgsMultiSurface() ); case QgsWkbTypes::GeometryCollection: - return new QgsGeometryCollection(); + return std::unique_ptr( new QgsGeometryCollection() ); default: return nullptr; } diff --git a/src/core/geometry/qgsgeometryfactory.h b/src/core/geometry/qgsgeometryfactory.h index 65fe1b984ef..07b36063b7c 100644 --- a/src/core/geometry/qgsgeometryfactory.h +++ b/src/core/geometry/qgsgeometryfactory.h @@ -51,28 +51,28 @@ class CORE_EXPORT QgsGeometryFactory /** Construct geometry from a WKB string. * Updates position of the passed WKB pointer. */ - static QgsAbstractGeometry *geomFromWkb( QgsConstWkbPtr &wkb ); + static std::unique_ptr< QgsAbstractGeometry > geomFromWkb( QgsConstWkbPtr &wkb ); /** Construct geometry from a WKT string. */ - static QgsAbstractGeometry *geomFromWkt( const QString &text ); + static std::unique_ptr< QgsAbstractGeometry > geomFromWkt( const QString &text ); //! Construct geometry from a point - static QgsAbstractGeometry *fromPoint( const QgsPointXY &point ); + static std::unique_ptr< QgsAbstractGeometry > fromPoint( const QgsPointXY &point ); //! Construct geometry from a multipoint - static QgsAbstractGeometry *fromMultiPoint( const QgsMultiPoint &multipoint ); + static std::unique_ptr< QgsAbstractGeometry > fromMultiPoint( const QgsMultiPoint &multipoint ); //! Construct geometry from a polyline - static QgsAbstractGeometry *fromPolyline( const QgsPolyline &polyline ); + static std::unique_ptr< QgsAbstractGeometry > fromPolyline( const QgsPolyline &polyline ); //! Construct geometry from a multipolyline - static QgsAbstractGeometry *fromMultiPolyline( const QgsMultiPolyline &multiline ); + static std::unique_ptr< QgsAbstractGeometry > fromMultiPolyline( const QgsMultiPolyline &multiline ); //! Construct geometry from a polygon - static QgsAbstractGeometry *fromPolygon( const QgsPolygon &polygon ); + static std::unique_ptr< QgsAbstractGeometry > fromPolygon( const QgsPolygon &polygon ); //! Construct geometry from a multipolygon - static QgsAbstractGeometry *fromMultiPolygon( const QgsMultiPolygon &multipoly ); + static std::unique_ptr< QgsAbstractGeometry > fromMultiPolygon( const QgsMultiPolygon &multipoly ); //! Construct geometry from a rectangle - static QgsAbstractGeometry *fromRect( const QgsRectangle &rect ); + static std::unique_ptr< QgsAbstractGeometry > fromRect( const QgsRectangle &rect ); //! Return empty geometry from wkb type - static QgsAbstractGeometry *geomFromWkbType( QgsWkbTypes::Type t ); + static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType( QgsWkbTypes::Type t ); /** * Returns a new geometry collection matching a specified WKB \a type. For instance, if @@ -81,7 +81,7 @@ class CORE_EXPORT QgsGeometryFactory static std::unique_ptr< QgsGeometryCollection > createCollectionOfType( QgsWkbTypes::Type type ); private: - static QgsLineString *linestringFromPolyline( const QgsPolyline &polyline ); + static std::unique_ptr< QgsLineString > linestringFromPolyline( const QgsPolyline &polyline ); }; #endif // QGSGEOMETRYFACTORY_H diff --git a/src/core/qgsvectordataprovider.cpp b/src/core/qgsvectordataprovider.cpp index 1db670aa324..1f11973495f 100644 --- a/src/core/qgsvectordataprovider.cpp +++ b/src/core/qgsvectordataprovider.cpp @@ -722,7 +722,7 @@ QgsGeometry QgsVectorDataProvider::convertToProviderType( const QgsGeometry &geo return QgsGeometry(); } - QgsAbstractGeometry *outputGeom = nullptr; + std::unique_ptr< QgsAbstractGeometry > outputGeom; //convert compoundcurve to circularstring (possible if compoundcurve consists of one circular string) if ( QgsWkbTypes::flatType( providerGeomType ) == QgsWkbTypes::CircularString ) @@ -735,7 +735,7 @@ QgsGeometry QgsVectorDataProvider::convertToProviderType( const QgsGeometry &geo const QgsCircularString *circularString = qgsgeometry_cast( compoundCurve->curveAt( 0 ) ); if ( circularString ) { - outputGeom = circularString->clone(); + outputGeom.reset( circularString->clone() ); } } } @@ -745,7 +745,7 @@ QgsGeometry QgsVectorDataProvider::convertToProviderType( const QgsGeometry &geo if ( QgsWkbTypes::isMultiType( providerGeomType ) && !QgsWkbTypes::isMultiType( geometry->wkbType() ) ) { outputGeom = QgsGeometryFactory::geomFromWkbType( providerGeomType ); - QgsGeometryCollection *geomCollection = qgsgeometry_cast( outputGeom ); + QgsGeometryCollection *geomCollection = qgsgeometry_cast( outputGeom.get() ); if ( geomCollection ) { geomCollection->addGeometry( geometry->clone() ); @@ -758,8 +758,7 @@ QgsGeometry QgsVectorDataProvider::convertToProviderType( const QgsGeometry &geo QgsAbstractGeometry *curveGeom = outputGeom ? outputGeom->toCurveType() : geometry->toCurveType(); if ( curveGeom ) { - delete outputGeom; - outputGeom = curveGeom; + outputGeom.reset( curveGeom ); } } @@ -770,8 +769,7 @@ QgsGeometry QgsVectorDataProvider::convertToProviderType( const QgsGeometry &geo segmentizedGeom = outputGeom ? outputGeom->segmentize() : geometry->segmentize(); if ( segmentizedGeom ) { - delete outputGeom; - outputGeom = segmentizedGeom; + outputGeom.reset( segmentizedGeom ); } } @@ -780,7 +778,7 @@ QgsGeometry QgsVectorDataProvider::convertToProviderType( const QgsGeometry &geo { if ( !outputGeom ) { - outputGeom = geometry->clone(); + outputGeom.reset( geometry->clone() ); } outputGeom->addZValue(); } @@ -788,14 +786,14 @@ QgsGeometry QgsVectorDataProvider::convertToProviderType( const QgsGeometry &geo { if ( !outputGeom ) { - outputGeom = geometry->clone(); + outputGeom.reset( geometry->clone() ); } outputGeom->addMValue(); } if ( outputGeom ) { - return QgsGeometry( outputGeom ); + return QgsGeometry( outputGeom.release() ); } return QgsGeometry(); } diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp index c9ccd5075f8..b4f97fb1c4b 100644 --- a/tests/src/core/testqgsdistancearea.cpp +++ b/tests/src/core/testqgsdistancearea.cpp @@ -218,7 +218,7 @@ void TestQgsDistanceArea::regression13601() QgsDistanceArea calc; calc.setEllipsoid( QStringLiteral( "NONE" ) ); calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 1108L ) ); - QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((252000 1389000, 265000 1389000, 265000 1385000, 252000 1385000, 252000 1389000))" ) ) ); + QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((252000 1389000, 265000 1389000, 265000 1385000, 252000 1385000, 252000 1389000))" ) ).release() ); QGSCOMPARENEAR( calc.measureArea( geom ), 52000000, 0.0001 ); } @@ -230,21 +230,21 @@ void TestQgsDistanceArea::collections() myDa.setEllipsoid( QStringLiteral( "WGS84" ) ); //collection of lines, should be sum of line length - QgsGeometry lines( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( LineString(0 36.53, 5.76 -48.16), LineString(0 25.54, 24.20 36.70) )" ) ) ); + QgsGeometry lines( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( LineString(0 36.53, 5.76 -48.16), LineString(0 25.54, 24.20 36.70) )" ) ).release() ); double result = myDa.measureLength( lines ); QGSCOMPARENEAR( result, 12006159, 1 ); result = myDa.measureArea( lines ); QVERIFY( qgsDoubleNear( result, 0 ) ); //collection of polygons - QgsGeometry polys( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) ) ); + QgsGeometry polys( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) ).release() ); result = myDa.measureArea( polys ); QGSCOMPARENEAR( result, 670434859475LL, 1 ); result = myDa.measureLength( polys ); QVERIFY( qgsDoubleNear( result, 0 ) ); //mixed collection - QgsGeometry mixed( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( LineString(0 36.53, 5.76 -48.16), LineString(0 25.54, 24.20 36.70), Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) ) ); + QgsGeometry mixed( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( LineString(0 36.53, 5.76 -48.16), LineString(0 25.54, 24.20 36.70), Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) ).release() ); //measure area specifically result = myDa.measureArea( mixed ); QGSCOMPARENEAR( result, 670434859475LL, 1 ); @@ -375,7 +375,7 @@ void TestQgsDistanceArea::regression14675() QgsDistanceArea calc; calc.setEllipsoid( QStringLiteral( "GRS80" ) ); calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 145L ) ); - QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((917593.5791854317067191 6833700.00807378999888897, 917596.43389983859378844 6833700.67099479306489229, 917599.53056440979707986 6833700.78673478215932846, 917593.5791854317067191 6833700.00807378999888897))" ) ) ); + QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((917593.5791854317067191 6833700.00807378999888897, 917596.43389983859378844 6833700.67099479306489229, 917599.53056440979707986 6833700.78673478215932846, 917593.5791854317067191 6833700.00807378999888897))" ) ).release() ); //lots of tolerance here - the formulas get quite unstable with small areas due to division by very small floats QGSCOMPARENEAR( calc.measureArea( geom ), 0.833010, 0.03 ); } diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 458e67aa0f6..61296682478 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -5293,7 +5293,7 @@ void TestQgsGeometry::wkbInOut() void TestQgsGeometry::segmentizeCircularString() { QString wkt( QStringLiteral( "CIRCULARSTRING( 0 0, 0.5 0.5, 2 0 )" ) ); - QgsCircularString *circularString = dynamic_cast( QgsGeometryFactory::geomFromWkt( wkt ) ); + QgsCircularString *circularString = dynamic_cast( QgsGeometryFactory::geomFromWkt( wkt ).release() ); QVERIFY( circularString ); QgsLineString *lineString = circularString->curveToLine(); QVERIFY( lineString ); @@ -5311,11 +5311,11 @@ void TestQgsGeometry::directionNeutralSegmentation() { //Tests, if segmentation of a circularstring is the same in both directions QString CWCircularStringWkt( QStringLiteral( "CIRCULARSTRING( 0 0, 0.5 0.5, 0.83 7.33 )" ) ); - QgsCircularString *CWCircularString = static_cast( QgsGeometryFactory::geomFromWkt( CWCircularStringWkt ) ); + QgsCircularString *CWCircularString = static_cast( QgsGeometryFactory::geomFromWkt( CWCircularStringWkt ).release() ); QgsLineString *CWLineString = CWCircularString->curveToLine(); QString CCWCircularStringWkt( QStringLiteral( "CIRCULARSTRING( 0.83 7.33, 0.5 0.5, 0 0 )" ) ); - QgsCircularString *CCWCircularString = static_cast( QgsGeometryFactory::geomFromWkt( CCWCircularStringWkt ) ); + QgsCircularString *CCWCircularString = static_cast( QgsGeometryFactory::geomFromWkt( CCWCircularStringWkt ).release() ); QgsLineString *CCWLineString = CCWCircularString->curveToLine(); QgsLineString *reversedCCWLineString = CCWLineString->reversed(); From 2693c6fb91f7cdb9cbda47a99c271da9f8be81ce Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 16:30:03 +1000 Subject: [PATCH 004/364] Fix ui build warning --- src/ui/qgsdiagrampropertiesbase.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/qgsdiagrampropertiesbase.ui b/src/ui/qgsdiagrampropertiesbase.ui index 43ad29fe20c..8a36c50b108 100755 --- a/src/ui/qgsdiagrampropertiesbase.ui +++ b/src/ui/qgsdiagrampropertiesbase.ui @@ -1617,7 +1617,7 @@ labelplacementgroup - + 8 From 43536a66e39359d246d967d0bcac36550474e4c3 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 16:34:35 +1000 Subject: [PATCH 005/364] Return subclasses where possible to fix build on older compilers --- src/core/geometry/qgsgeometry.cpp | 8 ++++---- src/core/geometry/qgsgeometryfactory.cpp | 10 +++++----- src/core/geometry/qgsgeometryfactory.h | 14 +++++++++----- tests/src/core/testqgsdistancearea.cpp | 2 +- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index d7a040b7377..26066065742 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -164,7 +164,7 @@ QgsGeometry QgsGeometry::fromPolyline( const QgsPolyline &polyline ) QgsGeometry QgsGeometry::fromPolygon( const QgsPolygon &polygon ) { - std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolygon( polygon ); + std::unique_ptr< QgsPolygonV2 > geom = QgsGeometryFactory::fromPolygon( polygon ); if ( geom ) { return QgsGeometry( geom.release() ); @@ -174,7 +174,7 @@ QgsGeometry QgsGeometry::fromPolygon( const QgsPolygon &polygon ) QgsGeometry QgsGeometry::fromMultiPoint( const QgsMultiPoint &multipoint ) { - std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromMultiPoint( multipoint ); + std::unique_ptr< QgsMultiPointV2 > geom = QgsGeometryFactory::fromMultiPoint( multipoint ); if ( geom ) { return QgsGeometry( geom.release() ); @@ -184,7 +184,7 @@ QgsGeometry QgsGeometry::fromMultiPoint( const QgsMultiPoint &multipoint ) QgsGeometry QgsGeometry::fromMultiPolyline( const QgsMultiPolyline &multiline ) { - std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromMultiPolyline( multiline ); + std::unique_ptr< QgsMultiLineString > geom = QgsGeometryFactory::fromMultiPolyline( multiline ); if ( geom ) { return QgsGeometry( geom.release() ); @@ -194,7 +194,7 @@ QgsGeometry QgsGeometry::fromMultiPolyline( const QgsMultiPolyline &multiline ) QgsGeometry QgsGeometry::fromMultiPolygon( const QgsMultiPolygon &multipoly ) { - std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromMultiPolygon( multipoly ); + std::unique_ptr< QgsMultiPolygonV2 > geom = QgsGeometryFactory::fromMultiPolygon( multipoly ); if ( geom ) { return QgsGeometry( geom.release() ); diff --git a/src/core/geometry/qgsgeometryfactory.cpp b/src/core/geometry/qgsgeometryfactory.cpp index 7287777baea..9361def73cc 100644 --- a/src/core/geometry/qgsgeometryfactory.cpp +++ b/src/core/geometry/qgsgeometryfactory.cpp @@ -135,7 +135,7 @@ std::unique_ptr< QgsAbstractGeometry > QgsGeometryFactory::fromPoint( const QgsP return std::unique_ptr< QgsAbstractGeometry >( new QgsPoint( point.x(), point.y() ) ); } -std::unique_ptr QgsGeometryFactory::fromMultiPoint( const QgsMultiPoint &multipoint ) +std::unique_ptr QgsGeometryFactory::fromMultiPoint( const QgsMultiPoint &multipoint ) { std::unique_ptr< QgsMultiPointV2 > mp( new QgsMultiPointV2() ); QgsMultiPoint::const_iterator ptIt = multipoint.constBegin(); @@ -152,7 +152,7 @@ std::unique_ptr QgsGeometryFactory::fromPolyline( const Qgs return linestringFromPolyline( polyline ); } -std::unique_ptr QgsGeometryFactory::fromMultiPolyline( const QgsMultiPolyline &multiline ) +std::unique_ptr QgsGeometryFactory::fromMultiPolyline( const QgsMultiPolyline &multiline ) { std::unique_ptr< QgsMultiLineString > mLine( new QgsMultiLineString() ); for ( int i = 0; i < multiline.size(); ++i ) @@ -162,7 +162,7 @@ std::unique_ptr QgsGeometryFactory::fromMultiPolyline( cons return mLine; } -std::unique_ptr QgsGeometryFactory::fromPolygon( const QgsPolygon &polygon ) +std::unique_ptr QgsGeometryFactory::fromPolygon( const QgsPolygon &polygon ) { std::unique_ptr< QgsPolygonV2 > poly( new QgsPolygonV2() ); @@ -185,7 +185,7 @@ std::unique_ptr QgsGeometryFactory::fromPolygon( const QgsP return poly; } -std::unique_ptr< QgsAbstractGeometry > QgsGeometryFactory::fromMultiPolygon( const QgsMultiPolygon &multipoly ) +std::unique_ptr< QgsMultiPolygonV2 > QgsGeometryFactory::fromMultiPolygon( const QgsMultiPolygon &multipoly ) { std::unique_ptr< QgsMultiPolygonV2 > mp( new QgsMultiPolygonV2() ); for ( int i = 0; i < multipoly.size(); ++i ) @@ -195,7 +195,7 @@ std::unique_ptr< QgsAbstractGeometry > QgsGeometryFactory::fromMultiPolygon( con return mp; } -std::unique_ptr QgsGeometryFactory::fromRect( const QgsRectangle &rect ) +std::unique_ptr QgsGeometryFactory::fromRect( const QgsRectangle &rect ) { QgsPolyline ring; ring.append( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) ); diff --git a/src/core/geometry/qgsgeometryfactory.h b/src/core/geometry/qgsgeometryfactory.h index 07b36063b7c..7dd931d144f 100644 --- a/src/core/geometry/qgsgeometryfactory.h +++ b/src/core/geometry/qgsgeometryfactory.h @@ -29,6 +29,10 @@ class QgsLineString; class QgsConstWkbPtr; class QgsRectangle; class QgsGeometryCollection; +class QgsMultiPointV2; +class QgsMultiLineString; +class QgsPolygonV2; +class QgsMultiPolygonV2; //compatibility with old classes #include "qgspointxy.h" @@ -60,17 +64,17 @@ class CORE_EXPORT QgsGeometryFactory //! Construct geometry from a point static std::unique_ptr< QgsAbstractGeometry > fromPoint( const QgsPointXY &point ); //! Construct geometry from a multipoint - static std::unique_ptr< QgsAbstractGeometry > fromMultiPoint( const QgsMultiPoint &multipoint ); + static std::unique_ptr fromMultiPoint( const QgsMultiPoint &multipoint ); //! Construct geometry from a polyline static std::unique_ptr< QgsAbstractGeometry > fromPolyline( const QgsPolyline &polyline ); //! Construct geometry from a multipolyline - static std::unique_ptr< QgsAbstractGeometry > fromMultiPolyline( const QgsMultiPolyline &multiline ); + static std::unique_ptr fromMultiPolyline( const QgsMultiPolyline &multiline ); //! Construct geometry from a polygon - static std::unique_ptr< QgsAbstractGeometry > fromPolygon( const QgsPolygon &polygon ); + static std::unique_ptr fromPolygon( const QgsPolygon &polygon ); //! Construct geometry from a multipolygon - static std::unique_ptr< QgsAbstractGeometry > fromMultiPolygon( const QgsMultiPolygon &multipoly ); + static std::unique_ptr fromMultiPolygon( const QgsMultiPolygon &multipoly ); //! Construct geometry from a rectangle - static std::unique_ptr< QgsAbstractGeometry > fromRect( const QgsRectangle &rect ); + static std::unique_ptr fromRect( const QgsRectangle &rect ); //! Return empty geometry from wkb type static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType( QgsWkbTypes::Type t ); diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp index b4f97fb1c4b..9f876248b93 100644 --- a/tests/src/core/testqgsdistancearea.cpp +++ b/tests/src/core/testqgsdistancearea.cpp @@ -385,7 +385,7 @@ void TestQgsDistanceArea::regression16820() QgsDistanceArea calc; calc.setEllipsoid( QStringLiteral( "WGS84" ) ); calc.setSourceCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:32634" ) ) ); - QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((110250.54038314701756462 5084495.57398066483438015, 110243.46975068224128336 5084507.17200060561299324, 110251.23908144699817058 5084506.68309532757848501, 110251.2394439501222223 5084506.68307251576334238, 110250.54048078990308568 5084495.57553235255181789, 110250.54038314701756462 5084495.57398066483438015))" ) ) ); + QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((110250.54038314701756462 5084495.57398066483438015, 110243.46975068224128336 5084507.17200060561299324, 110251.23908144699817058 5084506.68309532757848501, 110251.2394439501222223 5084506.68307251576334238, 110250.54048078990308568 5084495.57553235255181789, 110250.54038314701756462 5084495.57398066483438015))" ) ).release() ); //lots of tolerance here - the formulas get quite unstable with small areas due to division by very small floats QGSCOMPARENEAR( calc.measureArea( geom ), 43.3280029296875, 0.2 ); } From fad59736b0234706d988b969cd3f8d16302378dd Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Thu, 17 Aug 2017 09:38:19 +0200 Subject: [PATCH 006/364] spelling fixes --- src/app/dwg/libdxfrw/intern/dwgutil.cpp | 2 +- src/app/dwg/libdxfrw/intern/rscodec.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/dwg/libdxfrw/intern/dwgutil.cpp b/src/app/dwg/libdxfrw/intern/dwgutil.cpp index ad8ca31c977..c31643ac49d 100644 --- a/src/app/dwg/libdxfrw/intern/dwgutil.cpp +++ b/src/app/dwg/libdxfrw/intern/dwgutil.cpp @@ -189,7 +189,7 @@ void dwgCompressor::decompress18( duint8 *cbuf, duint8 *dbuf, duint32 csize, dui pos = 0; //current position in compressed buffer rpos = 0; //current position in resulting decompressed buffer litCount = litLength18(); - //copy first lileral length + //copy first literal length for ( duint32 i = 0; i < litCount; ++i ) { bufD[rpos++] = bufC[pos++]; diff --git a/src/app/dwg/libdxfrw/intern/rscodec.cpp b/src/app/dwg/libdxfrw/intern/rscodec.cpp index 893045ffe6b..a875ef0d553 100644 --- a/src/app/dwg/libdxfrw/intern/rscodec.cpp +++ b/src/app/dwg/libdxfrw/intern/rscodec.cpp @@ -412,7 +412,7 @@ bool RScodec::encode( unsigned char *data, unsigned char *parity ) evaluating, storing the syndromes in s[i], i=1..2tt (leave s[0] zero) . Then we use the Berlekamp iteration to find the error location polynomial elp[i]. If the degree of the elp is >tt, we cannot correct all the errors - and hence just put out the information symbols ian-overridesped. If the degree of + and hence just put out the information symbols unmodified. If the degree of elp is <=tt, we substitute alpha**i , i=1..n into the elp to get the roots, hence the inverse roots, the error location numbers. If the number of errors located does not equal the degree of the elp, we have more than tt errors From e4dab7c5c75dccd2d217bde0d3fd642679b8600f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 17:53:36 +1000 Subject: [PATCH 007/364] Fix intermittently failing QgsGeometry asGML tests --- src/core/qgstestutils.h | 5 +++++ tests/src/core/testqgsgeometry.cpp | 14 +++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/core/qgstestutils.h b/src/core/qgstestutils.h index b61e91a4c85..c1b0d9fb016 100644 --- a/src/core/qgstestutils.h +++ b/src/core/qgstestutils.h @@ -55,4 +55,9 @@ QGSCOMPARENEAR( rectangle1.yMaximum(), rectangle2.yMaximum(), epsilon ); \ } +//sometimes GML attributes are in a different order - but that's ok +#define QGSCOMPAREGML(result,expected) { \ + QCOMPARE( result.replace( QStringLiteral("ts=\" \" cs=\",\""), QStringLiteral("cs=\",\" ts=\" \"") ), expected ); \ + } + #endif // QGSTESTUTILS_H diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 61296682478..061966b90da 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -617,9 +617,9 @@ void TestQgsGeometry::point() QgsPoint exportPointFloat( 1 / 3.0, 2 / 3.0 ); QDomDocument doc( QStringLiteral( "gml" ) ); QString expectedGML2( QStringLiteral( "1,2" ) ); - QCOMPARE( elemToString( exportPoint.asGML2( doc ) ), expectedGML2 ); + QGSCOMPAREGML( elemToString( exportPoint.asGML2( doc ) ), expectedGML2 ); QString expectedGML2prec3( QStringLiteral( "0.333,0.667" ) ); - QCOMPARE( elemToString( exportPointFloat.asGML2( doc, 3 ) ), expectedGML2prec3 ); + QGSCOMPAREGML( elemToString( exportPointFloat.asGML2( doc, 3 ) ), expectedGML2prec3 ); //asGML3 QString expectedGML3( QStringLiteral( "1 2" ) ); @@ -1739,9 +1739,9 @@ void TestQgsGeometry::lineString() << QgsPoint( 2 + 1 / 3.0, 2 + 2 / 3.0 ) ); QDomDocument doc( QStringLiteral( "gml" ) ); QString expectedGML2( QStringLiteral( "31,32 41,42 51,52" ) ); - QCOMPARE( elemToString( exportLine.asGML2( doc ) ), expectedGML2 ); + QGSCOMPAREGML( elemToString( exportLine.asGML2( doc ) ), expectedGML2 ); QString expectedGML2prec3( QStringLiteral( "0.333,0.667 1.333,1.667 2.333,2.667" ) ); - QCOMPARE( elemToString( exportLineFloat.asGML2( doc, 3 ) ), expectedGML2prec3 ); + QGSCOMPAREGML( elemToString( exportLineFloat.asGML2( doc, 3 ) ), expectedGML2prec3 ); //asGML3 QString expectedGML3( QStringLiteral( "31 32 41 42 51 52" ) ); @@ -3203,7 +3203,7 @@ void TestQgsGeometry::polygon() // as GML2 QString expectedSimpleGML2( QStringLiteral( "0,0 0,10 10,10 10,0 0,0" ) ); - QCOMPARE( elemToString( exportPolygon.asGML2( doc ) ), expectedSimpleGML2 ); + QGSCOMPAREGML( elemToString( exportPolygon.asGML2( doc ) ), expectedSimpleGML2 ); //as GML3 QString expectedSimpleGML3( QStringLiteral( "0 0 0 10 10 10 10 0 0 0" ) ); @@ -3240,10 +3240,10 @@ void TestQgsGeometry::polygon() // as GML2 QString expectedGML2( QStringLiteral( "0,0 0,10 10,10 10,0 0,0" ) ); expectedGML2 += QStringLiteral( "1,1 1,9 9,9 9,1 1,1" ); - QCOMPARE( elemToString( exportPolygon.asGML2( doc ) ), expectedGML2 ); + QGSCOMPAREGML( elemToString( exportPolygon.asGML2( doc ) ), expectedGML2 ); QString expectedGML2prec3( QStringLiteral( "1.111,1.111 1.111,11.111 11.111,11.111 11.111,1.111 1.111,1.111" ) ); expectedGML2prec3 += QStringLiteral( "0.667,0.667 0.667,1.333 1.333,1.333 1.333,0.667 0.667,0.667" ); - QCOMPARE( elemToString( exportPolygonFloat.asGML2( doc, 3 ) ), expectedGML2prec3 ); + QGSCOMPAREGML( elemToString( exportPolygonFloat.asGML2( doc, 3 ) ), expectedGML2prec3 ); //as GML3 QString expectedGML3( QStringLiteral( "0 0 0 10 10 10 10 0 0 0" ) ); From e0d1ddc5061a68fbac6c6431d72f345694f774c9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 17:57:09 +1000 Subject: [PATCH 008/364] Fix failing test --- src/core/geometry/qgsgeometryfactory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/geometry/qgsgeometryfactory.cpp b/src/core/geometry/qgsgeometryfactory.cpp index 9361def73cc..3dea39be47d 100644 --- a/src/core/geometry/qgsgeometryfactory.cpp +++ b/src/core/geometry/qgsgeometryfactory.cpp @@ -61,6 +61,7 @@ std::unique_ptr QgsGeometryFactory::geomFromWkb( QgsConstWk { Q_UNUSED( e ); QgsDebugMsg( "WKB exception: " + e.what() ); + geom.reset(); } } From c7affb3b70b92b90cbb26169e3aad0869cac9e20 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 19:16:16 +1000 Subject: [PATCH 009/364] Use const references to options instead of pointers in QgsVectorLayerExporter The use of pointers make ownership of the argument confusing, and there's nothing stopping us just using an empty map as the default value instead. --- python/core/qgsvectorlayerexporter.sip | 8 +++--- src/core/processing/qgsprocessingutils.cpp | 2 +- src/core/qgsvectorlayerexporter.cpp | 27 ++++++++++--------- src/core/qgsvectorlayerexporter.h | 8 +++--- src/providers/ogr/qgsgeopackagedataitems.cpp | 12 ++++----- .../postgres/qgspostgresdataitems.cpp | 2 +- .../spatialite/qgsspatialitedataitems.cpp | 2 +- 7 files changed, 31 insertions(+), 30 deletions(-) diff --git a/python/core/qgsvectorlayerexporter.sip b/python/core/qgsvectorlayerexporter.sip index 26822d66818..afe2eb6fc44 100644 --- a/python/core/qgsvectorlayerexporter.sip +++ b/python/core/qgsvectorlayerexporter.sip @@ -52,7 +52,7 @@ class QgsVectorLayerExporter : QgsFeatureSink const QgsCoordinateReferenceSystem &destCRS, bool onlySelected = false, QString *errorMessage /Out/ = 0, - QMap *options = 0, + const QMap &options = QMap(), QgsFeedback *feedback = 0 ); %Docstring @@ -76,7 +76,7 @@ class QgsVectorLayerExporter : QgsFeatureSink QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, bool overwrite = false, - const QMap *options = 0 ); + const QMap &options = QMap() ); %Docstring Constructor for QgsVectorLayerExporter. \param uri URI for destination data source @@ -152,7 +152,7 @@ class QgsVectorLayerExporterTask : QgsTask const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, - QMap *options = 0, + const QMap &options = QMap(), bool ownsLayer = false ); %Docstring Constructor for QgsVectorLayerExporterTask. Takes a source ``layer``, destination ``uri`` @@ -165,7 +165,7 @@ class QgsVectorLayerExporterTask : QgsTask const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, - QMap *options = 0 ) /Factory/; + const QMap &options = QMap() ) /Factory/; %Docstring Creates a new QgsVectorLayerExporterTask which has ownership over a source ``layer``. When the export task has completed (successfully or otherwise) ``layer`` will be diff --git a/src/core/processing/qgsprocessingutils.cpp b/src/core/processing/qgsprocessingutils.cpp index f4ede6c4fd4..fe4e0742cac 100644 --- a/src/core/processing/qgsprocessingutils.cpp +++ b/src/core/processing/qgsprocessingutils.cpp @@ -369,7 +369,7 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs else { //create empty layer - std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, false, &options ) ); + std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, false, options ) ); if ( exporter->errorCode() ) return nullptr; diff --git a/src/core/qgsvectorlayerexporter.cpp b/src/core/qgsvectorlayerexporter.cpp index 943c8de2ebd..88be1917a02 100644 --- a/src/core/qgsvectorlayerexporter.cpp +++ b/src/core/qgsvectorlayerexporter.cpp @@ -51,7 +51,7 @@ QgsVectorLayerExporter::QgsVectorLayerExporter( const QString &uri, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, bool overwrite, - const QMap *options ) + const QMap &options ) : mErrorCount( 0 ) , mAttributeCount( -1 ) @@ -78,7 +78,7 @@ QgsVectorLayerExporter::QgsVectorLayerExporter( const QString &uri, // create an empty layer QString errMsg; - mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, options ); + mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, !options.isEmpty() ? &options : nullptr ); if ( errorCode() ) { mErrorMessage = errMsg; @@ -100,8 +100,8 @@ QgsVectorLayerExporter::QgsVectorLayerExporter( const QString &uri, if ( providerKey == "ogr" ) { QString layerName; - if ( options && options->contains( QStringLiteral( "layerName" ) ) ) - layerName = options->value( QStringLiteral( "layerName" ) ).toString(); + if ( options.contains( QStringLiteral( "layerName" ) ) ) + layerName = options.value( QStringLiteral( "layerName" ) ).toString(); if ( !layerName.isEmpty() ) { uriUpdated += "|layername="; @@ -231,7 +231,7 @@ QgsVectorLayerExporter::exportLayer( QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destCRS, bool onlySelected, QString *errorMessage, - QMap *options, + const QMap &options, QgsFeedback *feedback ) { QgsCoordinateReferenceSystem outputCRS; @@ -256,10 +256,11 @@ QgsVectorLayerExporter::exportLayer( QgsVectorLayer *layer, bool overwrite = false; bool forceSinglePartGeom = false; - if ( options ) + QMap providerOptions = options; + if ( !options.isEmpty() ) { - overwrite = options->take( QStringLiteral( "overwrite" ) ).toBool(); - forceSinglePartGeom = options->take( QStringLiteral( "forceSinglePartGeometryType" ) ).toBool(); + overwrite = providerOptions.take( QStringLiteral( "overwrite" ) ).toBool(); + forceSinglePartGeom = providerOptions.take( QStringLiteral( "forceSinglePartGeometryType" ) ).toBool(); } QgsFields fields = layer->fields(); @@ -304,7 +305,7 @@ QgsVectorLayerExporter::exportLayer( QgsVectorLayer *layer, } QgsVectorLayerExporter *writer = - new QgsVectorLayerExporter( uri, providerKey, fields, wkbType, outputCRS, overwrite, options ); + new QgsVectorLayerExporter( uri, providerKey, fields, wkbType, outputCRS, overwrite, providerOptions ); // check whether file creation was successful ExportError err = writer->errorCode(); @@ -456,21 +457,21 @@ QgsVectorLayerExporter::exportLayer( QgsVectorLayer *layer, // QgsVectorLayerExporterTask // -QgsVectorLayerExporterTask::QgsVectorLayerExporterTask( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, QMap *options, bool ownsLayer ) +QgsVectorLayerExporterTask::QgsVectorLayerExporterTask( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap &options, bool ownsLayer ) : QgsTask( tr( "Exporting %1" ).arg( layer->name() ), QgsTask::CanCancel ) , mLayer( layer ) , mOwnsLayer( ownsLayer ) , mDestUri( uri ) , mDestProviderKey( providerKey ) , mDestCrs( destinationCrs ) - , mOptions( options ? * options : QMap() ) + , mOptions( options ) , mOwnedFeedback( new QgsFeedback() ) { if ( mLayer ) setDependentLayers( QList< QgsMapLayer * >() << mLayer ); } -QgsVectorLayerExporterTask *QgsVectorLayerExporterTask::withLayerOwnership( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, QMap *options ) +QgsVectorLayerExporterTask *QgsVectorLayerExporterTask::withLayerOwnership( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap &options ) { std::unique_ptr< QgsVectorLayerExporterTask > newTask( new QgsVectorLayerExporterTask( layer, uri, providerKey, destinationCrs, options ) ); newTask->mOwnsLayer = true; @@ -493,7 +494,7 @@ bool QgsVectorLayerExporterTask::run() mError = QgsVectorLayerExporter::exportLayer( mLayer.data(), mDestUri, mDestProviderKey, mDestCrs, false, &mErrorMessage, - &mOptions, mOwnedFeedback.get() ); + mOptions, mOwnedFeedback.get() ); return mError == QgsVectorLayerExporter::NoError; } diff --git a/src/core/qgsvectorlayerexporter.h b/src/core/qgsvectorlayerexporter.h index 9e3e6eb74e0..08681165522 100644 --- a/src/core/qgsvectorlayerexporter.h +++ b/src/core/qgsvectorlayerexporter.h @@ -84,7 +84,7 @@ class CORE_EXPORT QgsVectorLayerExporter : public QgsFeatureSink const QgsCoordinateReferenceSystem &destCRS, bool onlySelected = false, QString *errorMessage SIP_OUT = 0, - QMap *options = nullptr, + const QMap &options = QMap(), QgsFeedback *feedback = nullptr ); @@ -105,7 +105,7 @@ class CORE_EXPORT QgsVectorLayerExporter : public QgsFeatureSink QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, bool overwrite = false, - const QMap *options = nullptr ); + const QMap &options = QMap() ); //! QgsVectorLayerExporter cannot be copied QgsVectorLayerExporter( const QgsVectorLayerExporter &rh ) = delete; @@ -195,7 +195,7 @@ class CORE_EXPORT QgsVectorLayerExporterTask : public QgsTask const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, - QMap *options = nullptr, + const QMap &options = QMap(), bool ownsLayer = false ); /** @@ -208,7 +208,7 @@ class CORE_EXPORT QgsVectorLayerExporterTask : public QgsTask const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, - QMap *options = nullptr ) SIP_FACTORY; + const QMap &options = QMap() ) SIP_FACTORY; virtual void cancel() override; diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 7806067f0d5..80c3b62b55d 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -322,13 +322,13 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct 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 ); + QVariantMap options; + 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 ) ); + std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( srcLayer, uri, QStringLiteral( "ogr" ), srcLayer->crs(), options, owner ) ); // when export is successful: connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]() diff --git a/src/providers/postgres/qgspostgresdataitems.cpp b/src/providers/postgres/qgspostgresdataitems.cpp index fb8deb10935..2dc0063d84a 100644 --- a/src/providers/postgres/qgspostgresdataitems.cpp +++ b/src/providers/postgres/qgspostgresdataitems.cpp @@ -244,7 +244,7 @@ bool QgsPGConnectionItem::handleDrop( const QMimeData *data, const QString &toSc uri.setSchema( toSchema ); } - std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( srcLayer, uri.uri( false ), QStringLiteral( "postgres" ), srcLayer->crs(), nullptr, owner ) ); + std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( srcLayer, uri.uri( false ), QStringLiteral( "postgres" ), srcLayer->crs(), QVariantMap(), owner ) ); // when export is successful: connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]() diff --git a/src/providers/spatialite/qgsspatialitedataitems.cpp b/src/providers/spatialite/qgsspatialitedataitems.cpp index 932e1116bf4..dd0aa3fb08c 100644 --- a/src/providers/spatialite/qgsspatialitedataitems.cpp +++ b/src/providers/spatialite/qgsspatialitedataitems.cpp @@ -229,7 +229,7 @@ bool QgsSLConnectionItem::handleDrop( const QMimeData *data, Qt::DropAction ) destUri.setDataSource( QString(), u.name, srcLayer->geometryType() != QgsWkbTypes::NullGeometry ? QStringLiteral( "geom" ) : QString() ); QgsDebugMsg( "URI " + destUri.uri() ); - std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( srcLayer, destUri.uri(), QStringLiteral( "spatialite" ), srcLayer->crs(), nullptr, owner ) ); + std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( srcLayer, destUri.uri(), QStringLiteral( "spatialite" ), srcLayer->crs(), QVariantMap(), owner ) ); // when export is successful: connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]() From 7a314f3cb256da52517f821192d61a5d25f69640 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 20:00:49 +1000 Subject: [PATCH 010/364] Avoid 'flashy' status bar progress widget when map renders are fast If previous canvas render took less than 0.5 seconds, delay the appearance of the 'render in progress' status bar widget by a short amount - this avoids the status bar rapidly appearing and then disappearing for very fast renders --- src/app/qgisapp.cpp | 23 ++++++++++++++++++++++- src/app/qgisapp.h | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 25fc2159578..e57fcdb34aa 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -8282,11 +8282,32 @@ void QgisApp::refreshMapCanvas() void QgisApp::canvasRefreshStarted() { - showProgress( -1, 0 ); // trick to make progress bar show busy indicator + mLastRenderTime.restart(); + // if previous render took less than 0.5 seconds, delay the appearance of the + // render in progress status bar by 0.5 seconds - this avoids the status bar + // rapidly appearing and then disappearing for very fast renders + if ( mLastRenderTimeSeconds > 0 && mLastRenderTimeSeconds < 0.5 ) + { + mRenderProgressBarTimer.setSingleShot( true ); + mRenderProgressBarTimer.setInterval( 500 ); + disconnect( mRenderProgressBarTimerConnection ); + mRenderProgressBarTimerConnection = connect( &mRenderProgressBarTimer, &QTimer::timeout, [ = ]() + { + showProgress( -1, 0 ); + } + ); + mRenderProgressBarTimer.start(); + } + else + { + showProgress( -1, 0 ); // trick to make progress bar show busy indicator + } } void QgisApp::canvasRefreshFinished() { + mRenderProgressBarTimer.stop(); + mLastRenderTimeSeconds = mLastRenderTime.elapsed() / 1000.0; showProgress( 0, 0 ); // stop the busy indicator } diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 62f87cca313..a39a4f60b77 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -2067,6 +2067,11 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QgsStatusBar *mStatusBar = nullptr; + QTime mLastRenderTime; + double mLastRenderTimeSeconds = 0; + QTimer mRenderProgressBarTimer; + QMetaObject::Connection mRenderProgressBarTimerConnection; + friend class TestQgisAppPython; }; From 7b2250bb35d74e5751b3c456b314b1ba92147b04 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 21 Jun 2017 20:52:36 +1000 Subject: [PATCH 011/364] Convert processing tests to use native algs where they exist --- .../processing/tests/AlgorithmsTestBase.py | 5 ++ .../expected/nullGeometryDissolve_output.gfs | 3 +- .../tests/testdata/qgis_algorithm_tests.yaml | 89 +++++++++---------- 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index e5f1de9365f..7f4837fe62d 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -56,6 +56,7 @@ from processing.script.ScriptAlgorithmProvider import ScriptAlgorithmProvider # from qgis.core import (QgsVectorLayer, QgsRasterLayer, + QgsFeatureRequest, QgsMapLayer, QgsProject, QgsApplication, @@ -123,6 +124,10 @@ class AlgorithmsTest(object): # ignore user setting for invalid geometry handling context = QgsProcessingContext() context.setProject(QgsProject.instance()) + + if 'skipInvalid' in defs and defs['skipInvalid']: + context.setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid) + feedback = QgsProcessingFeedback() if expectFailure: diff --git a/python/plugins/processing/tests/testdata/expected/nullGeometryDissolve_output.gfs b/python/plugins/processing/tests/testdata/expected/nullGeometryDissolve_output.gfs index 9481feb0e91..0ed5de4c983 100644 --- a/python/plugins/processing/tests/testdata/expected/nullGeometryDissolve_output.gfs +++ b/python/plugins/processing/tests/testdata/expected/nullGeometryDissolve_output.gfs @@ -2,7 +2,8 @@ nullGeometryDissolve_output nullGeometryDissolve_output - 3 + + 6 EPSG:3003 1 diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 982d1e12b5a..9cdf58451b0 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -289,53 +289,48 @@ tests: name: expected/dissolve_two_fields.gml type: vector -# - name: Dissolve with geometries reported as valid but as invalid with isGeosValid -# algorithm: qgis:dissolve -# params: -# DISSOLVE_ALL: 'True' -# FIELD: None -# INPUT: -# name: custom/innerRingTouchesOuterRing.gml -# type: vector -# results: -# OUTPUT: -# type: vector -# name: expected/innerRingTouchesOuterRing_output.gml -# compare: -# geometry: -# precision: 0 -# -# - name: Dissolve with NULL geometries -# algorithm: qgis:dissolve -# params: -# DISSOLVE_ALL: 'True' -# FIELD: None -# INPUT: -# name: custom/nullGeometryDissolve.gml -# type: vector -# results: -# OUTPUT: -# type: vector -# name: expected/nullGeometryDissolve_output.gml -# compare: -# geometry: -# precision: 7 -# -# - name: Dissolve with invalid geometries -# algorithm: qgis:dissolve -# params: -# DISSOLVE_ALL: 'True' -# FIELD: None -# INPUT: -# name: custom/PolygonDissolveTest.gml -# type: vector -# results: -# OUTPUT: -# type: vector -# name: expected/PolygonDissolveTest_output.gml -# compare: -# geometry: -# precision: 7 + - name: Dissolve with geometries reported as valid but as invalid with isGeosValid + algorithm: native:dissolve + params: + INPUT: + name: custom/innerRingTouchesOuterRing.gml + type: vector + results: + OUTPUT: + type: vector + name: expected/innerRingTouchesOuterRing_output.gml + compare: + geometry: + precision: 0 + + - name: Dissolve with NULL geometries + algorithm: native:dissolve + params: + INPUT: + name: custom/nullGeometryDissolve.gml + type: vector + results: + OUTPUT: + type: vector + name: expected/nullGeometryDissolve_output.gml + compare: + geometry: + precision: 7 + + - name: Dissolve with invalid geometries + algorithm: native:dissolve + skipInvalid: true + params: + INPUT: + name: custom/PolygonDissolveTest.gml + type: vector + results: + OUTPUT: + type: vector + name: expected/PolygonDissolveTest_output.gml + compare: + geometry: + precision: 7 - algorithm: qgis:fixeddistancebuffer name: Basic polygon buffer From 6c17577fbe7717fb48061f81e64598c46602c707 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 19:35:16 +1000 Subject: [PATCH 012/364] Use a QgsMapRendererSequentialJob for rendering preview maps Means that at most the preview jobs will use a single thread instead of hammering all available threads. --- src/gui/qgsmapcanvas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index 71a825e8854..8b69645d22f 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -2160,7 +2160,7 @@ void QgsMapCanvas::startPreviewJob( int number ) jobSettings.setExtent( jobExtent ); jobSettings.setFlag( QgsMapSettings::DrawLabeling, false ); - QgsMapRendererQImageJob *job = new QgsMapRendererParallelJob( jobSettings ); + QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings ); mPreviewJobs.append( job ); connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished ); job->start(); From 628a1b096b26cb617bf79f9088532afa64bbc47c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 17 Aug 2017 19:39:40 +1000 Subject: [PATCH 013/364] Also delay initial preview job by a short timeout --- src/gui/qgsmapcanvas.cpp | 25 +++++++++++++++---------- src/gui/qgsmapcanvas.h | 1 + 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index 8b69645d22f..17dff379a64 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -2132,7 +2132,7 @@ const QgsLabelingEngineSettings &QgsMapCanvas::labelingEngineSettings() const void QgsMapCanvas::startPreviewJobs() { stopPreviewJobs(); //just in case still running - startPreviewJob( 0 ); + schedulePreviewJob( 0 ); } void QgsMapCanvas::startPreviewJob( int number ) @@ -2167,15 +2167,7 @@ void QgsMapCanvas::startPreviewJob( int number ) if ( number < 8 ) { - mPreviewTimer.setSingleShot( true ); - mPreviewTimer.setInterval( 250 ); - disconnect( mPreviewTimerConnection ); - mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, [ = ]() - { - startPreviewJob( number + 1 ); - } - ); - mPreviewTimer.start(); + schedulePreviewJob( number + 1 ); } } @@ -2194,3 +2186,16 @@ void QgsMapCanvas::stopPreviewJobs() } mPreviewJobs.clear(); } + +void QgsMapCanvas::schedulePreviewJob( int number ) +{ + mPreviewTimer.setSingleShot( true ); + mPreviewTimer.setInterval( 250 ); + disconnect( mPreviewTimerConnection ); + mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, [ = ]() + { + startPreviewJob( number ); + } + ); + mPreviewTimer.start(); +} diff --git a/src/gui/qgsmapcanvas.h b/src/gui/qgsmapcanvas.h index 14a29784648..483847452b0 100644 --- a/src/gui/qgsmapcanvas.h +++ b/src/gui/qgsmapcanvas.h @@ -882,6 +882,7 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView void startPreviewJobs(); void stopPreviewJobs(); + void schedulePreviewJob( int number ); friend class TestQgsMapCanvas; From 9bfca65ac3623feac6316478f772cff577951607 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 18 Aug 2017 00:04:07 +1000 Subject: [PATCH 014/364] Add API to enable/disable preview jobs Disabled by default, and enabled only for main canvas (not secondary canvases) --- python/gui/qgsmapcanvas.sip | 21 +++++++++++++++++++++ src/app/qgisapp.cpp | 1 + src/gui/qgsmapcanvas.cpp | 13 ++++++++++++- src/gui/qgsmapcanvas.h | 23 +++++++++++++++++++++++ tests/src/python/test_qgsmapcanvas.py | 8 ++++++++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/python/gui/qgsmapcanvas.sip b/python/gui/qgsmapcanvas.sip index 2fff0b77c91..4d4ee376371 100644 --- a/python/gui/qgsmapcanvas.sip +++ b/python/gui/qgsmapcanvas.sip @@ -593,6 +593,27 @@ returns last position of mouse cursor :rtype: QgsLabelingEngineSettings %End + bool previewJobsEnabled() const; +%Docstring + Returns true if canvas map preview jobs (low priority render jobs which render portions + of the view just outside of the canvas extent, to allow preview of these + out-of-canvas areas when panning or zooming out the map) are enabled + for the canvas. +.. seealso:: setPreviewJobsEnabled() +.. versionadded:: 3.0 + :rtype: bool +%End + + void setPreviewJobsEnabled( bool enabled ); +%Docstring + Sets whether canvas map preview jobs (low priority render jobs which render portions + of the view just outside of the canvas extent, to allow preview of these + out-of-canvas areas when panning or zooming out the map) are ``enabled`` + for the canvas. +.. seealso:: previewJobsEnabled() +.. versionadded:: 3.0 +%End + public slots: void refresh(); diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 25fc2159578..08d5cefa846 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -727,6 +727,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh connect( mMapCanvas, &QgsMapCanvas::messageEmitted, this, &QgisApp::displayMessage ); mMapCanvas->setWhatsThis( tr( "Map canvas. This is where raster and vector " "layers are displayed when added to the map" ) ); + mMapCanvas->setPreviewJobsEnabled( true ); // set canvas color right away int myRed = settings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt(); diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index 17dff379a64..fb7b4ba0ac8 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -629,7 +629,8 @@ void QgsMapCanvas::rendererJobFinished() p.end(); mMap->setContent( img, imageRect( img, mSettings ) ); - startPreviewJobs(); + if ( mUsePreviewJobs ) + startPreviewJobs(); } // now we are in a slot called from mJob - do not delete it immediately @@ -664,6 +665,16 @@ QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &m return rect; } +bool QgsMapCanvas::previewJobsEnabled() const +{ + return mUsePreviewJobs; +} + +void QgsMapCanvas::setPreviewJobsEnabled( bool enabled ) +{ + mUsePreviewJobs = enabled; +} + void QgsMapCanvas::mapUpdateTimeout() { if ( mJob ) diff --git a/src/gui/qgsmapcanvas.h b/src/gui/qgsmapcanvas.h index 483847452b0..3abeb241796 100644 --- a/src/gui/qgsmapcanvas.h +++ b/src/gui/qgsmapcanvas.h @@ -84,6 +84,7 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView Q_OBJECT Q_PROPERTY( QString theme READ theme WRITE setTheme NOTIFY themeChanged ) + Q_PROPERTY( bool previewJobsEnabled READ previewJobsEnabled WRITE setPreviewJobsEnabled ) public: @@ -523,6 +524,26 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView */ const QgsLabelingEngineSettings &labelingEngineSettings() const; + /** + * Returns true if canvas map preview jobs (low priority render jobs which render portions + * of the view just outside of the canvas extent, to allow preview of these + * out-of-canvas areas when panning or zooming out the map) are enabled + * for the canvas. + * \see setPreviewJobsEnabled() + * \since QGIS 3.0 + */ + bool previewJobsEnabled() const; + + /** + * Sets whether canvas map preview jobs (low priority render jobs which render portions + * of the view just outside of the canvas extent, to allow preview of these + * out-of-canvas areas when panning or zooming out the map) are \a enabled + * for the canvas. + * \see previewJobsEnabled() + * \since QGIS 3.0 + */ + void setPreviewJobsEnabled( bool enabled ); + public slots: //! Repaints the canvas map @@ -854,6 +875,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView bool mAnnotationsVisible = true; + bool mUsePreviewJobs = false; + //! Force a resize of the map canvas item //! \since QGIS 2.16 void updateMapSize(); diff --git a/tests/src/python/test_qgsmapcanvas.py b/tests/src/python/test_qgsmapcanvas.py index e609f246cff..45ed415c347 100644 --- a/tests/src/python/test_qgsmapcanvas.py +++ b/tests/src/python/test_qgsmapcanvas.py @@ -47,6 +47,14 @@ class TestQgsMapCanvas(unittest.TestCase): with open(report_file_path, 'a') as report_file: report_file.write(self.report) + def testGettersSetters(self): + canvas = QgsMapCanvas() + + # should be disabled by default + self.assertFalse(canvas.previewJobsEnabled()) + canvas.setPreviewJobsEnabled(True) + self.assertTrue(canvas.previewJobsEnabled()) + def testDeferredUpdate(self): """ test that map canvas doesn't auto refresh on deferred layer update """ canvas = QgsMapCanvas() From 2d441a8d1df50d4b5af8879e77ae47da008b9624 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 18 Aug 2017 00:16:47 +1000 Subject: [PATCH 015/364] Make sure new map dock views use correct icon size --- src/app/qgsmapcanvasdockwidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/qgsmapcanvasdockwidget.cpp b/src/app/qgsmapcanvasdockwidget.cpp index 5e409409a42..108982a9a76 100644 --- a/src/app/qgsmapcanvasdockwidget.cpp +++ b/src/app/qgsmapcanvasdockwidget.cpp @@ -44,6 +44,8 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa static_cast< QVBoxLayout * >( mContents->layout() )->setSpacing( 0 ); setWindowTitle( name ); + mToolbar->setIconSize( QgisApp::instance()->iconSize( true ) ); + mMapCanvas = new QgsMapCanvas( this ); mXyMarker = new QgsVertexMarker( mMapCanvas ); mXyMarker->setIconType( QgsVertexMarker::ICON_CIRCLE ); From e8ad0ba5d918c709d5f8ab97bc5ddd3bbee1dd80 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 18 Aug 2017 00:27:34 +1000 Subject: [PATCH 016/364] Move "sync extent" canvas view option to map settings menu This means all the settings are collected together, and the dock toolbar just contains navigation actions. (followup feedback from hackfest demo) --- src/app/qgsmapcanvasdockwidget.cpp | 45 +++++++++++++++------------- src/app/qgsmapcanvasdockwidget.h | 3 ++ src/ui/qgsmapcanvasdockwidgetbase.ui | 42 ++++++++++++++++---------- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/src/app/qgsmapcanvasdockwidget.cpp b/src/app/qgsmapcanvasdockwidget.cpp index 108982a9a76..4fca3eed47b 100644 --- a/src/app/qgsmapcanvasdockwidget.cpp +++ b/src/app/qgsmapcanvasdockwidget.cpp @@ -67,11 +67,6 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa mMainWidget->layout()->addWidget( mMapCanvas ); - connect( mActionSyncView, &QAction::toggled, this, [ = ] - { - syncViewCenter( mMainCanvas ); - } ); - mMenu = new QMenu(); connect( mMenu, &QMenu::aboutToShow, this, &QgsMapCanvasDockWidget::menuAboutToShow ); @@ -124,12 +119,18 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa mActionShowLabels->setChecked( true ); connect( mActionShowLabels, &QAction::toggled, this, &QgsMapCanvasDockWidget::showLabels ); + mSyncExtentCheckBox = settingsAction->syncExtentCheckBox(); mScaleCombo = settingsAction->scaleCombo(); mRotationEdit = settingsAction->rotationSpinBox(); mMagnificationEdit = settingsAction->magnifierSpinBox(); mSyncScaleCheckBox = settingsAction->syncScaleCheckBox(); mScaleFactorWidget = settingsAction->scaleFactorSpinBox(); + connect( mSyncExtentCheckBox, &QCheckBox::toggled, this, [ = ] + { + syncViewCenter( mMainCanvas ); + } ); + connect( mScaleCombo, &QgsScaleComboBox::scaleChanged, this, [ = ]( double scale ) { if ( !mBlockScaleUpdate ) @@ -203,7 +204,7 @@ QgsMapCanvasDockWidget::QgsMapCanvasDockWidget( const QString &name, QWidget *pa connect( &mResizeTimer, &QTimer::timeout, this, [ = ] { mBlockExtentSync = false; - if ( mActionSyncView->isChecked() ) + if ( mSyncExtentCheckBox->isChecked() ) syncViewCenter( mMainCanvas ); } ); } @@ -236,12 +237,12 @@ QgsMapCanvas *QgsMapCanvasDockWidget::mapCanvas() void QgsMapCanvasDockWidget::setViewCenterSynchronized( bool enabled ) { - mActionSyncView->setChecked( enabled ); + mSyncExtentCheckBox->setChecked( enabled ); } bool QgsMapCanvasDockWidget::isViewCenterSynchronized() const { - return mActionSyncView->isChecked(); + return mSyncExtentCheckBox->isChecked(); } void QgsMapCanvasDockWidget::setCursorMarkerVisible( bool visible ) @@ -350,7 +351,7 @@ void QgsMapCanvasDockWidget::mapExtentChanged() mScaleFactorWidget->setValue( newScaleFactor ); } - if ( mActionSyncView->isChecked() ) + if ( mSyncExtentCheckBox->isChecked() ) syncViewCenter( sourceCanvas ); } @@ -477,11 +478,15 @@ QgsMapSettingsAction::QgsMapSettingsAction( QWidget *parent ) { QGridLayout *gLayout = new QGridLayout(); gLayout->setContentsMargins( 3, 2, 3, 2 ); + + mSyncExtentCheckBox = new QCheckBox( tr( "Synchronize View Center with Main Map" ) ); + gLayout->addWidget( mSyncExtentCheckBox, 0, 0, 1, 2 ); + QLabel *label = new QLabel( tr( "Scale" ) ); - gLayout->addWidget( label, 0, 0 ); + gLayout->addWidget( label, 1, 0 ); mScaleCombo = new QgsScaleComboBox(); - gLayout->addWidget( mScaleCombo, 0, 1 ); + gLayout->addWidget( mScaleCombo, 1, 1 ); mRotationWidget = new QgsDoubleSpinBox(); mRotationWidget->setClearValue( 0.0 ); @@ -495,8 +500,8 @@ QgsMapSettingsAction::QgsMapSettingsAction( QWidget *parent ) mRotationWidget->setToolTip( tr( "Current clockwise map rotation in degrees" ) ); label = new QLabel( tr( "Rotation" ) ); - gLayout->addWidget( label, 1, 0 ); - gLayout->addWidget( mRotationWidget, 1, 1 ); + gLayout->addWidget( label, 2, 0 ); + gLayout->addWidget( mRotationWidget, 2, 1 ); QgsSettings settings; int minimumFactor = 100 * QgsGuiUtils::CANVAS_MAGNIFICATION_MIN; @@ -516,11 +521,11 @@ QgsMapSettingsAction::QgsMapSettingsAction( QWidget *parent ) mMagnifierWidget->setValue( defaultFactor ); label = new QLabel( tr( "Magnification" ) ); - gLayout->addWidget( label, 2, 0 ); - gLayout->addWidget( mMagnifierWidget, 2, 1 ); + gLayout->addWidget( label, 3, 0 ); + gLayout->addWidget( mMagnifierWidget, 3, 1 ); - mSyncScaleCheckBox = new QCheckBox( tr( "Synchronize scale" ) ); - gLayout->addWidget( mSyncScaleCheckBox, 3, 0, 1, 2 ); + mSyncScaleCheckBox = new QCheckBox( tr( "Synchronize Scale" ) ); + gLayout->addWidget( mSyncScaleCheckBox, 4, 0, 1, 2 ); mScaleFactorWidget = new QgsDoubleSpinBox(); mScaleFactorWidget->setSuffix( trUtf8( "×" ) ); @@ -536,9 +541,9 @@ QgsMapSettingsAction::QgsMapSettingsAction( QWidget *parent ) connect( mSyncScaleCheckBox, &QCheckBox::toggled, mScaleFactorWidget, &QgsDoubleSpinBox::setEnabled ); - label = new QLabel( tr( "Scale factor" ) ); - gLayout->addWidget( label, 4, 0 ); - gLayout->addWidget( mScaleFactorWidget, 4, 1 ); + label = new QLabel( tr( "Scale Factor" ) ); + gLayout->addWidget( label, 5, 0 ); + gLayout->addWidget( mScaleFactorWidget, 5, 1 ); QWidget *w = new QWidget(); w->setLayout( gLayout ); diff --git a/src/app/qgsmapcanvasdockwidget.h b/src/app/qgsmapcanvasdockwidget.h index 54337644414..f7a29f70a50 100644 --- a/src/app/qgsmapcanvasdockwidget.h +++ b/src/app/qgsmapcanvasdockwidget.h @@ -157,6 +157,7 @@ class APP_EXPORT QgsMapCanvasDockWidget : public QgsDockWidget, private Ui::QgsM QgsMapCanvas *mMainCanvas = nullptr; QMenu *mMenu = nullptr; QList mMenuPresetActions; + QCheckBox *mSyncExtentCheckBox = nullptr; QgsScaleComboBox *mScaleCombo = nullptr; QgsDoubleSpinBox *mRotationEdit = nullptr; QgsDoubleSpinBox *mMagnificationEdit = nullptr; @@ -187,6 +188,7 @@ class QgsMapSettingsAction: public QWidgetAction QgsMapSettingsAction( QWidget *parent = nullptr ); + QCheckBox *syncExtentCheckBox() { return mSyncExtentCheckBox; } QgsScaleComboBox *scaleCombo() { return mScaleCombo; } QgsDoubleSpinBox *rotationSpinBox() { return mRotationWidget; } QgsDoubleSpinBox *magnifierSpinBox() { return mMagnifierWidget; } @@ -194,6 +196,7 @@ class QgsMapSettingsAction: public QWidgetAction QCheckBox *syncScaleCheckBox() { return mSyncScaleCheckBox; } private: + QCheckBox *mSyncExtentCheckBox = nullptr; QgsScaleComboBox *mScaleCombo = nullptr; QgsDoubleSpinBox *mRotationWidget = nullptr; QgsDoubleSpinBox *mMagnifierWidget = nullptr; diff --git a/src/ui/qgsmapcanvasdockwidgetbase.ui b/src/ui/qgsmapcanvasdockwidgetbase.ui index 91eb39609bc..5fc11474ba6 100644 --- a/src/ui/qgsmapcanvasdockwidgetbase.ui +++ b/src/ui/qgsmapcanvasdockwidgetbase.ui @@ -41,7 +41,6 @@ false - @@ -82,21 +81,6 @@ Set Map CRS - - - true - - - - :/images/themes/default/mActionLockExtent.svg:/images/themes/default/mActionLockExtent.svg - - - Synchronize View - - - Synchronize View Center with Main Map - - Rename View… @@ -178,6 +162,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From 37b899fb99a567b1499d4648641d370936c5d4e1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 18 Aug 2017 00:37:58 +1000 Subject: [PATCH 017/364] Add a pure virtual clone method to processing parameter definitions And use it when we need to clone parameters (instead of more fragile conversion to and from variants) This fixes model loading which use algorithms which create python subclasses of parameter definitions --- .../processing/qgsprocessingparameters.sip | 52 ++++++++ .../plugins/processing/algs/gdal/buildvrt.py | 4 + .../plugins/processing/algs/qgis/Aggregate.py | 4 + .../processing/algs/qgis/FieldsMapper.py | 12 +- .../plugins/processing/algs/qgis/Heatmap.py | 4 + .../models/qgsprocessingmodelalgorithm.cpp | 26 ++-- .../processing/qgsprocessingparameters.cpp | 115 ++++++++++++++++++ src/core/processing/qgsprocessingparameters.h | 28 +++++ 8 files changed, 227 insertions(+), 18 deletions(-) diff --git a/python/core/processing/qgsprocessingparameters.sip b/python/core/processing/qgsprocessingparameters.sip index e0bf143e895..1fdd27566d1 100644 --- a/python/core/processing/qgsprocessingparameters.sip +++ b/python/core/processing/qgsprocessingparameters.sip @@ -213,6 +213,12 @@ class QgsProcessingParameterDefinition virtual ~QgsProcessingParameterDefinition(); + virtual QgsProcessingParameterDefinition *clone() const = 0 /Factory/; +%Docstring + Creates a clone of the parameter definition. + :rtype: QgsProcessingParameterDefinition +%End + virtual QString type() const = 0; %Docstring Unique parameter type name. @@ -620,6 +626,8 @@ class QgsProcessingParameterBoolean : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const; @@ -656,6 +664,8 @@ class QgsProcessingParameterCrs : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -693,6 +703,8 @@ class QgsProcessingParameterMapLayer : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -730,6 +742,8 @@ class QgsProcessingParameterExtent : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -768,6 +782,8 @@ class QgsProcessingParameterPoint : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -809,6 +825,8 @@ class QgsProcessingParameterFile : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -879,6 +897,8 @@ class QgsProcessingParameterMatrix : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -967,6 +987,8 @@ class QgsProcessingParameterMultipleLayers : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1050,6 +1072,8 @@ class QgsProcessingParameterNumber : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1133,6 +1157,8 @@ class QgsProcessingParameterRange : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1188,6 +1214,8 @@ class QgsProcessingParameterRasterLayer : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1227,6 +1255,8 @@ class QgsProcessingParameterEnum : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1298,6 +1328,8 @@ class QgsProcessingParameterString : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const; @@ -1354,6 +1386,8 @@ class QgsProcessingParameterExpression : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const; @@ -1413,6 +1447,8 @@ class QgsProcessingParameterVectorLayer : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1479,6 +1515,8 @@ class QgsProcessingParameterField : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1565,6 +1603,8 @@ class QgsProcessingParameterFeatureSource : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1704,6 +1744,8 @@ class QgsProcessingParameterFeatureSink : QgsProcessingDestinationParameter Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1781,6 +1823,8 @@ class QgsProcessingParameterVectorDestination : QgsProcessingDestinationParamete Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1852,6 +1896,8 @@ class QgsProcessingParameterRasterDestination : QgsProcessingDestinationParamete Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1895,6 +1941,8 @@ class QgsProcessingParameterFileDestination : QgsProcessingDestinationParameter Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1958,6 +2006,8 @@ class QgsProcessingParameterFolderDestination : QgsProcessingDestinationParamete Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; @@ -1998,6 +2048,8 @@ class QgsProcessingParameterBand : QgsProcessingParameterDefinition Returns the type name for the parameter class. :rtype: str %End + virtual QgsProcessingParameterDefinition *clone() const /Factory/; + virtual QString type() const; virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const; diff --git a/python/plugins/processing/algs/gdal/buildvrt.py b/python/plugins/processing/algs/gdal/buildvrt.py index 07d9afe2fd3..e5e9597fcce 100644 --- a/python/plugins/processing/algs/gdal/buildvrt.py +++ b/python/plugins/processing/algs/gdal/buildvrt.py @@ -63,6 +63,10 @@ class buildvrt(GdalAlgorithm): def __init__(self, name, description): super().__init__(name, description) + def clone(self): + copy = ParameterVrtDestination(self.name(), self.description()) + return copy + def type(self): return 'vrt_destination' diff --git a/python/plugins/processing/algs/qgis/Aggregate.py b/python/plugins/processing/algs/qgis/Aggregate.py index 60c84b11e86..bed5597ece5 100644 --- a/python/plugins/processing/algs/qgis/Aggregate.py +++ b/python/plugins/processing/algs/qgis/Aggregate.py @@ -78,6 +78,10 @@ class Aggregate(QgisAlgorithm): super().__init__(name, description) self._parentLayerParameter = parentLayerParameterName + def clone(self): + copy = ParameterAggregates(self.name(), self.description(), self._parentLayerParameter) + return copy + def type(self): return 'aggregates' diff --git a/python/plugins/processing/algs/qgis/FieldsMapper.py b/python/plugins/processing/algs/qgis/FieldsMapper.py index 96ed9724a82..d155f2e695c 100644 --- a/python/plugins/processing/algs/qgis/FieldsMapper.py +++ b/python/plugins/processing/algs/qgis/FieldsMapper.py @@ -26,18 +26,12 @@ __copyright__ = '(C) 2014, Arnaud Morvan' __revision__ = '$Format:%H$' from qgis.core import ( - QgsApplication, QgsDistanceArea, QgsExpression, - QgsFeature, - QgsFeatureSink, QgsField, QgsFields, QgsProcessingException, - QgsProcessingParameterDefinition, - QgsProcessingUtils, - QgsProject, -) + QgsProcessingParameterDefinition) from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm @@ -59,6 +53,10 @@ class FieldsMapper(QgisFeatureBasedAlgorithm): super().__init__(name, description) self._parentLayerParameter = parentLayerParameterName + def clone(self): + copy = ParameterFieldsMapping(self.name(), self.description(), self._parentLayerParameter) + return copy + def type(self): return 'fields_mapping' diff --git a/python/plugins/processing/algs/qgis/Heatmap.py b/python/plugins/processing/algs/qgis/Heatmap.py index 9c84ee673c7..1d40131a52b 100644 --- a/python/plugins/processing/algs/qgis/Heatmap.py +++ b/python/plugins/processing/algs/qgis/Heatmap.py @@ -116,6 +116,10 @@ class Heatmap(QgisAlgorithm): self.radius_param = radius_param self.radius_field_param = radius_field_param + def clone(self): + copy = ParameterHeatmapPixelSize(self.name(), self.description(), self.parent_layer, self.radius_param, self.radius_field_param, self.minimum(), self.maximum(), self.defaultValue((), self.flags() & QgsProcessingParameterDefinition.FlagOptional)) + return copy + pixel_size_param = ParameterHeatmapPixelSize(self.PIXEL_SIZE, self.tr('Output raster size'), parent_layer=self.INPUT, diff --git a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp index d41c5e8cbd0..2d8a62d5fe9 100644 --- a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp +++ b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp @@ -729,19 +729,19 @@ void QgsProcessingModelAlgorithm::updateDestinationParameters() if ( !source ) continue; - QgsProcessingParameterDefinition *param = QgsProcessingParameters::parameterFromVariantMap( source->toVariantMap() ); + std::unique_ptr< QgsProcessingParameterDefinition > param( source->clone() ); param->setName( outputIt->childId() + ':' + outputIt->name() ); param->setDescription( outputIt->description() ); - addParameter( param ); - if ( const QgsProcessingDestinationParameter *destParam = dynamic_cast< const QgsProcessingDestinationParameter *>( param ) ) + if ( const QgsProcessingDestinationParameter *destParam = dynamic_cast< const QgsProcessingDestinationParameter *>( param.get() ) ) { - QgsProcessingOutputDefinition *output = destParam->toOutputDefinition(); + std::unique_ptr< QgsProcessingOutputDefinition > output( destParam->toOutputDefinition() ); if ( output ) { - addOutput( output ); + addOutput( output.release() ); } } + addParameter( param.release() ); } } } @@ -793,8 +793,11 @@ bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model ) for ( ; childIt != childMap.constEnd(); ++childIt ) { QgsProcessingModelChildAlgorithm child; + // we be leniant here - even if we couldn't load a parameter, don't interrupt the model loading + // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters) + // with no way for users to repair them if ( !child.loadVariant( childIt.value() ) ) - return false; + continue; mChildAlgorithms.insert( child.childId(), child ); } @@ -817,11 +820,12 @@ bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model ) QVariantMap::const_iterator paramDefIt = paramDefMap.constBegin(); for ( ; paramDefIt != paramDefMap.constEnd(); ++paramDefIt ) { - QgsProcessingParameterDefinition *param = QgsProcessingParameters::parameterFromVariantMap( paramDefIt.value().toMap() ); - if ( !param ) - return false; - - addParameter( param ); + std::unique_ptr< QgsProcessingParameterDefinition > param( QgsProcessingParameters::parameterFromVariantMap( paramDefIt.value().toMap() ) ); + // we be leniant here - even if we couldn't load a parameter, don't interrupt the model loading + // otherwise models may become unusable (e.g. due to removed plugins providing algs/parameters) + // with no way for users to repair them + if ( param ) + addParameter( param.release() ); } updateDestinationParameters(); diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index af1d1271667..0acd2a9fc93 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -975,6 +975,11 @@ QgsProcessingParameterBoolean::QgsProcessingParameterBoolean( const QString &nam : QgsProcessingParameterDefinition( name, description, defaultValue, optional ) {} +QgsProcessingParameterDefinition *QgsProcessingParameterBoolean::clone() const +{ + return new QgsProcessingParameterBoolean( *this ); +} + QString QgsProcessingParameterBoolean::valueAsPythonString( const QVariant &val, QgsProcessingContext & ) const { if ( val.canConvert() ) @@ -1003,6 +1008,11 @@ QgsProcessingParameterCrs::QgsProcessingParameterCrs( const QString &name, const } +QgsProcessingParameterDefinition *QgsProcessingParameterCrs::clone() const +{ + return new QgsProcessingParameterCrs( *this ); +} + bool QgsProcessingParameterCrs::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -1044,6 +1054,11 @@ QgsProcessingParameterMapLayer::QgsProcessingParameterMapLayer( const QString &n } +QgsProcessingParameterDefinition *QgsProcessingParameterMapLayer::clone() const +{ + return new QgsProcessingParameterMapLayer( *this ); +} + bool QgsProcessingParameterMapLayer::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context ) const { if ( !input.isValid() ) @@ -1097,6 +1112,11 @@ QgsProcessingParameterExtent::QgsProcessingParameterExtent( const QString &name, } +QgsProcessingParameterDefinition *QgsProcessingParameterExtent::clone() const +{ + return new QgsProcessingParameterExtent( *this ); +} + bool QgsProcessingParameterExtent::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context ) const { if ( !input.isValid() ) @@ -1163,6 +1183,11 @@ QgsProcessingParameterPoint::QgsProcessingParameterPoint( const QString &name, c } +QgsProcessingParameterDefinition *QgsProcessingParameterPoint::clone() const +{ + return new QgsProcessingParameterPoint( *this ); +} + bool QgsProcessingParameterPoint::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -1205,6 +1230,11 @@ QgsProcessingParameterFile::QgsProcessingParameterFile( const QString &name, con } +QgsProcessingParameterDefinition *QgsProcessingParameterFile::clone() const +{ + return new QgsProcessingParameterFile( *this ); +} + bool QgsProcessingParameterFile::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -1275,6 +1305,11 @@ QgsProcessingParameterMatrix::QgsProcessingParameterMatrix( const QString &name, } +QgsProcessingParameterDefinition *QgsProcessingParameterMatrix::clone() const +{ + return new QgsProcessingParameterMatrix( *this ); +} + bool QgsProcessingParameterMatrix::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -1390,6 +1425,11 @@ QgsProcessingParameterMultipleLayers::QgsProcessingParameterMultipleLayers( cons } +QgsProcessingParameterDefinition *QgsProcessingParameterMultipleLayers::clone() const +{ + return new QgsProcessingParameterMultipleLayers( *this ); +} + bool QgsProcessingParameterMultipleLayers::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context ) const { if ( !input.isValid() ) @@ -1583,6 +1623,11 @@ QgsProcessingParameterNumber::QgsProcessingParameterNumber( const QString &name, } +QgsProcessingParameterDefinition *QgsProcessingParameterNumber::clone() const +{ + return new QgsProcessingParameterNumber( *this ); +} + bool QgsProcessingParameterNumber::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -1673,6 +1718,11 @@ QgsProcessingParameterRange::QgsProcessingParameterRange( const QString &name, c } +QgsProcessingParameterDefinition *QgsProcessingParameterRange::clone() const +{ + return new QgsProcessingParameterRange( *this ); +} + bool QgsProcessingParameterRange::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -1765,6 +1815,11 @@ QgsProcessingParameterRasterLayer::QgsProcessingParameterRasterLayer( const QStr } +QgsProcessingParameterDefinition *QgsProcessingParameterRasterLayer::clone() const +{ + return new QgsProcessingParameterRasterLayer( *this ); +} + bool QgsProcessingParameterRasterLayer::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context ) const { if ( !input.isValid() ) @@ -1818,6 +1873,11 @@ QgsProcessingParameterEnum::QgsProcessingParameterEnum( const QString &name, con } +QgsProcessingParameterDefinition *QgsProcessingParameterEnum::clone() const +{ + return new QgsProcessingParameterEnum( *this ); +} + bool QgsProcessingParameterEnum::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -1982,6 +2042,11 @@ QgsProcessingParameterString::QgsProcessingParameterString( const QString &name, } +QgsProcessingParameterDefinition *QgsProcessingParameterString::clone() const +{ + return new QgsProcessingParameterString( *this ); +} + QString QgsProcessingParameterString::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const { if ( value.canConvert() ) @@ -2059,6 +2124,11 @@ QgsProcessingParameterExpression::QgsProcessingParameterExpression( const QStrin } +QgsProcessingParameterDefinition *QgsProcessingParameterExpression::clone() const +{ + return new QgsProcessingParameterExpression( *this ); +} + QString QgsProcessingParameterExpression::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const { if ( value.canConvert() ) @@ -2113,6 +2183,11 @@ QgsProcessingParameterVectorLayer::QgsProcessingParameterVectorLayer( const QStr } +QgsProcessingParameterDefinition *QgsProcessingParameterVectorLayer::clone() const +{ + return new QgsProcessingParameterVectorLayer( *this ); +} + bool QgsProcessingParameterVectorLayer::checkValueIsAcceptable( const QVariant &var, QgsProcessingContext *context ) const { if ( !var.isValid() ) @@ -2201,6 +2276,11 @@ QgsProcessingParameterField::QgsProcessingParameterField( const QString &name, c } +QgsProcessingParameterDefinition *QgsProcessingParameterField::clone() const +{ + return new QgsProcessingParameterField( *this ); +} + bool QgsProcessingParameterField::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) @@ -2402,6 +2482,11 @@ QgsProcessingParameterFeatureSource::QgsProcessingParameterFeatureSource( const } +QgsProcessingParameterDefinition *QgsProcessingParameterFeatureSource::clone() const +{ + return new QgsProcessingParameterFeatureSource( *this ); +} + bool QgsProcessingParameterFeatureSource::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context ) const { QVariant var = input; @@ -2579,6 +2664,11 @@ QgsProcessingParameterFeatureSink::QgsProcessingParameterFeatureSink( const QStr } +QgsProcessingParameterDefinition *QgsProcessingParameterFeatureSink::clone() const +{ + return new QgsProcessingParameterFeatureSink( *this ); +} + bool QgsProcessingParameterFeatureSink::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { QVariant var = input; @@ -2760,6 +2850,11 @@ QgsProcessingParameterRasterDestination::QgsProcessingParameterRasterDestination : QgsProcessingDestinationParameter( name, description, defaultValue, optional ) {} +QgsProcessingParameterDefinition *QgsProcessingParameterRasterDestination::clone() const +{ + return new QgsProcessingParameterRasterDestination( *this ); +} + bool QgsProcessingParameterRasterDestination::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { QVariant var = input; @@ -2831,6 +2926,11 @@ QgsProcessingParameterFileDestination::QgsProcessingParameterFileDestination( co } +QgsProcessingParameterDefinition *QgsProcessingParameterFileDestination::clone() const +{ + return new QgsProcessingParameterFileDestination( *this ); +} + bool QgsProcessingParameterFileDestination::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { QVariant var = input; @@ -2933,6 +3033,11 @@ QgsProcessingParameterFolderDestination::QgsProcessingParameterFolderDestination : QgsProcessingDestinationParameter( name, description, defaultValue, optional ) {} +QgsProcessingParameterDefinition *QgsProcessingParameterFolderDestination::clone() const +{ + return new QgsProcessingParameterFolderDestination( *this ); +} + bool QgsProcessingParameterFolderDestination::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { QVariant var = input; @@ -3012,6 +3117,11 @@ QgsProcessingParameterVectorDestination::QgsProcessingParameterVectorDestination } +QgsProcessingParameterDefinition *QgsProcessingParameterVectorDestination::clone() const +{ + return new QgsProcessingParameterVectorDestination( *this ); +} + bool QgsProcessingParameterVectorDestination::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { QVariant var = input; @@ -3179,6 +3289,11 @@ QgsProcessingParameterBand::QgsProcessingParameterBand( const QString &name, con } +QgsProcessingParameterDefinition *QgsProcessingParameterBand::clone() const +{ + return new QgsProcessingParameterBand( *this ); +} + bool QgsProcessingParameterBand::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const { if ( !input.isValid() ) diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index aec0eee7ddb..4b944e7460a 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -257,6 +257,11 @@ class CORE_EXPORT QgsProcessingParameterDefinition virtual ~QgsProcessingParameterDefinition() = default; + /** + * Creates a clone of the parameter definition. + */ + virtual QgsProcessingParameterDefinition *clone() const = 0 SIP_FACTORY; + /** * Unique parameter type name. */ @@ -640,6 +645,7 @@ class CORE_EXPORT QgsProcessingParameterBoolean : public QgsProcessingParameterD * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "boolean" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; QString asScriptCode() const override; @@ -670,6 +676,7 @@ class CORE_EXPORT QgsProcessingParameterCrs : public QgsProcessingParameterDefin * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "crs" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -701,6 +708,7 @@ class CORE_EXPORT QgsProcessingParameterMapLayer : public QgsProcessingParameter * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "layer" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -732,6 +740,7 @@ class CORE_EXPORT QgsProcessingParameterExtent : public QgsProcessingParameterDe * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "extent" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -764,6 +773,7 @@ class CORE_EXPORT QgsProcessingParameterPoint : public QgsProcessingParameterDef * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "point" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; @@ -801,6 +811,7 @@ class CORE_EXPORT QgsProcessingParameterFile : public QgsProcessingParameterDefi * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "file" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString asScriptCode() const override; @@ -865,6 +876,7 @@ class CORE_EXPORT QgsProcessingParameterMatrix : public QgsProcessingParameterDe * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "matrix" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -948,6 +960,7 @@ class CORE_EXPORT QgsProcessingParameterMultipleLayers : public QgsProcessingPar * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "multilayer" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1026,6 +1039,7 @@ class CORE_EXPORT QgsProcessingParameterNumber : public QgsProcessingParameterDe * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "number" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1103,6 +1117,7 @@ class CORE_EXPORT QgsProcessingParameterRange : public QgsProcessingParameterDef * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "range" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1152,6 +1167,7 @@ class CORE_EXPORT QgsProcessingParameterRasterLayer : public QgsProcessingParame * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "raster" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1185,6 +1201,7 @@ class CORE_EXPORT QgsProcessingParameterEnum : public QgsProcessingParameterDefi * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "enum" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1250,6 +1267,7 @@ class CORE_EXPORT QgsProcessingParameterString : public QgsProcessingParameterDe * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "string" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; QString asScriptCode() const override; @@ -1301,6 +1319,7 @@ class CORE_EXPORT QgsProcessingParameterExpression : public QgsProcessingParamet * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "expression" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; QStringList dependsOnOtherParameters() const override; @@ -1355,6 +1374,7 @@ class CORE_EXPORT QgsProcessingParameterVectorLayer : public QgsProcessingParame * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "vector" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1418,6 +1438,7 @@ class CORE_EXPORT QgsProcessingParameterField : public QgsProcessingParameterDef * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "field" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1497,6 +1518,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSource : public QgsProcessingPara * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "source" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1626,6 +1648,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingDestin * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "sink" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1690,6 +1713,7 @@ class CORE_EXPORT QgsProcessingParameterVectorDestination : public QgsProcessing * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "vectorDestination" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1751,6 +1775,7 @@ class CORE_EXPORT QgsProcessingParameterRasterDestination : public QgsProcessing * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "rasterDestination" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1786,6 +1811,7 @@ class CORE_EXPORT QgsProcessingParameterFileDestination : public QgsProcessingDe * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "fileDestination" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; @@ -1841,6 +1867,7 @@ class CORE_EXPORT QgsProcessingParameterFolderDestination : public QgsProcessing * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "folderDestination" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QgsProcessingOutputDefinition *toOutputDefinition() const override SIP_FACTORY; @@ -1874,6 +1901,7 @@ class CORE_EXPORT QgsProcessingParameterBand : public QgsProcessingParameterDefi * Returns the type name for the parameter class. */ static QString typeName() { return QStringLiteral( "band" ); } + QgsProcessingParameterDefinition *clone() const override SIP_FACTORY; QString type() const override { return typeName(); } bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; From f999897bddd6ada0feb1b074763979e8f9fee56d Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 17 Aug 2017 17:59:20 +0200 Subject: [PATCH 018/364] Geopackage moved delete to abstract item and implement raster deletion --- src/providers/ogr/qgsgeopackagedataitems.cpp | 152 +++++++++++++------ src/providers/ogr/qgsgeopackagedataitems.h | 33 ++-- 2 files changed, 125 insertions(+), 60 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 7806067f0d5..fc8ae3dddcb 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -13,6 +13,8 @@ * * ***************************************************************************/ +#include "sqlite3.h" + #include "qgsgeopackagedataitems.h" #include "qgsgeopackageconnection.h" #include "qgslogger.h" @@ -69,6 +71,7 @@ QVector QgsGeoPackageRootItem::createChildren() return connections; } +#ifdef HAVE_GUI QList QgsGeoPackageRootItem::actions() { QList lst; @@ -88,7 +91,7 @@ QWidget *QgsGeoPackageRootItem::paramWidget() { return nullptr; } - +#endif void QgsGeoPackageRootItem::connectionsChanged() { @@ -214,7 +217,7 @@ QVector QgsGeoPackageConnectionItem::createChildren() uri = QStringLiteral( "%1|layerid=%2" ).arg( mPath, layerId ); } QgsGeoPackageVectorLayerItem *item = new QgsGeoPackageVectorLayerItem( this, name, mPath, uri, layerType ); - QgsDebugMsgLevel( QStringLiteral( "Adding GPKG Vector item %1 %2 %3" ).arg( name, uri, geometryType ), 3 ); + QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Vector item %1 %2 %3" ).arg( name, uri, geometryType ), 3 ); children.append( item ); } } @@ -222,8 +225,7 @@ QVector QgsGeoPackageConnectionItem::createChildren() { QgsDebugMsgLevel( QStringLiteral( "Layer type is not a supported GeoPackage Vector layer %1" ).arg( mPath ), 3 ); } - QgsDebugMsgLevel( QStringLiteral( "Adding GPKG Vector item %1 %2 %3" ).arg( name, uri, geometryType ), 3 ); - qDebug() << QStringLiteral( "Adding GPKG Vector item %1 %2 %3" ).arg( name, uri, geometryType ); + QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Vector item %1 %2 %3" ).arg( name, uri, geometryType ), 3 ); } } } @@ -233,7 +235,7 @@ QVector QgsGeoPackageConnectionItem::createChildren() { QStringList pieces = uri.split( ':' ); QString name = pieces.value( pieces.length() - 1 ); - QgsDebugMsg( QStringLiteral( "Adding GPKG Raster item %1 %2 %3" ).arg( name, uri ) ); + QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, uri ), 3 ); QgsGeoPackageRasterLayerItem *item = new QgsGeoPackageRasterLayerItem( this, name, mPath, uri ); children.append( item ); @@ -361,7 +363,7 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct } else { - // TODO: implemnent raster import + // TODO: implement 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 ); @@ -441,17 +443,64 @@ void QgsGeoPackageConnectionItem::addTable() QList QgsGeoPackageAbstractLayerItem::actions() { QList lst; - // TODO: delete layer when the provider supports it (not currently implemented) + QAction *actionDeleteLayer = new QAction( tr( "Delete layer '%1'..." ).arg( mName ), this ); + connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageAbstractLayerItem::deleteLayer ); + lst.append( actionDeleteLayer ); return lst; } #endif +void QgsGeoPackageAbstractLayerItem::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 = executeDeleteLayer( 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() ) ); + } + +} + QgsGeoPackageAbstractLayerItem::QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, QString name, QString path, QString uri, QgsLayerItem::LayerType layerType, QString providerKey ) : QgsLayerItem( parent, name, path, uri, layerType, providerKey ) { setState( Populated ); // no children are expected } +bool QgsGeoPackageAbstractLayerItem::executeDeleteLayer( QString &errCause ) +{ + errCause = QObject::tr( "The layer %1 cannot be deleted because the this feature is not yet implemented for this kind of layers." ).arg( mName ); + return false; +} + QgsGeoPackageVectorLayerItem::QgsGeoPackageVectorLayerItem( QgsDataItem *parent, QString name, QString path, QString uri, LayerType layerType ) : QgsGeoPackageAbstractLayerItem( parent, name, path, uri, layerType, QStringLiteral( "ogr" ) ) @@ -466,54 +515,67 @@ QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent, } - -#ifdef HAVE_GUI -QList QgsGeoPackageVectorLayerItem::actions() +bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) { - 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() ) + bool result = false; + // Better safe than sorry + if ( ! mUri.isEmpty( ) ) { - if ( layer->publicSource() == mUri ) + QStringList pieces( mUri.split( ':' ) ); + if ( pieces.size() != 3 ) { - 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 ); + errCause = QStringLiteral( "Layer URI is malformed: layer %1 cannot be deleted!" ).arg( mName ); } else { - QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer deleted successfully." ) ); - if ( mParent ) - mParent->refresh(); + QString baseUri = pieces.at( 1 ); + QString layerName = pieces.at( 2 ); + sqlite3 *handle; + int status = sqlite3_open_v2( baseUri.toLocal8Bit().data(), &handle, SQLITE_OPEN_READWRITE, NULL ); + if ( status != SQLITE_OK ) + { + errCause = sqlite3_errmsg( handle ); + } + else + { + // Remove table + QString sql; + char *errmsg = NULL; + sql = QStringLiteral( "DROP table %1;" + "DELETE FROM gpkg_contents WHERE table_name = '%1';" + "DELETE FROM gpkg_tile_matrix WHERE table_name = '%1';" + "DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%1';" ).arg( layerName ); + status = sqlite3_exec( + handle, /* An open database */ + sql.toLocal8Bit().data(), /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + &errmsg /* Error msg written here */ + ); + if ( status == SQLITE_OK ) + { + result = true; + } + else + { + errCause = tr( "There was an error deleting the layer: %1" ).arg( errmsg ); + sqlite3_free( errmsg ); + } + } + sqlite3_close_v2( handle ); } } 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() ) ); + // This should never happen! + errCause = QStringLiteral( "Layer URI is empty: layer %1 cannot be deleted!" ).arg( mName ); } + return result; +} + + +bool QgsGeoPackageVectorLayerItem::executeDeleteLayer( QString &errCause ) +{ + return ::deleteLayer( mUri, errCause ); } -#endif diff --git a/src/providers/ogr/qgsgeopackagedataitems.h b/src/providers/ogr/qgsgeopackagedataitems.h index d736cb4dded..85308d02bab 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.h +++ b/src/providers/ogr/qgsgeopackagedataitems.h @@ -29,8 +29,14 @@ class QgsGeoPackageAbstractLayerItem : public QgsLayerItem protected: QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, QString name, QString path, QString uri, LayerType layerType, QString providerKey ); + /** Subclasses need to implement this function with + * the real deletion implementation + */ + virtual bool executeDeleteLayer( QString &errCause ); #ifdef HAVE_GUI - QList actions() override; + QList actions(); + public slots: + virtual void deleteLayer(); #endif }; @@ -38,28 +44,29 @@ class QgsGeoPackageAbstractLayerItem : public QgsLayerItem class QgsGeoPackageRasterLayerItem : public QgsGeoPackageAbstractLayerItem { Q_OBJECT + public: QgsGeoPackageRasterLayerItem( QgsDataItem *parent, QString name, QString path, QString uri ); - + protected: + virtual bool executeDeleteLayer( QString &errCause ) override; }; 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 + protected: + virtual bool executeDeleteLayer( QString &errCause ) override; }; class QgsGeoPackageConnectionItem : public QgsDataCollectionItem { Q_OBJECT + public: QgsGeoPackageConnectionItem( QgsDataItem *parent, QString name, QString path ); @@ -67,11 +74,10 @@ class QgsGeoPackageConnectionItem : public QgsDataCollectionItem virtual bool equal( const QgsDataItem *other ) override; #ifdef HAVE_GUI - virtual QList actions() override; -#endif - virtual bool acceptDrop() override { return true; } virtual bool handleDrop( const QMimeData *data, Qt::DropAction action ) override; + QList actions(); +#endif //! Return the layer type from \a geometryType static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType ); @@ -91,6 +97,7 @@ class QgsGeoPackageConnectionItem : public QgsDataCollectionItem class QgsGeoPackageRootItem : public QgsDataCollectionItem { Q_OBJECT + public: QgsGeoPackageRootItem( QgsDataItem *parent, QString name, QString path ); ~QgsGeoPackageRootItem(); @@ -98,12 +105,10 @@ class QgsGeoPackageRootItem : public QgsDataCollectionItem QVector createChildren() override; #ifdef HAVE_GUI - virtual QList actions() override; virtual QWidget *paramWidget() override; -#endif + QList actions(); public slots: -#ifdef HAVE_GUI void newConnection(); void connectionsChanged(); void createDatabase(); @@ -120,9 +125,7 @@ class QgsGeoPackageDataItemProvider : public QgsDataItemProvider { public: virtual QString name() override { return QStringLiteral( "GPKG" ); } - virtual int capabilities() override { return QgsDataProvider::Database; } - virtual QgsDataItem *createDataItem( const QString &path, QgsDataItem *parentItem ) override; }; From b46c9c3424e41d17c1640a35d03413d88f6b5473 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 17 Aug 2017 19:06:34 +0200 Subject: [PATCH 019/364] Geopackge bugfix: also show single raster layers The previous implementation only showed subdatasets while single raster were skipped. Now the single rasters are also showed and the name is retrieved from GDAL metadata IDENTIFIER. The gpkg table name is used to delete the table. --- src/providers/ogr/qgsgeopackagedataitems.cpp | 54 +++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index fc8ae3dddcb..733aa26a9ae 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -26,6 +26,7 @@ #include "qgsnewgeopackagelayerdialog.h" #include "qgsmessageoutput.h" #include "qgsvectorlayerexporter.h" +#include "gdal.h" #include #include @@ -231,14 +232,51 @@ QVector QgsGeoPackageConnectionItem::createChildren() } // Raster layers QgsRasterLayer rlayer( mPath, QStringLiteral( "gdal_tmp" ), QStringLiteral( "gdal" ), false ); - Q_FOREACH ( const QString &uri, rlayer.dataProvider()->subLayers( ) ) + if ( rlayer.dataProvider()->subLayers( ).size() > 0 ) { - QStringList pieces = uri.split( ':' ); - QString name = pieces.value( pieces.length() - 1 ); - QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, uri ), 3 ); - QgsGeoPackageRasterLayerItem *item = new QgsGeoPackageRasterLayerItem( this, name, mPath, uri ); - children.append( item ); + Q_FOREACH ( const QString &uri, rlayer.dataProvider()->subLayers( ) ) + { + QStringList pieces = uri.split( ':' ); + QString name = pieces.value( pieces.length() - 1 ); + QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, uri ), 3 ); + QgsGeoPackageRasterLayerItem *item = new QgsGeoPackageRasterLayerItem( this, name, mPath, uri ); + children.append( item ); + } + } + else if ( rlayer.isValid( ) ) + { + // Get the identifier + GDALAllRegister(); + // do not print errors, but write to debug + CPLPushErrorHandler( CPLQuietErrorHandler ); + CPLErrorReset(); + GDALDatasetH hDS = GDALOpen( mPath.toUtf8().constData(), GA_ReadOnly ); + CPLPopErrorHandler(); + if ( ! hDS ) + { + QgsDebugMsg( QString( "GDALOpen error # %1 : %2 " ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) ); + + } + else + { + QString uri( QStringLiteral( "GPKG:%1" ).arg( mPath ) ); + QString name = GDALGetMetadataItem( hDS, "IDENTIFIER", NULL ); + GDALClose( hDS ); + // Fallback: will not be able to delete the table + if ( name.isEmpty() ) + { + name = QFileInfo( mPath ).fileName(); + } + else + { + uri += QStringLiteral( ":%1" ).arg( name ); + } + + QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, mPath ), 3 ); + QgsGeoPackageRasterLayerItem *item = new QgsGeoPackageRasterLayerItem( this, name, mPath, uri ); + children.append( item ); + } } return children; @@ -531,7 +569,7 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) QString baseUri = pieces.at( 1 ); QString layerName = pieces.at( 2 ); sqlite3 *handle; - int status = sqlite3_open_v2( baseUri.toLocal8Bit().data(), &handle, SQLITE_OPEN_READWRITE, NULL ); + int status = sqlite3_open_v2( baseUri.toUtf8().constData(), &handle, SQLITE_OPEN_READWRITE, NULL ); if ( status != SQLITE_OK ) { errCause = sqlite3_errmsg( handle ); @@ -547,7 +585,7 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) "DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%1';" ).arg( layerName ); status = sqlite3_exec( handle, /* An open database */ - sql.toLocal8Bit().data(), /* SQL to be evaluated */ + sql.toUtf8().constData(), /* SQL to be evaluated */ NULL, /* Callback function */ NULL, /* 1st argument to callback */ &errmsg /* Error msg written here */ From bcd495c693b5c3126c2a382a445007f9545b02b2 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 17 Aug 2017 20:31:44 +0200 Subject: [PATCH 020/364] Prevent SQL injection by using sqlite3_mprintf --- src/providers/ogr/qgsgeopackagedataitems.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 733aa26a9ae..c92fa283016 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -514,7 +514,7 @@ void QgsGeoPackageAbstractLayerItem::deleteLayer() } else { - QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer deleted successfully." ) ); + QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer %1 deleted successfully." ).arg( mName ) ); if ( mParent ) mParent->refresh(); } @@ -577,19 +577,24 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) else { // Remove table - QString sql; char *errmsg = NULL; - sql = QStringLiteral( "DROP table %1;" - "DELETE FROM gpkg_contents WHERE table_name = '%1';" - "DELETE FROM gpkg_tile_matrix WHERE table_name = '%1';" - "DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%1';" ).arg( layerName ); + char *sql = sqlite3_mprintf( + "DROP table %w;" + "DELETE FROM gpkg_contents WHERE table_name = '%q';" + "DELETE FROM gpkg_tile_matrix WHERE table_name = '%q';" + "DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%q';", + layerName.toUtf8().constData(), + layerName.toUtf8().constData(), + layerName.toUtf8().constData(), + layerName.toUtf8().constData() ); status = sqlite3_exec( handle, /* An open database */ - sql.toUtf8().constData(), /* SQL to be evaluated */ + sql, /* SQL to be evaluated */ NULL, /* Callback function */ NULL, /* 1st argument to callback */ &errmsg /* Error msg written here */ ); + sqlite3_free( sql ); if ( status == SQLITE_OK ) { result = true; From 0eb302d5e873d21e778630abe93e028e0d960fa6 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 17 Aug 2017 20:42:34 +0200 Subject: [PATCH 021/364] Geopackage try to remove optional entries + fix build warnings --- src/providers/ogr/qgsgeopackagedataitems.cpp | 18 ++++++++++++++++++ src/providers/ogr/qgsgeopackagedataitems.h | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index c92fa283016..3862765b5ba 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -595,6 +595,24 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) &errmsg /* Error msg written here */ ); sqlite3_free( sql ); + // Remove from optional tables, may silently fail + for ( const auto tableName : QStringList() + << QStringLiteral( "gpkg_extensions" ) + << QStringLiteral( "gpkg_metadata" ) + << QStringLiteral( "gpkg_metadata_reference" ) ) + { + char *sql = sqlite3_mprintf( "DELETE FROM table %w WHERE table_name = '%q", + tableName.toUtf8().constData(), + layerName.toUtf8().constData() ); + status = sqlite3_exec( + handle, /* An open database */ + sql, /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + NULL /* Error msg written here */ + ); + sqlite3_free( sql ); + } if ( status == SQLITE_OK ) { result = true; diff --git a/src/providers/ogr/qgsgeopackagedataitems.h b/src/providers/ogr/qgsgeopackagedataitems.h index 85308d02bab..5b2f41fb846 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.h +++ b/src/providers/ogr/qgsgeopackagedataitems.h @@ -76,7 +76,7 @@ class QgsGeoPackageConnectionItem : public QgsDataCollectionItem #ifdef HAVE_GUI virtual bool acceptDrop() override { return true; } virtual bool handleDrop( const QMimeData *data, Qt::DropAction action ) override; - QList actions(); + QList actions() override; #endif //! Return the layer type from \a geometryType @@ -106,7 +106,7 @@ class QgsGeoPackageRootItem : public QgsDataCollectionItem #ifdef HAVE_GUI virtual QWidget *paramWidget() override; - QList actions(); + QList actions() override; public slots: void newConnection(); From b36ad06bc54af90f58e48890171564326221d65f Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 17 Aug 2017 22:48:57 +0200 Subject: [PATCH 022/364] Use sqlite3_close instead of v2 --- src/providers/ogr/qgsgeopackagedataitems.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 3862765b5ba..8e5b08d64d2 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -623,7 +623,7 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) sqlite3_free( errmsg ); } } - sqlite3_close_v2( handle ); + sqlite3_close( handle ); } } else From dc6d1c135e018a797ce3fcde079f049acd6026dc Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 17 Aug 2017 23:26:30 +0200 Subject: [PATCH 023/364] Removed metadata table from the list of tables to be cleared --- src/providers/ogr/qgsgeopackagedataitems.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 8e5b08d64d2..6d6945183e3 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -598,7 +598,6 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) // Remove from optional tables, may silently fail for ( const auto tableName : QStringList() << QStringLiteral( "gpkg_extensions" ) - << QStringLiteral( "gpkg_metadata" ) << QStringLiteral( "gpkg_metadata_reference" ) ) { char *sql = sqlite3_mprintf( "DELETE FROM table %w WHERE table_name = '%q", From c4011ad337352da5af9d11d86402367fb2003685 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 18 Aug 2017 15:22:13 +1000 Subject: [PATCH 024/364] Port oriented minimum bounds to new API --- .../algs/qgis/OrientedMinimumBoundingBox.py | 78 ++++++++++--------- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- .../tests/testdata/qgis_algorithm_tests.yaml | 30 +++---- 3 files changed, 59 insertions(+), 54 deletions(-) diff --git a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py b/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py index 20b6e1fd636..aa767cc54e3 100644 --- a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py +++ b/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py @@ -33,19 +33,17 @@ from qgis.core import (QgsField, QgsFeature, QgsWkbTypes, QgsFeatureRequest, - QgsApplication, - QgsProcessingUtils) + QgsProcessing, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterBoolean, + QgsProcessingParameterFeatureSink, + QgsProcessingException) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterBoolean -from processing.core.outputs import OutputVector -from processing.tools import dataobjects class OrientedMinimumBoundingBox(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' BY_FEATURE = 'BY_FEATURE' OUTPUT = 'OUTPUT' @@ -57,12 +55,12 @@ class OrientedMinimumBoundingBox(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'))) - self.addParameter(ParameterBoolean(self.BY_FEATURE, - self.tr('Calculate OMBB for each feature separately'), True)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), [QgsProcessing.TypeVectorAny])) + self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE, + self.tr('Calculate bounds for each feature separately'), defaultValue=True)) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Oriented_MBBox'), datatype=[dataobjects.TYPE_VECTOR_POLYGON])) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding boxes'), QgsProcessing.TypeVectorPolygon)) def name(self): return 'orientedminimumboundingbox' @@ -71,14 +69,14 @@ class OrientedMinimumBoundingBox(QgisAlgorithm): return self.tr('Oriented minimum bounding box') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) - byFeature = self.getParameterValue(self.BY_FEATURE) + source = self.parameterAsSource(parameters, self.INPUT, context) + by_feature = self.parameterAsBool(parameters, self.BY_FEATURE, context) - if byFeature and layer.geometryType() == QgsWkbTypes.PointGeometry and layer.featureCount() <= 2: - raise GeoAlgorithmExecutionException(self.tr("Can't calculate an OMBB for each point, it's a point. The number of points must be greater than 2")) + if not by_feature and QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PointGeometry and source.featureCount() <= 2: + raise QgsProcessingException(self.tr("Can't calculate an OMBB for each point, it's a point. The number of points must be greater than 2")) - if byFeature: - fields = layer.fields() + if by_feature: + fields = source.fields() else: fields = QgsFields() fields.append(QgsField('area', QVariant.Double)) @@ -87,29 +85,32 @@ class OrientedMinimumBoundingBox(QgisAlgorithm): fields.append(QgsField('width', QVariant.Double)) fields.append(QgsField('height', QVariant.Double)) - writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, QgsWkbTypes.Polygon, source.sourceCrs()) - if byFeature: - self.featureOmbb(layer, context, writer, feedback) + if by_feature: + self.featureOmbb(source, context, sink, feedback) else: - self.layerOmmb(layer, context, writer, feedback) + self.layerOmmb(source, context, sink, feedback) - del writer + return {self.OUTPUT: dest_id} - def layerOmmb(self, layer, context, writer, feedback): + def layerOmmb(self, source, context, sink, feedback): req = QgsFeatureRequest().setSubsetOfAttributes([]) - features = QgsProcessingUtils.getFeatures(layer, context, req) - total = 100.0 / layer.featureCount() if layer.featureCount() else 0 + features = source.getFeatures(req) + total = 100.0 / source.featureCount() if source.featureCount() else 0 newgeometry = QgsGeometry() first = True + geometries = [] for current, inFeat in enumerate(features): - if first: - newgeometry = inFeat.geometry() - first = False - else: - newgeometry = newgeometry.combine(inFeat.geometry()) + if feedback.isCanceled(): + break + + if inFeat.hasGeometry(): + geometries.append(inFeat.geometry()) feedback.setProgress(int(current * total)) + newgeometry = QgsGeometry.unaryUnion(geometries) geometry, area, angle, width, height = newgeometry.orientedMinimumBoundingBox() if geometry: @@ -121,13 +122,16 @@ class OrientedMinimumBoundingBox(QgisAlgorithm): angle, width, height]) - writer.addFeature(outFeat, QgsFeatureSink.FastInsert) + sink.addFeature(outFeat, QgsFeatureSink.FastInsert) - def featureOmbb(self, layer, context, writer, feedback): - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / layer.featureCount() if layer.featureCount() else 0 + def featureOmbb(self, source, context, sink, feedback): + features = source.getFeatures() + total = 100.0 / source.featureCount() if source.featureCount() else 0 outFeat = QgsFeature() for current, inFeat in enumerate(features): + if feedback.isCanceled(): + break + geometry, area, angle, width, height = inFeat.geometry().orientedMinimumBoundingBox() if geometry: outFeat.setGeometry(geometry) @@ -138,7 +142,7 @@ class OrientedMinimumBoundingBox(QgisAlgorithm): width, height]) outFeat.setAttributes(attrs) - writer.addFeature(outFeat, QgsFeatureSink.FastInsert) + sink.addFeature(outFeat, QgsFeatureSink.FastInsert) else: feedback.pushInfo(self.tr("Can't calculate an OMBB for feature {0}.").format(inFeat.id())) feedback.setProgress(int(current * total)) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 84b0ec3039f..9895697441b 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -92,6 +92,7 @@ from .Merge import Merge from .MergeLines import MergeLines from .NearestNeighbourAnalysis import NearestNeighbourAnalysis from .OffsetLine import OffsetLine +from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox from .Orthogonalize import Orthogonalize from .PointDistance import PointDistance from .PointOnSurface import PointOnSurface @@ -163,7 +164,6 @@ from .ZonalStatistics import ZonalStatistics # from .SelectByAttributeSum import SelectByAttributeSum # from .HypsometricCurves import HypsometricCurves # from .Datasources2Vrt import Datasources2Vrt -# from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox # from .DefineProjection import DefineProjection # from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable # from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed @@ -195,7 +195,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SetVectorStyle(), SetRasterStyle(), # HypsometricCurves(), # FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(), - # OrientedMinimumBoundingBox(), + # # DefineProjection(), # RectanglesOvalsDiamondsVariable(), # RectanglesOvalsDiamondsFixed(), @@ -256,6 +256,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): MergeLines(), NearestNeighbourAnalysis(), OffsetLine(), + OrientedMinimumBoundingBox(), Orthogonalize(), PointDistance(), PointOnSurface(), diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index ca90ca0ca17..19b7de3e92b 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2235,21 +2235,21 @@ tests: # OUTPUT: # hash: fe6e018be13c5a3c17f3f4d0f0dc7686c628cb440b74c4642aa0c939 # type: rasterhash -# -# - algorithm: qgis:orientedminimumboundingbox -# name: Oriented minimum bounding box polys -# params: -# BY_FEATURE: true -# INPUT_LAYER: -# name: custom/oriented_bbox.gml -# type: vector -# results: -# OUTPUT: -# name: expected/oriented_bounds.gml -# type: vector -# compare: -# geometry: -# precision: 7 + + - algorithm: qgis:orientedminimumboundingbox + name: Oriented minimum bounding box polys + params: + BY_FEATURE: true + INPUT: + name: custom/oriented_bbox.gml + type: vector + results: + OUTPUT: + name: expected/oriented_bounds.gml + type: vector + compare: + geometry: + precision: 7 - algorithm: qgis:orthogonalize name: Orthogonalize polys From be46b756ea5b9334021dc84e7adee9b3329805b9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 18 Aug 2017 15:35:27 +1000 Subject: [PATCH 025/364] Port hypsometic curves to new API --- .../processing/algs/qgis/HypsometricCurves.py | 81 +++++++++++-------- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/python/plugins/processing/algs/qgis/HypsometricCurves.py b/python/plugins/processing/algs/qgis/HypsometricCurves.py index bb916842d8c..6678a764b58 100644 --- a/python/plugins/processing/algs/qgis/HypsometricCurves.py +++ b/python/plugins/processing/algs/qgis/HypsometricCurves.py @@ -27,24 +27,24 @@ __copyright__ = '(C) 2014, Alexander Bruy' __revision__ = '$Format:%H$' import os - import numpy +import csv + from osgeo import gdal, ogr, osr from qgis.core import (QgsRectangle, - QgsFeatureSink, QgsGeometry, - QgsApplication, - QgsProcessingUtils) + QgsFeatureRequest, + QgsProcessing, + QgsProcessingParameterBoolean, + QgsProcessingParameterNumber, + QgsProcessingParameterRasterLayer, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterFolderDestination) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterRaster -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterNumber -from processing.core.parameters import ParameterBoolean -from processing.core.outputs import OutputDirectory - -from processing.tools import raster, vector, dataobjects +from processing.tools import raster +from processing.tools.dataobjects import exportRasterLayer class HypsometricCurves(QgisAlgorithm): @@ -62,17 +62,17 @@ class HypsometricCurves(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterRaster(self.INPUT_DEM, - self.tr('DEM to analyze'))) - self.addParameter(ParameterVector(self.BOUNDARY_LAYER, - self.tr('Boundary layer'), dataobjects.TYPE_VECTOR_POLYGON)) - self.addParameter(ParameterNumber(self.STEP, - self.tr('Step'), 0.0, 999999999.999999, 100.0)) - self.addParameter(ParameterBoolean(self.USE_PERCENTAGE, - self.tr('Use % of area instead of absolute value'), False)) + self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT_DEM, + self.tr('DEM to analyze'))) + self.addParameter(QgsProcessingParameterFeatureSource(self.BOUNDARY_LAYER, + self.tr('Boundary layer'), [QgsProcessing.TypeVectorPolygon])) + self.addParameter(QgsProcessingParameterNumber(self.STEP, + self.tr('Step'), minValue=0.0, maxValue=999999999.999999, defaultValue=100.0)) + self.addParameter(QgsProcessingParameterBoolean(self.USE_PERCENTAGE, + self.tr('Use % of area instead of absolute value'), defaultValue=False)) - self.addOutput(OutputDirectory(self.OUTPUT_DIRECTORY, - self.tr('Hypsometric curves'))) + self.addParameter(QgsProcessingParameterFolderDestination(self.OUTPUT_DIRECTORY, + self.tr('Hypsometric curves'))) def name(self): return 'hypsometriccurves' @@ -81,12 +81,15 @@ class HypsometricCurves(QgisAlgorithm): return self.tr('Hypsometric curves') def processAlgorithm(self, parameters, context, feedback): - rasterPath = self.getParameterValue(self.INPUT_DEM) - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.BOUNDARY_LAYER), context) - step = self.getParameterValue(self.STEP) - percentage = self.getParameterValue(self.USE_PERCENTAGE) + raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_DEM, context) + target_crs = raster_layer.crs() + rasterPath = exportRasterLayer(raster_layer) - outputPath = self.getOutputValue(self.OUTPUT_DIRECTORY) + source = self.parameterAsSource(parameters, self.BOUNDARY_LAYER, context) + step = self.parameterAsDouble(parameters, self.STEP, context) + percentage = self.parameterAsBool(parameters, self.USE_PERCENTAGE, context) + + outputPath = self.parameterAsString(parameters, self.OUTPUT_DIRECTORY, context) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() @@ -105,15 +108,21 @@ class HypsometricCurves(QgisAlgorithm): rasterGeom = QgsGeometry.fromRect(rasterBBox) crs = osr.SpatialReference() - crs.ImportFromProj4(str(layer.crs().toProj4())) + crs.ImportFromProj4(str(target_crs.toProj4())) memVectorDriver = ogr.GetDriverByName('Memory') memRasterDriver = gdal.GetDriverByName('MEM') - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / layer.featureCount() if layer.featureCount() else 0 + features = source.getFeatures(QgsFeatureRequest().setDestinationCrs(target_crs)) + total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): + if not f.hasGeometry(): + continue + + if feedback.isCanceled(): + break + geom = f.geometry() intersectedGeom = rasterGeom.intersection(geom) @@ -124,7 +133,7 @@ class HypsometricCurves(QgisAlgorithm): continue fName = os.path.join( - outputPath, 'hystogram_%s_%s.csv' % (layer.name(), f.id())) + outputPath, 'hystogram_%s_%s.csv' % (source.sourceName(), f.id())) ogrGeom = ogr.CreateGeometryFromWkt(intersectedGeom.exportToWkt()) bbox = intersectedGeom.boundingBox() @@ -185,6 +194,8 @@ class HypsometricCurves(QgisAlgorithm): rasterDS = None + return {self.OUTPUT_DIRECTORY: outputPath} + def calculateHypsometry(self, fid, fName, feedback, data, pX, pY, percentage, step): out = dict() @@ -220,7 +231,9 @@ class HypsometricCurves(QgisAlgorithm): out[i[0]] = i[1] + out[prev] prev = i[0] - writer = vector.TableWriter(fName, 'utf-8', [self.tr('Area'), self.tr('Elevation')]) - for i in sorted(out.items()): - writer.addRecord([i[1], i[0]]) - del writer + with open(fName, 'w', newline='', encoding='utf-8') as out_file: + writer = csv.writer(out_file) + writer.writerow([self.tr('Area'), self.tr('Elevation')]) + + for i in sorted(out.items()): + writer.writerow([i[1], i[0]]) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 9895697441b..b4dde7a7ca2 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -81,6 +81,7 @@ from .Hillshade import Hillshade from .HubDistanceLines import HubDistanceLines from .HubDistancePoints import HubDistancePoints from .HubLines import HubLines +from .HypsometricCurves import HypsometricCurves from .ImportIntoPostGIS import ImportIntoPostGIS from .ImportIntoSpatialite import ImportIntoSpatialite from .Intersection import Intersection @@ -162,7 +163,6 @@ from .ZonalStatistics import ZonalStatistics # from .SetVectorStyle import SetVectorStyle # from .SetRasterStyle import SetRasterStyle # from .SelectByAttributeSum import SelectByAttributeSum -# from .HypsometricCurves import HypsometricCurves # from .Datasources2Vrt import Datasources2Vrt # from .DefineProjection import DefineProjection # from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable @@ -193,9 +193,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # GeometryConvert(), FieldsCalculator(), # FieldsPyculator(), # SetVectorStyle(), SetRasterStyle(), - # HypsometricCurves(), # FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(), - # # DefineProjection(), # RectanglesOvalsDiamondsVariable(), # RectanglesOvalsDiamondsFixed(), @@ -245,6 +243,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): HubDistanceLines(), HubDistancePoints(), HubLines(), + HypsometricCurves(), ImportIntoPostGIS(), ImportIntoSpatialite(), Intersection(), From cea7bd39d8a3575bda85acdd15bae621e2547cd1 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 18 Aug 2017 07:38:46 +0100 Subject: [PATCH 026/364] Fixes relation reference widget by refreshing filter lists. Fixes #16400 --- .../qgsrelationreferencewidget.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/gui/editorwidgets/qgsrelationreferencewidget.cpp b/src/gui/editorwidgets/qgsrelationreferencewidget.cpp index 0c84329d4c3..447200b5e85 100644 --- a/src/gui/editorwidgets/qgsrelationreferencewidget.cpp +++ b/src/gui/editorwidgets/qgsrelationreferencewidget.cpp @@ -280,6 +280,20 @@ void QgsRelationReferenceWidget::setForeignKey( const QVariant &value ) } else { + QVariant nullValue = QgsApplication::nullRepresentation(); + + if ( mChainFilters && mFeature.isValid() && mFilterComboBoxes.count() >= mFilterFields.count() ) + { + QgsFeature feature = mFeature; + + for ( int i = 0; i < mFilterFields.size(); i++ ) + { + QVariant v = feature.attribute( mFilterFields[i] ); + QString f = v.isNull() ? nullValue.toString() : v.toString(); + mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) ); + } + } + int i = mComboBox->findData( mFeature.id(), QgsAttributeTableModel::FeatureIdRole ); if ( i == -1 && mAllowNull ) { @@ -971,12 +985,12 @@ void QgsRelationReferenceWidget::disableChainedComboBoxes( const QComboBox *scb continue; } + cb->setCurrentIndex( 0 ); if ( ccb->currentIndex() == 0 ) { - cb->setCurrentIndex( 0 ); cb->setEnabled( false ); } - else - ccb = cb; + + ccb = cb; } } From a2914ff66c57f900d8c55c2b0d2cc1bb8ce9f222 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 18 Aug 2017 07:42:39 +0100 Subject: [PATCH 027/364] Add unit tests --- .../gui/testqgsrelationreferencewidget.cpp | 159 +++++++++++------- 1 file changed, 100 insertions(+), 59 deletions(-) diff --git a/tests/src/gui/testqgsrelationreferencewidget.cpp b/tests/src/gui/testqgsrelationreferencewidget.cpp index ee7425c2246..6d1b72f1377 100644 --- a/tests/src/gui/testqgsrelationreferencewidget.cpp +++ b/tests/src/gui/testqgsrelationreferencewidget.cpp @@ -39,6 +39,12 @@ class TestQgsRelationReferenceWidget : public QObject void cleanup(); // will be called after every testfunction. void testChainFilter(); + void testChainFilterRefreshed(); + + private: + std::unique_ptr mLayer1; + std::unique_ptr mLayer2; + std::unique_ptr mRelation; }; void TestQgsRelationReferenceWidget::initTestCase() @@ -55,6 +61,64 @@ void TestQgsRelationReferenceWidget::cleanupTestCase() void TestQgsRelationReferenceWidget::init() { + // create layer + mLayer1.reset( new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:3111&field=pk:int&field=fk:int" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) ) ); + QgsProject::instance()->addMapLayer( mLayer1.get(), false, false ); + + mLayer2.reset( new QgsVectorLayer( QStringLiteral( "LineString?field=pk:int&field=material:string&field=diameter:int&field=raccord:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) ) ); + QgsProject::instance()->addMapLayer( mLayer2.get(), false, false ); + + // create relation + mRelation.reset( new QgsRelation() ); + mRelation->setId( QStringLiteral( "vl1.vl2" ) ); + mRelation->setName( QStringLiteral( "vl1.vl2" ) ); + mRelation->setReferencingLayer( mLayer1->id() ); + mRelation->setReferencedLayer( mLayer2->id() ); + mRelation->addFieldPair( "fk", "pk" ); + QVERIFY( mRelation->isValid() ); + QgsProject::instance()->relationManager()->addRelation( *mRelation.get() ); + + // add features + QgsFeature ft0( mLayer1->fields() ); + ft0.setAttribute( QStringLiteral( "pk" ), 0 ); + ft0.setAttribute( QStringLiteral( "fk" ), 0 ); + mLayer1->startEditing(); + mLayer1->addFeature( ft0 ); + mLayer1->commitChanges(); + + QgsFeature ft1( mLayer1->fields() ); + ft1.setAttribute( QStringLiteral( "pk" ), 1 ); + ft1.setAttribute( QStringLiteral( "fk" ), 1 ); + mLayer1->startEditing(); + mLayer1->addFeature( ft1 ); + mLayer1->commitChanges(); + + QgsFeature ft2( mLayer2->fields() ); + ft2.setAttribute( QStringLiteral( "pk" ), 10 ); + ft2.setAttribute( QStringLiteral( "material" ), "iron" ); + ft2.setAttribute( QStringLiteral( "diameter" ), 120 ); + ft2.setAttribute( QStringLiteral( "raccord" ), "brides" ); + mLayer2->startEditing(); + mLayer2->addFeature( ft2 ); + mLayer2->commitChanges(); + + QgsFeature ft3( mLayer2->fields() ); + ft3.setAttribute( QStringLiteral( "pk" ), 11 ); + ft3.setAttribute( QStringLiteral( "material" ), "iron" ); + ft3.setAttribute( QStringLiteral( "diameter" ), 120 ); + ft3.setAttribute( QStringLiteral( "raccord" ), "sleeve" ); + mLayer2->startEditing(); + mLayer2->addFeature( ft3 ); + mLayer2->commitChanges(); + + QgsFeature ft4( mLayer2->fields() ); + ft4.setAttribute( QStringLiteral( "pk" ), 12 ); + ft4.setAttribute( QStringLiteral( "material" ), "steel" ); + ft4.setAttribute( QStringLiteral( "diameter" ), 120 ); + ft4.setAttribute( QStringLiteral( "raccord" ), "collar" ); + mLayer2->startEditing(); + mLayer2->addFeature( ft4 ); + mLayer2->commitChanges(); } void TestQgsRelationReferenceWidget::cleanup() @@ -63,71 +127,13 @@ void TestQgsRelationReferenceWidget::cleanup() void TestQgsRelationReferenceWidget::testChainFilter() { - // create layers - QgsVectorLayer vl1( QStringLiteral( "LineString?crs=epsg:3111&field=pk:int&field=fk:int" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) ); - QgsVectorLayer vl2( QStringLiteral( "LineString?field=pk:int&field=material:string&field=diameter:int&field=raccord:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) ); - QgsProject::instance()->addMapLayer( &vl1, false, false ); - QgsProject::instance()->addMapLayer( &vl2, false, false ); - - // create a relation between them - QgsRelation relation; - relation.setId( QStringLiteral( "vl1.vl2" ) ); - relation.setName( QStringLiteral( "vl1.vl2" ) ); - relation.setReferencingLayer( vl1.id() ); - relation.setReferencedLayer( vl2.id() ); - relation.addFieldPair( "fk", "pk" ); - QVERIFY( relation.isValid() ); - QgsProject::instance()->relationManager()->addRelation( relation ); - - // add features - QgsFeature ft0( vl1.fields() ); - ft0.setAttribute( QStringLiteral( "pk" ), 0 ); - ft0.setAttribute( QStringLiteral( "fk" ), 0 ); - vl1.startEditing(); - vl1.addFeature( ft0 ); - vl1.commitChanges(); - - QgsFeature ft1( vl1.fields() ); - ft1.setAttribute( QStringLiteral( "pk" ), 1 ); - ft1.setAttribute( QStringLiteral( "fk" ), 1 ); - vl1.startEditing(); - vl1.addFeature( ft1 ); - vl1.commitChanges(); - - QgsFeature ft2( vl2.fields() ); - ft2.setAttribute( QStringLiteral( "pk" ), 10 ); - ft2.setAttribute( QStringLiteral( "material" ), "iron" ); - ft2.setAttribute( QStringLiteral( "diameter" ), 120 ); - ft2.setAttribute( QStringLiteral( "raccord" ), "brides" ); - vl2.startEditing(); - vl2.addFeature( ft2 ); - vl2.commitChanges(); - - QgsFeature ft3( vl2.fields() ); - ft3.setAttribute( QStringLiteral( "pk" ), 11 ); - ft3.setAttribute( QStringLiteral( "material" ), "iron" ); - ft3.setAttribute( QStringLiteral( "diameter" ), 120 ); - ft3.setAttribute( QStringLiteral( "raccord" ), "sleeve" ); - vl2.startEditing(); - vl2.addFeature( ft3 ); - vl2.commitChanges(); - - QgsFeature ft4( vl2.fields() ); - ft4.setAttribute( QStringLiteral( "pk" ), 12 ); - ft4.setAttribute( QStringLiteral( "material" ), "steel" ); - ft4.setAttribute( QStringLiteral( "diameter" ), 120 ); - ft4.setAttribute( QStringLiteral( "raccord" ), "collar" ); - vl2.startEditing(); - vl2.addFeature( ft4 ); - vl2.commitChanges(); - // init a relation reference widget QStringList filterFields = { "material", "diameter", "raccord" }; QgsRelationReferenceWidget w( new QWidget() ); w.setChainFilters( true ); w.setFilterFields( filterFields ); - w.setRelation( relation, true ); + w.setRelation( *mRelation, true ); w.init(); // check default status for comboboxes @@ -181,5 +187,40 @@ void TestQgsRelationReferenceWidget::testChainFilter() QCOMPARE( w.mComboBox->count(), 4 ); } +void TestQgsRelationReferenceWidget::testChainFilterRefreshed() +{ + // init a relation reference widget + QStringList filterFields = { "material", "diameter", "raccord" }; + + QgsRelationReferenceWidget w( new QWidget() ); + w.setChainFilters( true ); + w.setFilterFields( filterFields ); + w.setRelation( *mRelation, true ); + w.init(); + + // check default status for comboboxes + QList cbs = w.mFilterComboBoxes; + QCOMPARE( cbs.count(), 3 ); + QCOMPARE( cbs[0]->currentText(), QString( "material" ) ); + QCOMPARE( cbs[1]->currentText(), QString( "diameter" ) ); + QCOMPARE( cbs[2]->currentText(), QString( "raccord" ) ); + + // update foreign key + w.setForeignKey( QVariant( 12 ) ); + QCOMPARE( cbs[0]->currentText(), QString( "steel" ) ); + QCOMPARE( cbs[1]->currentText(), QString( "120" ) ); + QCOMPARE( cbs[2]->currentText(), QString( "collar" ) ); + + w.setForeignKey( QVariant( 10 ) ); + QCOMPARE( cbs[0]->currentText(), QString( "iron" ) ); + QCOMPARE( cbs[1]->currentText(), QString( "120" ) ); + QCOMPARE( cbs[2]->currentText(), QString( "brides" ) ); + + w.setForeignKey( QVariant( 11 ) ); + QCOMPARE( cbs[0]->currentText(), QString( "iron" ) ); + QCOMPARE( cbs[1]->currentText(), QString( "120" ) ); + QCOMPARE( cbs[2]->currentText(), QString( "sleeve" ) ); +} + QGSTEST_MAIN( TestQgsRelationReferenceWidget ) #include "testqgsrelationreferencewidget.moc" From f0d36778659e274d063ee77fba774293cadd0ee9 Mon Sep 17 00:00:00 2001 From: Etienne Trimaille Date: Fri, 18 Aug 2017 09:46:47 +0200 Subject: [PATCH 028/364] fix opening sublayers in a group --- src/gui/qgssublayersdialog.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/gui/qgssublayersdialog.cpp b/src/gui/qgssublayersdialog.cpp index 4263c89bb4d..060dc2e9679 100644 --- a/src/gui/qgssublayersdialog.cpp +++ b/src/gui/qgssublayersdialog.cpp @@ -79,13 +79,9 @@ QgsSublayersDialog::QgsSublayersDialog( ProviderType providerType, const QString restoreGeometry( settings.value( "/Windows/" + mName + "SubLayers/geometry" ).toByteArray() ); // Checkbox about adding sublayers to a group - if ( mShowAddToGroupCheckbox ) - { - mCheckboxAddToGroup = new QCheckBox( tr( "Add layers to a group" ) ); - bool addToGroup = settings.value( QStringLiteral( "/qgis/openSublayersInGroup" ), false ).toBool(); - mCheckboxAddToGroup->setChecked( addToGroup ); - buttonBox->addButton( mCheckboxAddToGroup, QDialogButtonBox::ActionRole ); - } + mCheckboxAddToGroup = new QCheckBox( tr( "Add layers to a group" ), this ); + buttonBox->addButton( mCheckboxAddToGroup, QDialogButtonBox::ActionRole ); + mCheckboxAddToGroup->setVisible( false ); } QgsSublayersDialog::~QgsSublayersDialog() @@ -201,11 +197,20 @@ int QgsSublayersDialog::exec() cursor = QCursor( * QApplication::overrideCursor() ); QApplication::restoreOverrideCursor(); } + + // Checkbox about adding sublayers to a group + if ( mShowAddToGroupCheckbox ) + { + mCheckboxAddToGroup->setVisible( true ); + bool addToGroup = settings.value( QStringLiteral( "/qgis/openSublayersInGroup" ), false ).toBool(); + mCheckboxAddToGroup->setChecked( addToGroup ); + } + int ret = QDialog::exec(); if ( overrideCursor ) QApplication::setOverrideCursor( cursor ); - if ( mCheckboxAddToGroup ) + if ( mShowAddToGroupCheckbox ) settings.setValue( QStringLiteral( "/qgis/openSublayersInGroup" ), mCheckboxAddToGroup->isChecked() ); return ret; -} \ No newline at end of file +} From 79d65cec8868eddd102068b8259f1f7b6799729b Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 18 Aug 2017 14:10:00 +0100 Subject: [PATCH 029/364] Fix OGC test getcapabilities:style-legendurls --- src/server/services/wms/qgswmsparameters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/services/wms/qgswmsparameters.cpp b/src/server/services/wms/qgswmsparameters.cpp index b5512f7031e..aca8974e271 100644 --- a/src/server/services/wms/qgswmsparameters.cpp +++ b/src/server/services/wms/qgswmsparameters.cpp @@ -362,7 +362,7 @@ namespace QgsWms QVariant( "" ), QVariant() }; - save( pLayers ); + save( pStyle ); const Parameter pStyles = { ParameterName::STYLES, QVariant::String, From 465b27a40dcea07b5affd152c465157f01582ac9 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 18 Aug 2017 14:14:06 +0100 Subject: [PATCH 030/364] Add test --- tests/src/python/test_qgsserver_wms.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/src/python/test_qgsserver_wms.py b/tests/src/python/test_qgsserver_wms.py index 75a4eb1e208..34029fb65da 100644 --- a/tests/src/python/test_qgsserver_wms.py +++ b/tests/src/python/test_qgsserver_wms.py @@ -702,7 +702,7 @@ class TestQgsServerWMS(QgsServerTestBase): r, h = self._result(self._execute_request(qs)) self._img_diff_error(r, h, "WMS_GetMap_StyleDefault") - # custom style + # custom style with STYLES parameter qs = "?" + "&".join(["%s=%s" % i for i in list({ "MAP": urllib.parse.quote(self.projectPath), "SERVICE": "WMS", @@ -720,6 +720,24 @@ class TestQgsServerWMS(QgsServerTestBase): r, h = self._result(self._execute_request(qs)) self._img_diff_error(r, h, "WMS_GetMap_StyleCustom") + # custom style with STYLE parameter + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "Country_Labels", + "STYLE": "custom", + "FORMAT": "image/png", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857" + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetMap_StyleCustom") + def test_wms_getmap_filter(self): qs = "?" + "&".join(["%s=%s" % i for i in list({ "MAP": urllib.parse.quote(self.projectPath), From f6600f23aacf53733449c18b6f1a700014aaf4a7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 00:01:58 +1000 Subject: [PATCH 031/364] Port raster relief alg to new API --- .../algs/qgis/QGISAlgorithmProvider.py | 4 +- python/plugins/processing/algs/qgis/Relief.py | 161 ++++++++---------- python/plugins/processing/script/snippets.py | 3 - .../tests/testdata/qgis_algorithm_tests.yaml | 64 +++---- 4 files changed, 111 insertions(+), 121 deletions(-) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index b4dde7a7ca2..21b075c29d0 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -119,6 +119,7 @@ from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets from .Rasterize import RasterizeAlgorithm from .RasterLayerStatistics import RasterLayerStatistics from .RegularPoints import RegularPoints +from .Relief import Relief from .ReverseLineDirection import ReverseLineDirection from .Ruggedness import Ruggedness from .SaveSelectedFeatures import SaveSelectedFeatures @@ -167,7 +168,6 @@ from .ZonalStatistics import ZonalStatistics # from .DefineProjection import DefineProjection # from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable # from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed -# from .Relief import Relief # from .IdwInterpolation import IdwInterpolation # from .TinInterpolation import TinInterpolation # from .RasterCalculator import RasterCalculator @@ -197,7 +197,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # DefineProjection(), # RectanglesOvalsDiamondsVariable(), # RectanglesOvalsDiamondsFixed(), - # Relief(), # IdwInterpolation(), TinInterpolation(), # RasterCalculator(), # ExecuteSQL(), FindProjection(), @@ -281,6 +280,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): RasterizeAlgorithm(), RasterLayerStatistics(), RegularPoints(), + Relief(), ReverseLineDirection(), Ruggedness(), SaveSelectedFeatures(), diff --git a/python/plugins/processing/algs/qgis/Relief.py b/python/plugins/processing/algs/qgis/Relief.py index 7e4518cb9ee..fa38d867b9b 100644 --- a/python/plugins/processing/algs/qgis/Relief.py +++ b/python/plugins/processing/algs/qgis/Relief.py @@ -30,27 +30,66 @@ import os from qgis.PyQt.QtGui import QIcon, QColor from qgis.analysis import QgsRelief -from qgis.core import QgsProcessingParameterDefinition +from qgis.core import (QgsProcessingParameterDefinition, + QgsProcessingParameterRasterLayer, + QgsProcessingParameterNumber, + QgsProcessingParameterBoolean, + QgsProcessingParameterRasterDestination, + QgsProcessingParameterFileDestination, + QgsRasterFileWriter, + QgsProcessingException) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import (Parameter, - ParameterRaster, - ParameterNumber, - ParameterBoolean, - _splitParameterOptions) -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.outputs import OutputRaster, OutputTable -from processing.tools import raster +from processing.tools.dataobjects import exportRasterLayer pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] +class ParameterReliefColors(QgsProcessingParameterDefinition): + + def __init__(self, name='', description='', parent=None, optional=True): + super().__init__(name, description, None, optional) + self.parent = parent + self.setMetadata({'widget_wrapper': 'processing.algs.qgis.ui.ReliefColorsWidget.ReliefColorsWidgetWrapper'}) + + def type(self): + return 'relief_colors' + + def clone(self): + return ParameterReliefColors(self.name(), self.description(), self.parent, + self.flags() & QgsProcessingParameterDefinition.FlagOptional) + + @staticmethod + def valueToColors(value): + if value is None: + return None + + if value == '': + return None + + if isinstance(value, str): + return value.split(';') + else: + return ParameterReliefColors.colorsToString(value) + + @staticmethod + def colorsToString(colors): + s = '' + for c in colors: + s += '{:f}, {:f}, {:d}, {:d}, {:d};'.format(c[0], + c[1], + c[2], + c[3], + c[4]) + return s[:-1] + + class Relief(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' Z_FACTOR = 'Z_FACTOR' AUTO_COLORS = 'AUTO_COLORS' COLORS = 'COLORS' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' FREQUENCY_DISTRIBUTION = 'FREQUENCY_DISTRIBUTION' def icon(self): @@ -63,74 +102,22 @@ class Relief(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - class ParameterReliefColors(Parameter): - default_metadata = { - 'widget_wrapper': 'processing.algs.qgis.ui.ReliefColorsWidget.ReliefColorsWidgetWrapper' - } - - def __init__(self, name='', description='', parent=None, optional=True): - Parameter.__init__(self, name, description, None, optional) - self.parent = parent - - def setValue(self, value): - if value is None: - if not self.flags() & QgsProcessingParameterDefinition.FlagOptional: - return False - self.value = None - return True - - if value == '': - if not self.flags() & QgsProcessingParameterDefinition.FlagOptional: - return False - - if isinstance(value, str): - self.value = value if value != '' else None - else: - self.value = ParameterReliefColors.colorsToString(value) - return True - - def getValueAsCommandLineParameter(self): - return '"{}"'.format(self.value) - - def getAsScriptCode(self): - param_type = '' - param_type += 'relief colors ' - return '##' + self.name + '=' + param_type - - @classmethod - def fromScriptCode(self, line): - isOptional, name, definition = _splitParameterOptions(line) - descName = QgsProcessingParameters.descriptionFromName(name) - parent = definition.lower().strip()[len('relief colors') + 1:] - return ParameterReliefColors(name, descName, parent) - - @staticmethod - def colorsToString(colors): - s = '' - for c in colors: - s += '{:f}, {:f}, {:d}, {:d}, {:d};'.format(c[0], - c[1], - c[2], - c[3], - c[4]) - return s[:-1] - - self.addParameter(ParameterRaster(self.INPUT_LAYER, - self.tr('Elevation layer'))) - self.addParameter(ParameterNumber(self.Z_FACTOR, - self.tr('Z factor'), - 1.0, 999999.99, 1.0)) - self.addParameter(ParameterBoolean(self.AUTO_COLORS, - self.tr('Generate relief classes automatically'), - False)) + self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, + self.tr('Elevation layer'))) + self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, + self.tr('Z factor'), type=QgsProcessingParameterNumber.Double, + minValue=1.0, maxValue=999999.99, defaultValue=1.0)) + self.addParameter(QgsProcessingParameterBoolean(self.AUTO_COLORS, + self.tr('Generate relief classes automatically'), + defaultValue=False)) self.addParameter(ParameterReliefColors(self.COLORS, self.tr('Relief colors'), - self.INPUT_LAYER, + self.INPUT, True)) - self.addOutput(OutputRaster(self.OUTPUT_LAYER, - self.tr('Relief'))) - self.addOutput(OutputTable(self.FREQUENCY_DISTRIBUTION, - self.tr('Frequency distribution'))) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, + self.tr('Relief'))) + self.addParameter(QgsProcessingParameterFileDestination(self.FREQUENCY_DISTRIBUTION, + self.tr('Frequency distribution'), 'CSV files (*.csv)', optional=True)) def name(self): return 'relief' @@ -139,26 +126,26 @@ class Relief(QgisAlgorithm): return self.tr('Relief') def processAlgorithm(self, parameters, context, feedback): - inputFile = self.getParameterValue(self.INPUT_LAYER) - zFactor = self.getParameterValue(self.Z_FACTOR) - automaticColors = self.getParameterValue(self.AUTO_COLORS) - colors = self.getParameterValue(self.COLORS) - outputFile = self.getOutputValue(self.OUTPUT_LAYER) - frequencyDistribution = self.getOutputValue(self.FREQUENCY_DISTRIBUTION) + inputFile = exportRasterLayer(self.parameterAsRasterLayer(parameters, self.INPUT, context)) + zFactor = self.parameterAsDouble(parameters, self.Z_FACTOR, context) + automaticColors = self.parameterAsBool(parameters, self.AUTO_COLORS, context) + outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) + frequencyDistribution = self.parameterAsFileOutput(parameters, self.FREQUENCY_DISTRIBUTION, context) - outputFormat = raster.formatShortNameFromFileName(outputFile) + outputFormat = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1]) relief = QgsRelief(inputFile, outputFile, outputFormat) if automaticColors: reliefColors = relief.calculateOptimizedReliefClasses() else: - if colors is None: - raise GeoAlgorithmExecutionException( + colors = ParameterReliefColors.valueToColors(parameters[self.COLORS]) + if colors is None or len(colors) == 0: + raise QgsProcessingException( self.tr('Specify relief colors or activate "Generate relief classes automatically" option.')) reliefColors = [] - for c in colors.split(';'): + for c in colors: v = c.split(',') color = QgsRelief.ReliefColor(QColor(int(v[2]), int(v[3]), int(v[4])), float(v[0]), @@ -169,3 +156,5 @@ class Relief(QgisAlgorithm): relief.setZFactor(zFactor) relief.exportFrequencyDistributionToCsv(frequencyDistribution) relief.processRaster(None) + + return {self.OUTPUT: outputFile, self.FREQUENCY_DISTRIBUTION: frequencyDistribution} diff --git a/python/plugins/processing/script/snippets.py b/python/plugins/processing/script/snippets.py index 02761116462..a9cf663044f 100644 --- a/python/plugins/processing/script/snippets.py +++ b/python/plugins/processing/script/snippets.py @@ -12,6 +12,3 @@ fields.append(('NEW_FIELD', str)) writer = processing.VectorWriter(output_file, None, fields, processing.geomtype(layer), layer.crs() ) - -##Create a new table -writer = processing.TableWriter(output_file, None, ['field1', 'field2']) diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 19b7de3e92b..4b2bb9c7b52 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -1388,36 +1388,40 @@ tests: - 75cca4c1a870a1e21185a2d85b33b6d9958a69fc6ebb04e4d6ceb8a3 type: rasterhash -# - algorithm: qgis:relief -# name: Relief (automatic colors generation) -# params: -# AUTO_COLORS: true -# INPUT_LAYER: -# name: dem.tif -# type: raster -# Z_FACTOR: 1.0 -# results: -# OUTPUT_LAYER: -# hash: 7fe0e0174185fd743e23760f33615adf10f771b4275f320db6f7f4f8 -# type: rasterhash -# -# - algorithm: qgis:relief -# name: Relief (custom colors) -# params: -# AUTO_COLORS: false -# COLORS: 85.000000, 104.436508, 7, 165, 144;104.436508, 104.436508, 12, 221, 162;104.436508, -# 104.436508, 33, 252, 183;104.436508, 104.436508, 247, 252, 152;104.436508, 104.436508, -# 252, 196, 8;104.436508, 190.333333, 252, 166, 15;190.333333, 226.698413, 175, -# 101, 15;226.698413, 226.698413, 255, 133, 92;226.698413, 243.000000, 204, 204, -# 204 -# INPUT_LAYER: -# name: dem.tif -# type: raster -# Z_FACTOR: 1.0 -# results: -# OUTPUT_LAYER: -# hash: 7fe0e0174185fd743e23760f33615adf10f771b4275f320db6f7f4f8 -# type: rasterhash + - algorithm: qgis:relief + name: Relief (automatic colors generation) + params: + AUTO_COLORS: true + INPUT: + name: dem.tif + type: raster + Z_FACTOR: 1.0 + results: + OUTPUT: + hash: + - 7fe0e0174185fd743e23760f33615adf10f771b4275f320db6f7f4f8 + - 094a2d0dea250690084e0812bf1e8f8666043d17d6a71de278810bb9 + type: rasterhash + + - algorithm: qgis:relief + name: Relief (custom colors) + params: + AUTO_COLORS: false + COLORS: 85.000000, 104.436508, 7, 165, 144;104.436508, 104.436508, 12, 221, 162;104.436508, + 104.436508, 33, 252, 183;104.436508, 104.436508, 247, 252, 152;104.436508, 104.436508, + 252, 196, 8;104.436508, 190.333333, 252, 166, 15;190.333333, 226.698413, 175, + 101, 15;226.698413, 226.698413, 255, 133, 92;226.698413, 243.000000, 204, 204, + 204 + INPUT: + name: dem.tif + type: raster + Z_FACTOR: 1.0 + results: + OUTPUT: + hash: + - 7fe0e0174185fd743e23760f33615adf10f771b4275f320db6f7f4f8 + - 094a2d0dea250690084e0812bf1e8f8666043d17d6a71de278810bb9 + type: rasterhash - algorithm: qgis:createconstantrasterlayer name: Create constant raster From be48f17e2c77e8138b1b0b1f0ee80fb27f907f5b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 00:07:07 +1000 Subject: [PATCH 032/364] Use a QgsFeedback object in QgsRelief instead of QProgressDialog --- doc/api_break.dox | 5 ++++ python/analysis/raster/qgsrelief.sip | 4 ++-- python/plugins/processing/algs/qgis/Relief.py | 5 ++-- src/analysis/raster/qgsrelief.cpp | 23 ++++++++----------- src/analysis/raster/qgsrelief.h | 6 ++--- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/doc/api_break.dox b/doc/api_break.dox index 17e5773d269..f5e5e9e493f 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -1973,6 +1973,11 @@ QgsRelation {#qgis_api_break_3_0_QgsRelation} - `setRelationName()` has been renamed to `QgsRelation::setName()` - `setRelationId()` has been renamed to `QgsRelation::setId()` +QgsRelief {#qgis_api_break_3_0_QgsRelief} +--------- + +- processRaster() now uses a QgsFeedback object instead of a QProgressDialog + QgsRenderChecker {#qgis_api_break_3_0_QgsRenderChecker} ---------------- diff --git a/python/analysis/raster/qgsrelief.sip b/python/analysis/raster/qgsrelief.sip index 3e8356f0503..2988af7a635 100644 --- a/python/analysis/raster/qgsrelief.sip +++ b/python/analysis/raster/qgsrelief.sip @@ -32,10 +32,10 @@ class QgsRelief ~QgsRelief(); - int processRaster( QProgressDialog *p ); + int processRaster( QgsFeedback *feedback = 0 ); %Docstring Starts the calculation, reads from mInputFile and stores the result in mOutputFile -\param p progress dialog that receives update and that is checked for abort. 0 if no progress bar is needed. +\param feedback feedback object that receives update and that is checked for cancelation. :return: 0 in case of success* :rtype: int %End diff --git a/python/plugins/processing/algs/qgis/Relief.py b/python/plugins/processing/algs/qgis/Relief.py index fa38d867b9b..f0e31347c07 100644 --- a/python/plugins/processing/algs/qgis/Relief.py +++ b/python/plugins/processing/algs/qgis/Relief.py @@ -154,7 +154,8 @@ class Relief(QgisAlgorithm): relief.setReliefColors(reliefColors) relief.setZFactor(zFactor) - relief.exportFrequencyDistributionToCsv(frequencyDistribution) - relief.processRaster(None) + if frequencyDistribution: + relief.exportFrequencyDistributionToCsv(frequencyDistribution) + relief.processRaster(feedback) return {self.OUTPUT: outputFile, self.FREQUENCY_DISTRIBUTION: frequencyDistribution} diff --git a/src/analysis/raster/qgsrelief.cpp b/src/analysis/raster/qgsrelief.cpp index 770000c1846..d54238a841f 100644 --- a/src/analysis/raster/qgsrelief.cpp +++ b/src/analysis/raster/qgsrelief.cpp @@ -20,11 +20,13 @@ #include "qgsaspectfilter.h" #include "qgshillshadefilter.h" #include "qgsslopefilter.h" +#include "qgsfeedback.h" #include "qgis.h" #include "cpl_string.h" -#include #include +#include +#include #include #include @@ -78,7 +80,7 @@ void QgsRelief::setDefaultReliefColors() addReliefColorClass( ReliefColor( QColor( 255, 255, 255 ), 4000, 9000 ) ); } -int QgsRelief::processRaster( QProgressDialog *p ) +int QgsRelief::processRaster( QgsFeedback *feedback ) { //open input file int xSize, ySize; @@ -170,22 +172,17 @@ int QgsRelief::processRaster( QProgressDialog *p ) unsigned char *resultGreenLine = ( unsigned char * ) CPLMalloc( sizeof( unsigned char ) * xSize ); unsigned char *resultBlueLine = ( unsigned char * ) CPLMalloc( sizeof( unsigned char ) * xSize ); - if ( p ) - { - p->setMaximum( ySize ); - } - bool resultOk; //values outside the layer extent (if the 3x3 window is on the border) are sent to the processing method as (input) nodata values for ( int i = 0; i < ySize; ++i ) { - if ( p ) + if ( feedback ) { - p->setValue( i ); + feedback->setProgress( 100.0 * i / static_cast< double >( ySize ) ); } - if ( p && p->wasCanceled() ) + if ( feedback && feedback->isCanceled() ) { break; } @@ -269,9 +266,9 @@ int QgsRelief::processRaster( QProgressDialog *p ) } } - if ( p ) + if ( feedback ) { - p->setValue( ySize ); + feedback->setProgress( 100 ); } CPLFree( resultRedLine ); @@ -283,7 +280,7 @@ int QgsRelief::processRaster( QProgressDialog *p ) GDALClose( inputDataset ); - if ( p && p->wasCanceled() ) + if ( feedback && feedback->isCanceled() ) { //delete the dataset without closing (because it is faster) GDALDeleteDataset( outputDriver, mOutputFile.toUtf8().constData() ); diff --git a/src/analysis/raster/qgsrelief.h b/src/analysis/raster/qgsrelief.h index 53a21502e8c..c02338adefe 100644 --- a/src/analysis/raster/qgsrelief.h +++ b/src/analysis/raster/qgsrelief.h @@ -28,7 +28,7 @@ class QgsAspectFilter; class QgsSlopeFilter; class QgsHillshadeFilter; -class QProgressDialog; +class QgsFeedback; /** \ingroup analysis * Produces colored relief rasters from DEM*/ @@ -52,9 +52,9 @@ class ANALYSIS_EXPORT QgsRelief QgsRelief &operator=( const QgsRelief &rh ) = delete; /** Starts the calculation, reads from mInputFile and stores the result in mOutputFile - \param p progress dialog that receives update and that is checked for abort. 0 if no progress bar is needed. + \param feedback feedback object that receives update and that is checked for cancelation. \returns 0 in case of success*/ - int processRaster( QProgressDialog *p ); + int processRaster( QgsFeedback *feedback = nullptr ); double zFactor() const { return mZFactor; } void setZFactor( double factor ) { mZFactor = factor; } From cfbc00990b2ad8ce0ad1db37e2b13439b6d0a3e6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 00:23:25 +1000 Subject: [PATCH 033/364] Restore Set style for vector layer alg --- .../algs/qgis/QGISAlgorithmProvider.py | 5 +-- .../processing/algs/qgis/SetVectorStyle.py | 36 ++++++++----------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 21b075c29d0..12913b5e6ab 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -128,6 +128,7 @@ from .SelectByExpression import SelectByExpression from .ServiceAreaFromLayer import ServiceAreaFromLayer from .ServiceAreaFromPoint import ServiceAreaFromPoint from .SetMValue import SetMValue +from .SetVectorStyle import SetVectorStyle from .SetZValue import SetZValue from .ShortestPathLayerToPoint import ShortestPathLayerToPoint from .ShortestPathPointToLayer import ShortestPathPointToLayer @@ -161,7 +162,6 @@ from .ZonalStatistics import ZonalStatistics # from .GeometryConvert import GeometryConvert # from .FieldsCalculator import FieldsCalculator # from .FieldPyculator import FieldsPyculator -# from .SetVectorStyle import SetVectorStyle # from .SetRasterStyle import SetRasterStyle # from .SelectByAttributeSum import SelectByAttributeSum # from .Datasources2Vrt import Datasources2Vrt @@ -192,7 +192,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SpatialJoin(), # GeometryConvert(), FieldsCalculator(), # FieldsPyculator(), - # SetVectorStyle(), SetRasterStyle(), + # SetRasterStyle(), # FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(), # DefineProjection(), # RectanglesOvalsDiamondsVariable(), @@ -289,6 +289,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ServiceAreaFromLayer(), ServiceAreaFromPoint(), SetMValue(), + SetVectorStyle(), SetZValue(), ShortestPathLayerToPoint(), ShortestPathPointToLayer(), diff --git a/python/plugins/processing/algs/qgis/SetVectorStyle.py b/python/plugins/processing/algs/qgis/SetVectorStyle.py index 73f85a4a021..1f37eb56117 100644 --- a/python/plugins/processing/algs/qgis/SetVectorStyle.py +++ b/python/plugins/processing/algs/qgis/SetVectorStyle.py @@ -25,14 +25,10 @@ __copyright__ = '(C) 2012, Victor Olaya' __revision__ = '$Format:%H$' -import os -from qgis.core import (QgsApplication, - QgsProcessingUtils) +from qgis.core import (QgsProcessingParameterFile, + QgsProcessingParameterVectorLayer, + QgsProcessingOutputVectorLayer) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.outputs import OutputVector -from processing.core.parameters import ParameterFile -from processing.tools import dataobjects class SetVectorStyle(QgisAlgorithm): @@ -48,11 +44,12 @@ class SetVectorStyle(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT, - self.tr('Vector layer'))) - self.addParameter(ParameterFile(self.STYLE, - self.tr('Style file'), False, False, 'qml')) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Styled'), True)) + self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT, + self.tr('Vector layer'))) + self.addParameter(QgsProcessingParameterFile(self.STYLE, + self.tr('Style file'), extension='qml')) + self.addOutput(QgsProcessingOutputVectorLayer(self.INPUT, + self.tr('Styled'))) def name(self): return 'setstyleforvectorlayer' @@ -61,13 +58,8 @@ class SetVectorStyle(QgisAlgorithm): return self.tr('Set style for vector layer') def processAlgorithm(self, parameters, context, feedback): - filename = self.getParameterValue(self.INPUT) - - style = self.getParameterValue(self.STYLE) - layer = QgsProcessingUtils.mapLayerFromString(filename, context, False) - if layer is None: - dataobjects.load(filename, os.path.basename(filename), style=style) - else: - layer.loadNamedStyle(style) - context.addLayerToLoadOnCompletion(layer.id()) - layer.triggerRepaint() + layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) + style = self.parameterAsFile(parameters, self.STYLE, context) + layer.loadNamedStyle(style) + layer.triggerRepaint() + return {self.INPUT: layer} From 387e04974a8b4f084f836ee4e792f12c1205146b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 00:23:43 +1000 Subject: [PATCH 034/364] Allow selecting wildcard filter for any file input parameter --- python/plugins/processing/gui/FileSelectionPanel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/gui/FileSelectionPanel.py b/python/plugins/processing/gui/FileSelectionPanel.py index d79bb8e5b39..1c2120f6250 100644 --- a/python/plugins/processing/gui/FileSelectionPanel.py +++ b/python/plugins/processing/gui/FileSelectionPanel.py @@ -71,7 +71,7 @@ class FileSelectionPanel(BASE, WIDGET): os.path.dirname(folder)) else: filenames, selected_filter = QFileDialog.getOpenFileNames(self, - self.tr('Select file'), path, '*.' + self.ext) + self.tr('Select file'), path, self.tr('{} files').format(self.ext.upper()) + ' (*.' + self.ext + self.tr(');;All files (*.*)')) if filenames: self.leText.setText(u';'.join(filenames)) settings.setValue('/Processing/LastInputPath', From 0a4a7acf5842ff7de8c22c4db8d46057e7195f9b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 00:30:32 +1000 Subject: [PATCH 035/364] Port set raster style alg to new API --- .../algs/qgis/QGISAlgorithmProvider.py | 4 +- .../processing/algs/qgis/SetRasterStyle.py | 44 +++++++------------ 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 12913b5e6ab..6fc2c7047c5 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -128,6 +128,7 @@ from .SelectByExpression import SelectByExpression from .ServiceAreaFromLayer import ServiceAreaFromLayer from .ServiceAreaFromPoint import ServiceAreaFromPoint from .SetMValue import SetMValue +from .SetRasterStyle import SetRasterStyle from .SetVectorStyle import SetVectorStyle from .SetZValue import SetZValue from .ShortestPathLayerToPoint import ShortestPathLayerToPoint @@ -162,7 +163,6 @@ from .ZonalStatistics import ZonalStatistics # from .GeometryConvert import GeometryConvert # from .FieldsCalculator import FieldsCalculator # from .FieldPyculator import FieldsPyculator -# from .SetRasterStyle import SetRasterStyle # from .SelectByAttributeSum import SelectByAttributeSum # from .Datasources2Vrt import Datasources2Vrt # from .DefineProjection import DefineProjection @@ -192,7 +192,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SpatialJoin(), # GeometryConvert(), FieldsCalculator(), # FieldsPyculator(), - # SetRasterStyle(), # FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(), # DefineProjection(), # RectanglesOvalsDiamondsVariable(), @@ -289,6 +288,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ServiceAreaFromLayer(), ServiceAreaFromPoint(), SetMValue(), + SetRasterStyle(), SetVectorStyle(), SetZValue(), ShortestPathLayerToPoint(), diff --git a/python/plugins/processing/algs/qgis/SetRasterStyle.py b/python/plugins/processing/algs/qgis/SetRasterStyle.py index 061e190d387..1ad3662e2f3 100644 --- a/python/plugins/processing/algs/qgis/SetRasterStyle.py +++ b/python/plugins/processing/algs/qgis/SetRasterStyle.py @@ -29,13 +29,10 @@ import os from qgis.PyQt.QtXml import QDomDocument -from qgis.core import (QgsApplication, - QgsProcessingUtils) +from qgis.core import (QgsProcessingParameterRasterLayer, + QgsProcessingParameterFile, + QgsProcessingOutputRasterLayer) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterFile -from processing.core.parameters import ParameterRaster -from processing.core.outputs import OutputRaster -from processing.tools import dataobjects class SetRasterStyle(QgisAlgorithm): @@ -51,11 +48,11 @@ class SetRasterStyle(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterRaster(self.INPUT, - self.tr('Raster layer'))) - self.addParameter(ParameterFile(self.STYLE, - self.tr('Style file'), False, False, 'qml')) - self.addOutput(OutputRaster(self.OUTPUT, self.tr('Styled'), True)) + self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, + self.tr('Raster layer'))) + self.addParameter(QgsProcessingParameterFile(self.STYLE, + self.tr('Style file'), extension='qml')) + self.addOutput(QgsProcessingOutputRasterLayer(self.INPUT, self.tr('Styled'))) def name(self): return 'setstyleforrasterlayer' @@ -64,19 +61,12 @@ class SetRasterStyle(QgisAlgorithm): return self.tr('Set style for raster layer') def processAlgorithm(self, parameters, context, feedback): - filename = self.getParameterValue(self.INPUT) - layer = QgsProcessingUtils.mapLayerFromString(filename, context) - - style = self.getParameterValue(self.STYLE) - if layer is None: - dataobjects.load(filename, os.path.basename(filename), style=style) - else: - with open(style) as f: - xml = "".join(f.readlines()) - d = QDomDocument() - d.setContent(xml) - n = d.firstChild() - layer.readSymbology(n, '') - context.addLayerToLoadOnCompletion(self.getOutputFromName(self.OUTPUT).value) - self.setOutputValue(self.OUTPUT, filename) - layer.triggerRepaint() + layer = self.parameterAsRasterLayer(parameters, self.INPUT, context) + style = self.parameterAsFile(parameters, self.STYLE, context) + with open(style) as f: + xml = "".join(f.readlines()) + d = QDomDocument() + d.setContent(xml) + layer.importNamedStyle(d) + layer.triggerRepaint() + return {self.INPUT: layer} From 7879c0a20b460ed1f6167850a8a1841af48529b5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 00:47:21 +1000 Subject: [PATCH 036/364] Fix processing rendering styles for output dialog, functionality --- .../core/processing/qgsprocessingcontext.sip | 7 ++- .../gui/EditRenderingStylesDialog.py | 43 +++++++++---------- .../plugins/processing/gui/Postprocessing.py | 4 +- src/core/processing/qgsprocessingcontext.h | 6 ++- .../processing/qgsprocessingparameters.cpp | 10 ++++- tests/src/core/testqgsprocessing.cpp | 2 + 6 files changed, 44 insertions(+), 28 deletions(-) diff --git a/python/core/processing/qgsprocessingcontext.sip b/python/core/processing/qgsprocessingcontext.sip index 53f97f35cc9..bfcad4d3575 100644 --- a/python/core/processing/qgsprocessingcontext.sip +++ b/python/core/processing/qgsprocessingcontext.sip @@ -92,7 +92,7 @@ class QgsProcessingContext struct LayerDetails { - LayerDetails( const QString &name, QgsProject *project ); + LayerDetails( const QString &name, QgsProject *project, const QString &outputName = QString() ); %Docstring Constructor for LayerDetails. %End @@ -100,6 +100,11 @@ class QgsProcessingContext QString name; %Docstring Friendly name for layer, to use when loading layer into project. +%End + + QString outputName; +%Docstring +Associated output name from algorithm which generated the layer. %End QgsProject *project; diff --git a/python/plugins/processing/gui/EditRenderingStylesDialog.py b/python/plugins/processing/gui/EditRenderingStylesDialog.py index 8152fcab66c..a064cea3e85 100644 --- a/python/plugins/processing/gui/EditRenderingStylesDialog.py +++ b/python/plugins/processing/gui/EditRenderingStylesDialog.py @@ -32,12 +32,11 @@ from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt from qgis.PyQt.QtWidgets import QDialog, QHeaderView, QTableWidgetItem -from qgis.core import QgsProcessingParameterDefinition +from qgis.core import (QgsProcessingOutputRasterLayer, + QgsProcessingOutputVectorLayer) from processing.gui.RenderingStyles import RenderingStyles from processing.gui.RenderingStyleFilePanel import RenderingStyleFilePanel -from processing.core.outputs import OutputRaster -from processing.core.outputs import OutputVector pluginPath = os.path.split(os.path.dirname(__file__))[0] WIDGET, BASE = uic.loadUiType( @@ -61,29 +60,27 @@ class EditRenderingStylesDialog(BASE, WIDGET): def setTableContent(self): numOutputs = 0 - for output in self.alg.outputs: - if isinstance(output, (OutputVector, OutputRaster)): - if not output.flags() & QgsProcessingParameterDefinition.FlagHidden: - numOutputs += 1 + for output in self.alg.outputDefinitions(): + if isinstance(output, (QgsProcessingOutputVectorLayer, QgsProcessingOutputRasterLayer)): + numOutputs += 1 self.tblStyles.setRowCount(numOutputs) i = 0 - for output in self.alg.outputs: - if isinstance(output, (OutputVector, OutputRaster)): - if not output.flags() & QgsProcessingParameterDefinition.FlagHidden: - item = QTableWidgetItem(output.description() + '<' + - output.__class__.__name__ + '>') - item.setFlags(Qt.ItemIsEnabled) - self.tblStyles.setItem(i, 0, item) - item = RenderingStyleFilePanel() - style = \ - RenderingStyles.getStyle(self.alg.id(), - output.name()) - if style: - item.setText(str(style)) - self.valueItems[output.name()] = item - self.tblStyles.setCellWidget(i, 1, item) - self.tblStyles.setRowHeight(i, 22) + for output in self.alg.outputDefinitions(): + if isinstance(output, (QgsProcessingOutputVectorLayer, QgsProcessingOutputRasterLayer)): + item = QTableWidgetItem(output.description() + '<' + + output.__class__.__name__ + '>') + item.setFlags(Qt.ItemIsEnabled) + self.tblStyles.setItem(i, 0, item) + item = RenderingStyleFilePanel() + style = \ + RenderingStyles.getStyle(self.alg.id(), + output.name()) + if style: + item.setText(str(style)) + self.valueItems[output.name()] = item + self.tblStyles.setCellWidget(i, 1, item) + self.tblStyles.setRowHeight(i, 22) i += 1 def accept(self): diff --git a/python/plugins/processing/gui/Postprocessing.py b/python/plugins/processing/gui/Postprocessing.py index c2886325bf2..8010e218f05 100644 --- a/python/plugins/processing/gui/Postprocessing.py +++ b/python/plugins/processing/gui/Postprocessing.py @@ -60,7 +60,9 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True): if layer is not None: layer.setName(details.name) - style = RenderingStyles.getStyle(alg.id(), layer.name()) + style = None + if details.outputName: + style = RenderingStyles.getStyle(alg.id(), details.outputName) if style is None: if layer.type() == QgsMapLayer.RasterLayer: style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE) diff --git a/src/core/processing/qgsprocessingcontext.h b/src/core/processing/qgsprocessingcontext.h index 3316300f2ec..2e4fb87de55 100644 --- a/src/core/processing/qgsprocessingcontext.h +++ b/src/core/processing/qgsprocessingcontext.h @@ -134,14 +134,18 @@ class CORE_EXPORT QgsProcessingContext /** * Constructor for LayerDetails. */ - LayerDetails( const QString &name, QgsProject *project ) + LayerDetails( const QString &name, QgsProject *project, const QString &outputName = QString() ) : name( name ) + , outputName( outputName ) , project( project ) {} //! Friendly name for layer, to use when loading layer into project. QString name; + //! Associated output name from algorithm which generated the layer. + QString outputName; + //! Destination project QgsProject *project; diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 0acd2a9fc93..2860fba4b16 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -265,7 +265,10 @@ QgsFeatureSink *QgsProcessingParameters::parameterAsSink( const QgsProcessingPar { destName = definition->description(); } - context.addLayerToLoadOnCompletion( destinationIdentifier, QgsProcessingContext::LayerDetails( destName, destinationProject ) ); + QString outputName; + if ( definition ) + outputName = definition->name(); + context.addLayerToLoadOnCompletion( destinationIdentifier, QgsProcessingContext::LayerDetails( destName, destinationProject, outputName ) ); } return sink.release(); @@ -404,11 +407,14 @@ QString QgsProcessingParameters::parameterAsOutputLayer( const QgsProcessingPara if ( destinationProject ) { + QString outputName; if ( destName.isEmpty() && definition ) { destName = definition->description(); } - context.addLayerToLoadOnCompletion( dest, QgsProcessingContext::LayerDetails( destName, destinationProject ) ); + if ( definition ) + outputName = definition->name(); + context.addLayerToLoadOnCompletion( dest, QgsProcessingContext::LayerDetails( destName, destinationProject, outputName ) ); } return dest; diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index 87ff8bcb6b0..60dfd7a488c 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -1382,6 +1382,7 @@ void TestQgsProcessing::parameters() QCOMPARE( context2.layersToLoadOnCompletion().size(), 1 ); QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 0 ), destId ); QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "my_dest" ) ); + QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).outputName, QStringLiteral( "fs" ) ); } void TestQgsProcessing::algorithmParameters() @@ -3820,6 +3821,7 @@ void TestQgsProcessing::parameterRasterOut() QCOMPARE( context2.layersToLoadOnCompletion().size(), 1 ); QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 0 ), QStringLiteral( "test.tif" ) ); QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "my_dest" ) ); + QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).outputName, QStringLiteral( "x" ) ); } void TestQgsProcessing::parameterFileOut() From f1ac0be86769e924624cb87b48e0ef69a0da1387 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 00:56:18 +1000 Subject: [PATCH 037/364] Fix processing setting to use filename as layer name --- python/plugins/processing/gui/Postprocessing.py | 3 ++- src/core/processing/qgsprocessingutils.cpp | 7 +++++-- tests/src/core/testqgsprocessing.cpp | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/python/plugins/processing/gui/Postprocessing.py b/python/plugins/processing/gui/Postprocessing.py index 8010e218f05..ffe40ad7b35 100644 --- a/python/plugins/processing/gui/Postprocessing.py +++ b/python/plugins/processing/gui/Postprocessing.py @@ -58,7 +58,8 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True): try: layer = QgsProcessingUtils.mapLayerFromString(l, context) if layer is not None: - layer.setName(details.name) + if not ProcessingConfig.getSetting(ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): + layer.setName(details.name) style = None if details.outputName: diff --git a/src/core/processing/qgsprocessingutils.cpp b/src/core/processing/qgsprocessingutils.cpp index fe4e0742cac..2da3bcb4d43 100644 --- a/src/core/processing/qgsprocessingutils.cpp +++ b/src/core/processing/qgsprocessingutils.cpp @@ -161,13 +161,16 @@ QgsMapLayer *QgsProcessingUtils::loadMapLayerFromString( const QString &string ) ProjectionSettingRestorer restorer; ( void )restorer; // no warnings + QFileInfo fi( string ); + QString name = fi.baseName(); + // brute force attempt to load a matching layer - std::unique_ptr< QgsVectorLayer > layer( new QgsVectorLayer( string, QStringLiteral( "temp" ), QStringLiteral( "ogr" ), false ) ); + std::unique_ptr< QgsVectorLayer > layer( new QgsVectorLayer( string, name, QStringLiteral( "ogr" ), false ) ); if ( layer->isValid() ) { return layer.release(); } - std::unique_ptr< QgsRasterLayer > rasterLayer( new QgsRasterLayer( string, QStringLiteral( "temp" ), QStringLiteral( "gdal" ), false ) ); + std::unique_ptr< QgsRasterLayer > rasterLayer( new QgsRasterLayer( string, name, QStringLiteral( "gdal" ), false ) ); if ( rasterLayer->isValid() ) { return rasterLayer.release(); diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index 60dfd7a488c..f64b8496799 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -684,12 +684,15 @@ void TestQgsProcessing::mapLayers() QgsMapLayer *l = QgsProcessingUtils::loadMapLayerFromString( raster ); QVERIFY( l->isValid() ); QCOMPARE( l->type(), QgsMapLayer::RasterLayer ); + QCOMPARE( l->name(), QStringLiteral( "landsat" ) ); + delete l; //test with vector l = QgsProcessingUtils::loadMapLayerFromString( vector ); QVERIFY( l->isValid() ); QCOMPARE( l->type(), QgsMapLayer::VectorLayer ); + QCOMPARE( l->name(), QStringLiteral( "points" ) ); delete l; l = QgsProcessingUtils::loadMapLayerFromString( QString() ); @@ -699,6 +702,7 @@ void TestQgsProcessing::mapLayers() l = QgsProcessingUtils::loadMapLayerFromString( testDataDir + "multipoint.shp" ); QVERIFY( l->isValid() ); QCOMPARE( l->type(), QgsMapLayer::VectorLayer ); + QCOMPARE( l->name(), QStringLiteral( "multipoint" ) ); delete l; } From f2d2777bf3af2f6cb60ca56f2d0cba302cbc54af Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 01:09:22 +1000 Subject: [PATCH 038/364] Model vector layer inputs can also be used for feature source parameters --- python/plugins/processing/gui/wrappers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index db431077aca..6373dd26fc8 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -862,7 +862,7 @@ class VectorWidgetWrapper(WidgetWrapper): return widget else: self.combo = QComboBox() - layers = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFeatureSource, QgsProcessingOutputVectorLayer) + layers = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer), QgsProcessingOutputVectorLayer) self.combo.setEditable(True) for layer in layers: self.combo.addItem(self.dialog.resolveValueDescription(layer), layer) From 48202165fc5581b6616a246d696c91f0306a2a3a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 01:26:55 +1000 Subject: [PATCH 039/364] Port build virtual vector alg to new API --- .../processing/algs/qgis/Datasources2Vrt.py | 73 +++++++++++-------- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/python/plugins/processing/algs/qgis/Datasources2Vrt.py b/python/plugins/processing/algs/qgis/Datasources2Vrt.py index 7ad3ff5b71d..e0220a32854 100644 --- a/python/plugins/processing/algs/qgis/Datasources2Vrt.py +++ b/python/plugins/processing/algs/qgis/Datasources2Vrt.py @@ -31,21 +31,20 @@ import xml.sax.saxutils from osgeo import ogr from qgis.core import (QgsProcessingFeedback, - QgsApplication) -from processing.tools import dataobjects + QgsProcessingParameterMultipleLayers, + QgsProcessingParameterBoolean, + QgsProcessing, + QgsProcessingParameterVectorDestination, + QgsProcessingOutputString, + QgsProcessingException) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import ParameterMultipleInput -from processing.core.parameters import ParameterBoolean -from processing.core.outputs import OutputFile -from processing.core.outputs import OutputString class Datasources2Vrt(QgisAlgorithm): - DATASOURCES = 'DATASOURCES' + INPUT = 'INPUT' UNIONED = 'UNIONED' - VRT_FILE = 'VRT_FILE' + OUTPUT = 'OUTPUT' VRT_STRING = 'VRT_STRING' def group(self): @@ -55,17 +54,32 @@ class Datasources2Vrt(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterMultipleInput(self.DATASOURCES, - self.tr('Input datasources'), - dataobjects.TYPE_TABLE)) - self.addParameter(ParameterBoolean(self.UNIONED, - self.tr('Create "unioned" VRT'), - default=False)) + self.addParameter(QgsProcessingParameterMultipleLayers(self.INPUT, + self.tr('Input datasources'), + QgsProcessing.TypeTable)) + self.addParameter(QgsProcessingParameterBoolean(self.UNIONED, + self.tr('Create "unioned" VRT'), + defaultValue=False)) - self.addOutput(OutputFile(self.VRT_FILE, - self.tr('Virtual vector'), ext='vrt')) - self.addOutput(OutputString(self.VRT_STRING, - self.tr('Virtual string'))) + class ParameterVectorVrtDestination(QgsProcessingParameterVectorDestination): + + def __init__(self, name, description): + super().__init__(name, description) + + def clone(self): + copy = ParameterVectorVrtDestination(self.name(), self.description()) + return copy + + def type(self): + return 'vrt_vector_destination' + + def defaultFileExtension(self): + return 'vrt' + + self.addParameter(ParameterVectorVrtDestination(self.OUTPUT, + self.tr('Virtual vector'))) + self.addOutput(QgsProcessingOutputString(self.VRT_STRING, + self.tr('Virtual string'))) def name(self): return 'buildvirtualvector' @@ -74,20 +88,17 @@ class Datasources2Vrt(QgisAlgorithm): return self.tr('Build virtual vector') def processAlgorithm(self, parameters, context, feedback): - input_layers = self.getParameterValue(self.DATASOURCES) - unioned = self.getParameterValue(self.UNIONED) - vrtPath = self.getOutputValue(self.VRT_FILE) + input_layers = self.parameterAsLayerList(parameters, self.INPUT, context) + unioned = self.parameterAsBool(parameters, self.UNIONED, context) + vrtPath = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) - layers = input_layers.split(';') - - vrtString = self.mergeDataSources2Vrt(layers, + vrtString = self.mergeDataSources2Vrt(input_layers, vrtPath, union=unioned, relative=False, schema=False, feedback=feedback) - - self.setOutputValue(self.VRT_STRING, vrtString) + return {self.OUTPUT: vrtPath, self.VRT_STRING: vrtString} def mergeDataSources2Vrt(self, dataSources, outFile, union=False, relative=False, schema=False, feedback=None): @@ -107,12 +118,16 @@ class Datasources2Vrt(QgisAlgorithm): vrt += '' total = 100.0 / len(dataSources) if dataSources else 1 - for current, inFile in enumerate(dataSources): + for current, layer in enumerate(dataSources): + if feedback.isCanceled(): + break + feedback.setProgress(int(current * total)) + inFile = layer.source() srcDS = ogr.Open(inFile, 0) if srcDS is None: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Invalid datasource: {}'.format(inFile))) if schema: diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 6fc2c7047c5..c9160120a93 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -52,6 +52,7 @@ from .ConcaveHull import ConcaveHull from .ConvexHull import ConvexHull from .CreateAttributeIndex import CreateAttributeIndex from .CreateConstantRaster import CreateConstantRaster +from .Datasources2Vrt import Datasources2Vrt from .Delaunay import Delaunay from .DeleteColumn import DeleteColumn from .DeleteDuplicateGeometries import DeleteDuplicateGeometries @@ -164,7 +165,6 @@ from .ZonalStatistics import ZonalStatistics # from .FieldsCalculator import FieldsCalculator # from .FieldPyculator import FieldsPyculator # from .SelectByAttributeSum import SelectByAttributeSum -# from .Datasources2Vrt import Datasources2Vrt # from .DefineProjection import DefineProjection # from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable # from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed @@ -192,7 +192,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SpatialJoin(), # GeometryConvert(), FieldsCalculator(), # FieldsPyculator(), - # FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(), + # FieldsMapper(), SelectByAttributeSum() # DefineProjection(), # RectanglesOvalsDiamondsVariable(), # RectanglesOvalsDiamondsFixed(), @@ -212,6 +212,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ConvexHull(), CreateAttributeIndex(), CreateConstantRaster(), + Datasources2Vrt(), Delaunay(), DeleteColumn(), DeleteDuplicateGeometries(), From 548117769910a4373747899b99478835e5c65c5e Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 18 Aug 2017 17:58:00 +0200 Subject: [PATCH 040/364] [OGR provider] Avoid 'database locked' issues when editing several layers of the same GeoPackage (fixes #17034) --- src/providers/ogr/qgsogrprovider.cpp | 2 + tests/src/python/test_provider_ogr_gpkg.py | 62 ++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index c9f83520b7c..0126c6535ee 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -1671,6 +1671,7 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_ pushError( tr( "Feature %1 for attribute update not found." ).arg( fid ) ); continue; } + OGR_L_ResetReading( ogrLayer ); // needed for SQLite-based to clear iterator QgsLocaleNumC l; @@ -1811,6 +1812,7 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map ) pushError( tr( "OGR error changing geometry: feature %1 not found" ).arg( it.key() ) ); continue; } + OGR_L_ResetReading( ogrLayer ); // needed for SQLite-based to clear iterator OGRGeometryH newGeometry = nullptr; QByteArray wkb = it->exportToWkb(); diff --git a/tests/src/python/test_provider_ogr_gpkg.py b/tests/src/python/test_provider_ogr_gpkg.py index 2645028ed88..d80c3756c44 100644 --- a/tests/src/python/test_provider_ogr_gpkg.py +++ b/tests/src/python/test_provider_ogr_gpkg.py @@ -451,6 +451,68 @@ class TestPyQgsOGRProviderGpkg(unittest.TestCase): self.assertEqual(f['f1'], 3) features = None + def testGeopackageTwoLayerEdition(self): + ''' test https://issues.qgis.org/issues/17034 ''' + tmpfile = os.path.join(self.basetestpath, 'testGeopackageTwoLayerEdition.gpkg') + ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) + lyr = ds.CreateLayer('layer1', geom_type=ogr.wkbPoint) + lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) + lyr.CreateFeature(f) + f = None + lyr = ds.CreateLayer('layer2', geom_type=ogr.wkbPoint) + lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)')) + lyr.CreateFeature(f) + f = None + ds = None + + vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer1', u'ogr') + vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer2", u'layer2', u'ogr') + + # Edit vl1, vl2 multiple times + self.assertTrue(vl1.startEditing()) + self.assertTrue(vl2.startEditing()) + self.assertTrue(vl1.changeGeometry(1, QgsGeometry.fromWkt('Point (2 2)'))) + self.assertTrue(vl2.changeGeometry(1, QgsGeometry.fromWkt('Point (3 3)'))) + self.assertTrue(vl1.commitChanges()) + self.assertTrue(vl2.commitChanges()) + + self.assertTrue(vl1.startEditing()) + self.assertTrue(vl2.startEditing()) + self.assertTrue(vl1.changeAttributeValue(1, 1, 100)) + self.assertTrue(vl2.changeAttributeValue(1, 1, 101)) + self.assertTrue(vl1.commitChanges()) + self.assertTrue(vl2.commitChanges()) + + self.assertTrue(vl1.startEditing()) + self.assertTrue(vl2.startEditing()) + self.assertTrue(vl1.changeGeometry(1, QgsGeometry.fromWkt('Point (4 4)'))) + self.assertTrue(vl2.changeGeometry(1, QgsGeometry.fromWkt('Point (5 5)'))) + self.assertTrue(vl1.commitChanges()) + self.assertTrue(vl2.commitChanges()) + + vl1 = None + vl2 = None + + # Check everything is as expected after re-opening + vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer1', u'ogr') + vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer2", u'layer2', u'ogr') + + got = [feat for feat in vl1.getFeatures()][0] + got_geom = got.geometry() + self.assertEqual(got['attr'], 100) + reference = QgsGeometry.fromWkt('Point (4 4)') + self.assertEqual(got_geom.exportToWkb(), reference.exportToWkb(), 'Expected {}, got {}'.format(reference.exportToWkt(), got_geom.exportToWkt())) + + got = [feat for feat in vl2.getFeatures()][0] + got_geom = got.geometry() + self.assertEqual(got['attr'], 101) + reference = QgsGeometry.fromWkt('Point (5 5)') + self.assertEqual(got_geom.exportToWkb(), reference.exportToWkb(), 'Expected {}, got {}'.format(reference.exportToWkt(), got_geom.exportToWkt())) + if __name__ == '__main__': unittest.main() From 076fdc9449c4508a044bc69dd3dcc8effcfe2e22 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 02:37:39 +1000 Subject: [PATCH 041/364] Port rectangles, ovals, diamonds to new API --- .../algs/qgis/QGISAlgorithmProvider.py | 8 +- .../algs/qgis/RectanglesOvalsDiamondsFixed.py | 150 ++++++++---- .../qgis/RectanglesOvalsDiamondsVariable.py | 220 +++++++++++------- .../tests/testdata/qgis_algorithm_tests.yaml | 124 +++++----- 4 files changed, 298 insertions(+), 204 deletions(-) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index c9160120a93..a601bfe6813 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -119,6 +119,8 @@ from .RandomSelection import RandomSelection from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets from .Rasterize import RasterizeAlgorithm from .RasterLayerStatistics import RasterLayerStatistics +from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed +from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable from .RegularPoints import RegularPoints from .Relief import Relief from .ReverseLineDirection import ReverseLineDirection @@ -166,8 +168,6 @@ from .ZonalStatistics import ZonalStatistics # from .FieldPyculator import FieldsPyculator # from .SelectByAttributeSum import SelectByAttributeSum # from .DefineProjection import DefineProjection -# from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable -# from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed # from .IdwInterpolation import IdwInterpolation # from .TinInterpolation import TinInterpolation # from .RasterCalculator import RasterCalculator @@ -194,8 +194,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # FieldsPyculator(), # FieldsMapper(), SelectByAttributeSum() # DefineProjection(), - # RectanglesOvalsDiamondsVariable(), - # RectanglesOvalsDiamondsFixed(), # IdwInterpolation(), TinInterpolation(), # RasterCalculator(), # ExecuteSQL(), FindProjection(), @@ -279,6 +277,8 @@ class QGISAlgorithmProvider(QgsProcessingProvider): RandomSelectionWithinSubsets(), RasterizeAlgorithm(), RasterLayerStatistics(), + RectanglesOvalsDiamondsFixed(), + RectanglesOvalsDiamondsVariable(), RegularPoints(), Relief(), ReverseLineDirection(), diff --git a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py index cbe5e922917..ba69bde8057 100644 --- a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py +++ b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py @@ -28,31 +28,29 @@ __revision__ = '$Format:%H$' import math -from qgis.core import (QgsApplication, +from qgis.core import (QgsProcessingParameterFeatureSource, + QgsProcessingParameterEnum, + QgsProcessingParameterNumber, + QgsProcessingParameterFeatureSink, QgsFeature, QgsFeatureSink, QgsGeometry, QgsPointXY, QgsWkbTypes, - QgsProcessingUtils) + QgsProcessing) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterSelection -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputVector -from processing.tools import dataobjects class RectanglesOvalsDiamondsFixed(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' SHAPE = 'SHAPE' WIDTH = 'WIDTH' HEIGHT = 'HEIGHT' ROTATION = 'ROTATION' SEGMENTS = 'SEGMENTS' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' def group(self): return self.tr('Vector geometry tools') @@ -63,26 +61,26 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): def initAlgorithm(self, config=None): self.shapes = [self.tr('Rectangles'), self.tr('Diamonds'), self.tr('Ovals')] - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'), - [dataobjects.TYPE_VECTOR_POINT])) - self.addParameter(ParameterSelection(self.SHAPE, - self.tr('Buffer shape'), self.shapes)) - self.addParameter(ParameterNumber(self.WIDTH, self.tr('Width'), - 0.0000001, 999999999.0, 1.0)) - self.addParameter(ParameterNumber(self.HEIGHT, self.tr('Height'), - 0.0000001, 999999999.0, 1.0)) - self.addParameter(ParameterNumber(self.ROTATION, self.tr('Rotation'), - 0.0, 360.0, optional=True)) - self.addParameter(ParameterNumber(self.SEGMENTS, - self.tr('Number of segments'), - 1, - 999999999, - 36)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), + [QgsProcessing.TypeVectorPoint])) + self.addParameter(QgsProcessingParameterEnum(self.SHAPE, + self.tr('Buffer shape'), options=self.shapes)) + self.addParameter(QgsProcessingParameterNumber(self.WIDTH, self.tr('Width'), type=QgsProcessingParameterNumber.Double, + minValue=0.0000001, maxValue=999999999.0, defaultValue=1.0)) + self.addParameter(QgsProcessingParameterNumber(self.HEIGHT, self.tr('Height'), type=QgsProcessingParameterNumber.Double, + minValue=0.0000001, maxValue=999999999.0, defaultValue=1.0)) + self.addParameter(QgsProcessingParameterNumber(self.ROTATION, self.tr('Rotation'), type=QgsProcessingParameterNumber.Double, + minValue=0.0, maxValue=360.0, optional=True)) + self.addParameter(QgsProcessingParameterNumber(self.SEGMENTS, + self.tr('Number of segments'), + minValue=1, + maxValue=999999999, + defaultValue=36)) - self.addOutput(OutputVector(self.OUTPUT_LAYER, - self.tr('Output'), - datatype=[dataobjects.TYPE_VECTOR_POLYGON])) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('Output'), + type=QgsProcessing.TypeVectorPolygon)) def name(self): return 'rectanglesovalsdiamondsfixed' @@ -91,36 +89,44 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): return self.tr('Rectangles, ovals, diamonds (fixed)') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) - shape = self.getParameterValue(self.SHAPE) - width = self.getParameterValue(self.WIDTH) - height = self.getParameterValue(self.HEIGHT) - rotation = self.getParameterValue(self.ROTATION) - segments = self.getParameterValue(self.SEGMENTS) + source = self.parameterAsSource(parameters, self.INPUT, context) + shape = self.parameterAsEnum(parameters, self.SHAPE, context) + width = self.parameterAsDouble(parameters, self.WIDTH, context) + height = self.parameterAsDouble(parameters, self.HEIGHT, context) + rotation = self.parameterAsDouble(parameters, self.ROTATION, context) + segments = self.parameterAsInt(parameters, self.SEGMENTS, context) - writer = self.getOutputFromName( - self.OUTPUT_LAYER).getVectorWriter(layer.fields(), QgsWkbTypes.Polygon, layer.crs(), context) - - features = QgsProcessingUtils.getFeatures(layer, context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + source.fields(), QgsWkbTypes.Polygon, source.sourceCrs()) if shape == 0: - self.rectangles(writer, features, width, height, rotation) + self.rectangles(sink, source, width, height, rotation, feedback) elif shape == 1: - self.diamonds(writer, features, width, height, rotation) + self.diamonds(sink, source, width, height, rotation, feedback) else: - self.ovals(writer, features, width, height, rotation, segments) + self.ovals(sink, source, width, height, rotation, segments, feedback) - del writer + return {self.OUTPUT: dest_id} - def rectangles(self, writer, features, width, height, rotation): + def rectangles(self, sink, source, width, height, rotation, feedback): + + features = source.getFeatures() ft = QgsFeature() xOffset = width / 2.0 yOffset = height / 2.0 + total = 100.0 / source.featureCount() if source.featureCount() else 0 + if rotation is not None: phi = rotation * math.pi / 180 for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + point = feat.geometry().asPoint() x = point.x() y = point.y() @@ -130,9 +136,17 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + + feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + point = feat.geometry().asPoint() x = point.x() y = point.y() @@ -141,17 +155,27 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) - def diamonds(self, writer, features, width, height, rotation): + feedback.setProgress(int(current * total)) + + def diamonds(self, sink, source, width, height, rotation, feedback): + features = source.getFeatures() ft = QgsFeature() xOffset = width / 2.0 yOffset = height / 2.0 + total = 100.0 / source.featureCount() if source.featureCount() else 0 if rotation is not None: phi = rotation * math.pi / 180 for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + point = feat.geometry().asPoint() x = point.x() y = point.y() @@ -161,9 +185,16 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + point = feat.geometry().asPoint() x = point.x() y = point.y() @@ -172,17 +203,26 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) - def ovals(self, writer, features, width, height, rotation, segments): + def ovals(self, sink, source, width, height, rotation, segments, feedback): + features = source.getFeatures() ft = QgsFeature() xOffset = width / 2.0 yOffset = height / 2.0 + total = 100.0 / source.featureCount() if source.featureCount() else 0 if rotation is not None: phi = rotation * math.pi / 180 for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + point = feat.geometry().asPoint() x = point.x() y = point.y() @@ -194,9 +234,16 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + point = feat.geometry().asPoint() x = point.x() y = point.y() @@ -207,4 +254,5 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) diff --git a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py index 77f080c7b43..f90d2b80c95 100644 --- a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py +++ b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py @@ -28,33 +28,30 @@ __revision__ = '$Format:%H$' import math -from qgis.core import (QgsApplication, - QgsWkbTypes, +from qgis.core import (QgsWkbTypes, QgsFeature, QgsFeatureSink, QgsGeometry, QgsPointXY, - QgsMessageLog, - QgsProcessingUtils) + QgsProcessing, + QgsProcessingParameterField, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterEnum, + QgsProcessingParameterNumber, + QgsProcessingParameterFeatureSink) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterSelection -from processing.core.parameters import ParameterTableField -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputVector -from processing.tools import dataobjects class RectanglesOvalsDiamondsVariable(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' SHAPE = 'SHAPE' WIDTH = 'WIDTH' HEIGHT = 'HEIGHT' ROTATION = 'ROTATION' SEGMENTS = 'SEGMENTS' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' def group(self): return self.tr('Vector geometry tools') @@ -65,33 +62,35 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): def initAlgorithm(self, config=None): self.shapes = [self.tr('Rectangles'), self.tr('Diamonds'), self.tr('Ovals')] - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'), - [dataobjects.TYPE_VECTOR_POINT])) - self.addParameter(ParameterSelection(self.SHAPE, - self.tr('Buffer shape'), self.shapes)) - self.addParameter(ParameterTableField(self.WIDTH, - self.tr('Width field'), - self.INPUT_LAYER, - ParameterTableField.DATA_TYPE_NUMBER)) - self.addParameter(ParameterTableField(self.HEIGHT, - self.tr('Height field'), - self.INPUT_LAYER, - ParameterTableField.DATA_TYPE_NUMBER)) - self.addParameter(ParameterTableField(self.ROTATION, - self.tr('Rotation field'), - self.INPUT_LAYER, - ParameterTableField.DATA_TYPE_NUMBER, - True)) - self.addParameter(ParameterNumber(self.SEGMENTS, - self.tr('Number of segments'), - 1, - 999999999, - 36)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), + [QgsProcessing.TypeVectorPoint])) - self.addOutput(OutputVector(self.OUTPUT_LAYER, - self.tr('Output'), - datatype=[dataobjects.TYPE_VECTOR_POLYGON])) + self.addParameter(QgsProcessingParameterEnum(self.SHAPE, + self.tr('Buffer shape'), options=self.shapes)) + + self.addParameter(QgsProcessingParameterField(self.WIDTH, + self.tr('Width field'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) + self.addParameter(QgsProcessingParameterField(self.HEIGHT, + self.tr('Height field'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) + self.addParameter(QgsProcessingParameterField(self.ROTATION, + self.tr('Rotation field'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric, + optional=True)) + self.addParameter(QgsProcessingParameterNumber(self.SEGMENTS, + self.tr('Number of segments'), + minValue=1, + maxValue=999999999, + defaultValue=36)) + + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('Output'), + type=QgsProcessing.TypeVectorPolygon)) def name(self): return 'rectanglesovalsdiamondsvariable' @@ -100,40 +99,50 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): return self.tr('Rectangles, ovals, diamonds (variable)') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) - shape = self.getParameterValue(self.SHAPE) - width = self.getParameterValue(self.WIDTH) - height = self.getParameterValue(self.HEIGHT) - rotation = self.getParameterValue(self.ROTATION) - segments = self.getParameterValue(self.SEGMENTS) + source = self.parameterAsSource(parameters, self.INPUT, context) + shape = self.parameterAsEnum(parameters, self.SHAPE, context) - writer = self.getOutputFromName( - self.OUTPUT_LAYER).getVectorWriter(layer.fields(), QgsWkbTypes.Polygon, layer.crs(), context) + width_field = self.parameterAsString(parameters, self.WIDTH, context) + height_field = self.parameterAsString(parameters, self.HEIGHT, context) + rotation_field = self.parameterAsString(parameters, self.ROTATION, context) + segments = self.parameterAsInt(parameters, self.SEGMENTS, context) - features = QgsProcessingUtils.getFeatures(layer, context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + source.fields(), QgsWkbTypes.Polygon, source.sourceCrs()) + width = source.fields().lookupField(width_field) + height = source.fields().lookupField(height_field) + rotation = source.fields().lookupField(rotation_field) if shape == 0: - self.rectangles(writer, features, width, height, rotation) + self.rectangles(sink, source, width, height, rotation, feedback) elif shape == 1: - self.diamonds(writer, features, width, height, rotation) + self.diamonds(sink, source, width, height, rotation, feedback) else: - self.ovals(writer, features, width, height, rotation, segments) + self.ovals(sink, source, width, height, rotation, segments, feedback) - del writer + return {self.OUTPUT: dest_id} - def rectangles(self, writer, features, width, height, rotation): + def rectangles(self, sink, source, width, height, rotation, feedback): ft = QgsFeature() + features = source.getFeatures() - if rotation is not None: + total = 100.0 / source.featureCount() if source.featureCount() else 0 + + if rotation >= 0: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + w = feat[width] h = feat[height] angle = feat[rotation] if not w or not h or not angle: - QgsMessageLog.logMessage(self.tr('Feature {} has empty ' - 'width, height or angle. ' - 'Skipping...'.format(feat.id())), - self.tr('Processing'), QgsMessageLog.WARNING) + feedback.pushInfo(self.tr('Feature {} has empty ' + 'width, height or angle. ' + 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 @@ -149,16 +158,23 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + + feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + w = feat[width] h = feat[height] if not w or not h: - QgsMessageLog.logMessage(self.tr('Feature {} has empty ' - 'width or height. ' - 'Skipping...'.format(feat.id())), - self.tr('Processing'), QgsMessageLog.WARNING) + feedback.pushInfo(self.tr('Feature {} has empty ' + 'width or height. ' + 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 @@ -172,21 +188,30 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) - def diamonds(self, writer, features, width, height, rotation): + feedback.setProgress(int(current * total)) + + def diamonds(self, sink, source, width, height, rotation, feedback): + features = source.getFeatures() ft = QgsFeature() - if rotation is not None: + total = 100.0 / source.featureCount() if source.featureCount() else 0 + if rotation >= 0: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + w = feat[width] h = feat[height] angle = feat[rotation] if not w or not h or not angle: - QgsMessageLog.logMessage(self.tr('Feature {} has empty ' - 'width, height or angle. ' - 'Skipping...'.format(feat.id())), - self.tr('Processing'), QgsMessageLog.WARNING) + feedback.pushInfo(self.tr('Feature {} has empty ' + 'width, height or angle. ' + 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 @@ -202,16 +227,22 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + w = feat[width] h = feat[height] if not w or not h: - QgsMessageLog.logMessage(self.tr('Feature {} has empty ' - 'width or height. ' - 'Skipping...'.format(feat.id())), - self.tr('Processing'), QgsMessageLog.WARNING) + feedback.pushInfo(self.tr('Feature {} has empty ' + 'width or height. ' + 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 @@ -225,21 +256,29 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) - def ovals(self, writer, features, width, height, rotation, segments): + def ovals(self, sink, source, width, height, rotation, segments, feedback): + features = source.getFeatures() ft = QgsFeature() - if rotation is not None: + total = 100.0 / source.featureCount() if source.featureCount() else 0 + if rotation >= 0: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + w = feat[width] h = feat[height] angle = feat[rotation] if not w or not h or not angle: - QgsMessageLog.logMessage(self.tr('Feature {} has empty ' - 'width, height or angle. ' - 'Skipping...'.format(feat.id())), - self.tr('Processing'), QgsMessageLog.WARNING) + feedback.pushInfo(self.tr('Feature {} has empty ' + 'width, height or angle. ' + 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 @@ -257,16 +296,22 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): + if feedback.isCanceled(): + break + + if not feat.hasGeometry(): + continue + w = feat[width] h = feat[height] if not w or not h: - QgsMessageLog.logMessage(self.tr('Feature {} has empty ' - 'width or height. ' - 'Skipping...'.format(feat.id())), - self.tr('Processing'), QgsMessageLog.WARNING) + feedback.pushInfo(self.tr('Feature {} has empty ' + 'width or height. ' + 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 @@ -282,4 +327,5 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): ft.setGeometry(QgsGeometry.fromPolygon(polygon)) ft.setAttributes(feat.attributes()) - writer.addFeature(ft, QgsFeatureSink.FastInsert) + sink.addFeature(ft, QgsFeatureSink.FastInsert) + feedback.setProgress(int(current * total)) diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 4b2bb9c7b52..42be1f3c818 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -569,24 +569,24 @@ tests: intval: skip floatval: skip -# - algorithm: qgis:rectanglesovalsdiamondsfixed -# name: Create fixed distance rectange buffers around points -# params: -# HEIGHT: 0.25 -# INPUT_LAYER: -# name: points.gml -# type: vector -# ROTATION: 45 -# SEGMENTS: 36 -# SHAPE: 0 -# WIDTH: 0.5 -# results: -# OUTPUT_LAYER: -# name: expected/rectanglesovalsdiamondsfixed.gml -# type: vector -# compare: -# geometry: -# precision: 7 + - algorithm: qgis:rectanglesovalsdiamondsfixed + name: Create fixed distance rectange buffers around points + params: + HEIGHT: 0.25 + INPUT: + name: points.gml + type: vector + ROTATION: 45 + SEGMENTS: 36 + SHAPE: 0 + WIDTH: 0.5 + results: + OUTPUT: + name: expected/rectanglesovalsdiamondsfixed.gml + type: vector + compare: + geometry: + precision: 7 - algorithm: qgis:mergelines name: Merge lines algorithm @@ -2898,50 +2898,50 @@ tests: geometry: precision: 5 -# - algorithm: qgis:rectanglesovalsdiamondsvariable -# name: Rectangular buffer shape -# params: -# HEIGHT: id -# INPUT_LAYER: -# name: custom/points_weighted.gml -# type: vector -# SEGMENTS: 36 -# SHAPE: '0' -# WIDTH: id -# results: -# OUTPUT_LAYER: -# name: expected/buffer_rect.gml -# type: vector -# -# - algorithm: qgis:rectanglesovalsdiamondsvariable -# name: Diamond buffer shape -# params: -# HEIGHT: id -# INPUT_LAYER: -# name: custom/points_weighted.gml -# type: vector -# SEGMENTS: 36 -# SHAPE: '1' -# WIDTH: id -# results: -# OUTPUT_LAYER: -# name: expected/buffer_diamond.gml -# type: vector -# -# - algorithm: qgis:rectanglesovalsdiamondsvariable -# name: Oval buffer shape -# params: -# HEIGHT: id -# INPUT_LAYER: -# name: custom/points_weighted.gml -# type: vector -# SEGMENTS: 36 -# SHAPE: '2' -# WIDTH: id -# results: -# OUTPUT_LAYER: -# name: expected/buffer_ovals.gml -# type: vector + - algorithm: qgis:rectanglesovalsdiamondsvariable + name: Rectangular buffer shape + params: + HEIGHT: id + INPUT: + name: custom/points_weighted.gml + type: vector + SEGMENTS: 36 + SHAPE: '0' + WIDTH: id + results: + OUTPUT: + name: expected/buffer_rect.gml + type: vector + + - algorithm: qgis:rectanglesovalsdiamondsvariable + name: Diamond buffer shape + params: + HEIGHT: id + INPUT: + name: custom/points_weighted.gml + type: vector + SEGMENTS: 36 + SHAPE: '1' + WIDTH: id + results: + OUTPUT: + name: expected/buffer_diamond.gml + type: vector + + - algorithm: qgis:rectanglesovalsdiamondsvariable + name: Oval buffer shape + params: + HEIGHT: id + INPUT: + name: custom/points_weighted.gml + type: vector + SEGMENTS: 36 + SHAPE: '2' + WIDTH: id + results: + OUTPUT: + name: expected/buffer_ovals.gml + type: vector - algorithm: qgis:creategridlines name: Lines grid 0.1 degree spacing From ebda2fd212eb68f1519ed557f0fc3913bd1eb23b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 02:46:22 +1000 Subject: [PATCH 042/364] Rename some enum values for clarity --- python/core/processing/qgsprocessing.sip | 8 +-- .../processing/qgsprocessingalgorithm.sip | 4 +- .../core/processing/qgsprocessingoutputs.sip | 8 +-- .../processing/qgsprocessingparameters.sip | 24 ++++----- .../processing/algs/qgis/CheckValidity.py | 6 +-- .../processing/algs/qgis/Datasources2Vrt.py | 2 +- .../algs/qgis/ExtractSpecificNodes.py | 2 +- python/plugins/processing/algs/qgis/Merge.py | 2 +- .../algs/qgis/OrientedMinimumBoundingBox.py | 2 +- .../algs/qgis/PointsLayerFromTable.py | 2 +- .../gui/BatchInputSelectionPanel.py | 4 +- python/plugins/processing/gui/TestTools.py | 2 +- python/plugins/processing/gui/wrappers.py | 20 ++++---- .../models/qgsprocessingmodelalgorithm.cpp | 4 +- src/core/processing/qgsnativealgorithms.cpp | 8 +-- src/core/processing/qgsnativealgorithms.h | 2 +- src/core/processing/qgsprocessing.h | 12 ++--- src/core/processing/qgsprocessingalgorithm.h | 2 +- src/core/processing/qgsprocessingoutputs.cpp | 6 +-- src/core/processing/qgsprocessingoutputs.h | 8 +-- .../processing/qgsprocessingparameters.cpp | 48 +++++++++--------- src/core/processing/qgsprocessingparameters.h | 28 +++++------ tests/src/core/testqgsprocessing.cpp | 50 +++++++++---------- 23 files changed, 127 insertions(+), 127 deletions(-) diff --git a/python/core/processing/qgsprocessing.sip b/python/core/processing/qgsprocessing.sip index 4775f0c5d2c..d3fbec02119 100644 --- a/python/core/processing/qgsprocessing.sip +++ b/python/core/processing/qgsprocessing.sip @@ -26,16 +26,16 @@ class QgsProcessing %End public: - enum LayerType + enum SourceType { - TypeAny, - TypeVectorAny, + TypeMapLayer, + TypeVectorAnyGeometry, TypeVectorPoint, TypeVectorLine, TypeVectorPolygon, TypeRaster, TypeFile, - TypeTable, + TypeVector, }; diff --git a/python/core/processing/qgsprocessingalgorithm.sip b/python/core/processing/qgsprocessingalgorithm.sip index be0c9b2d6ef..fe66f47111c 100644 --- a/python/core/processing/qgsprocessingalgorithm.sip +++ b/python/core/processing/qgsprocessingalgorithm.sip @@ -760,11 +760,11 @@ class QgsProcessingFeatureBasedAlgorithm : QgsProcessingAlgorithm :rtype: str %End - virtual QgsProcessing::LayerType outputLayerType() const; + virtual QgsProcessing::SourceType outputLayerType() const; %Docstring Returns the layer type for layers generated by this algorithm, if this is possible to determine in advance. - :rtype: QgsProcessing.LayerType + :rtype: QgsProcessing.SourceType %End virtual QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const; diff --git a/python/core/processing/qgsprocessingoutputs.sip b/python/core/processing/qgsprocessingoutputs.sip index 2cb435af229..9bce9f91549 100644 --- a/python/core/processing/qgsprocessingoutputs.sip +++ b/python/core/processing/qgsprocessingoutputs.sip @@ -108,7 +108,7 @@ class QgsProcessingOutputVectorLayer : QgsProcessingOutputDefinition %End public: - QgsProcessingOutputVectorLayer( const QString &name, const QString &description = QString(), QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny ); + QgsProcessingOutputVectorLayer( const QString &name, const QString &description = QString(), QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry ); %Docstring Constructor for QgsProcessingOutputVectorLayer. %End @@ -120,14 +120,14 @@ class QgsProcessingOutputVectorLayer : QgsProcessingOutputDefinition %End virtual QString type() const; - QgsProcessing::LayerType dataType() const; + QgsProcessing::SourceType dataType() const; %Docstring Returns the layer type for the output layer. .. seealso:: setDataType() - :rtype: QgsProcessing.LayerType + :rtype: QgsProcessing.SourceType %End - void setDataType( QgsProcessing::LayerType type ); + void setDataType( QgsProcessing::SourceType type ); %Docstring Sets the layer ``type`` for the output layer. .. seealso:: dataType() diff --git a/python/core/processing/qgsprocessingparameters.sip b/python/core/processing/qgsprocessingparameters.sip index 1fdd27566d1..7b2e185b2f7 100644 --- a/python/core/processing/qgsprocessingparameters.sip +++ b/python/core/processing/qgsprocessingparameters.sip @@ -975,7 +975,7 @@ class QgsProcessingParameterMultipleLayers : QgsProcessingParameterDefinition %End public: - QgsProcessingParameterMultipleLayers( const QString &name, const QString &description = QString(), QgsProcessing::LayerType layerType = QgsProcessing::TypeVectorAny, + QgsProcessingParameterMultipleLayers( const QString &name, const QString &description = QString(), QgsProcessing::SourceType layerType = QgsProcessing::TypeVectorAnyGeometry, const QVariant &defaultValue = QVariant(), bool optional = false ); %Docstring @@ -997,14 +997,14 @@ class QgsProcessingParameterMultipleLayers : QgsProcessingParameterDefinition virtual QString asScriptCode() const; - QgsProcessing::LayerType layerType() const; + QgsProcessing::SourceType layerType() const; %Docstring Returns the layer type for layers acceptable by the parameter. .. seealso:: setLayerType() - :rtype: QgsProcessing.LayerType + :rtype: QgsProcessing.SourceType %End - void setLayerType( QgsProcessing::LayerType type ); + void setLayerType( QgsProcessing::SourceType type ); %Docstring Sets the layer ``type`` for layers acceptable by the parameter. .. seealso:: layerType() @@ -1733,7 +1733,7 @@ class QgsProcessingParameterFeatureSink : QgsProcessingDestinationParameter %End public: - QgsProcessingParameterFeatureSink( const QString &name, const QString &description = QString(), QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny, const QVariant &defaultValue = QVariant(), + QgsProcessingParameterFeatureSink( const QString &name, const QString &description = QString(), QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry, const QVariant &defaultValue = QVariant(), bool optional = false ); %Docstring Constructor for QgsProcessingParameterFeatureSink. @@ -1758,11 +1758,11 @@ class QgsProcessingParameterFeatureSink : QgsProcessingDestinationParameter virtual QString defaultFileExtension() const; - QgsProcessing::LayerType dataType() const; + QgsProcessing::SourceType dataType() const; %Docstring Returns the layer type for sinks associated with the parameter. .. seealso:: setDataType() - :rtype: QgsProcessing.LayerType + :rtype: QgsProcessing.SourceType %End bool hasGeometry() const; @@ -1772,7 +1772,7 @@ class QgsProcessingParameterFeatureSink : QgsProcessingDestinationParameter :rtype: bool %End - void setDataType( QgsProcessing::LayerType type ); + void setDataType( QgsProcessing::SourceType type ); %Docstring Sets the layer ``type`` for the sinks associated with the parameter. .. seealso:: dataType() @@ -1812,7 +1812,7 @@ class QgsProcessingParameterVectorDestination : QgsProcessingDestinationParamete %End public: - QgsProcessingParameterVectorDestination( const QString &name, const QString &description = QString(), QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny, const QVariant &defaultValue = QVariant(), + QgsProcessingParameterVectorDestination( const QString &name, const QString &description = QString(), QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry, const QVariant &defaultValue = QVariant(), bool optional = false ); %Docstring Constructor for QgsProcessingParameterVectorDestination. @@ -1837,11 +1837,11 @@ class QgsProcessingParameterVectorDestination : QgsProcessingDestinationParamete virtual QString defaultFileExtension() const; - QgsProcessing::LayerType dataType() const; + QgsProcessing::SourceType dataType() const; %Docstring Returns the layer type for this created vector layer. .. seealso:: setDataType() - :rtype: QgsProcessing.LayerType + :rtype: QgsProcessing.SourceType %End bool hasGeometry() const; @@ -1851,7 +1851,7 @@ class QgsProcessingParameterVectorDestination : QgsProcessingDestinationParamete :rtype: bool %End - void setDataType( QgsProcessing::LayerType type ); + void setDataType( QgsProcessing::SourceType type ); %Docstring Sets the layer ``type`` for the created vector layer. .. seealso:: dataType() diff --git a/python/plugins/processing/algs/qgis/CheckValidity.py b/python/plugins/processing/algs/qgis/CheckValidity.py index df0fbac2191..619e26d9760 100644 --- a/python/plugins/processing/algs/qgis/CheckValidity.py +++ b/python/plugins/processing/algs/qgis/CheckValidity.py @@ -81,13 +81,13 @@ class CheckValidity(QgisAlgorithm): self.addParameter(QgsProcessingParameterEnum(self.METHOD, self.tr('Method'), self.methods)) - self.addParameter(QgsProcessingParameterFeatureSink(self.VALID_OUTPUT, self.tr('Valid output'), QgsProcessing.TypeVectorAny, '', True)) + self.addParameter(QgsProcessingParameterFeatureSink(self.VALID_OUTPUT, self.tr('Valid output'), QgsProcessing.TypeVectorAnyGeometry, '', True)) self.addOutput(QgsProcessingOutputNumber(self.VALID_COUNT, self.tr('Count of valid features'))) - self.addParameter(QgsProcessingParameterFeatureSink(self.INVALID_OUTPUT, self.tr('Invalid output'), QgsProcessing.TypeVectorAny, '', True)) + self.addParameter(QgsProcessingParameterFeatureSink(self.INVALID_OUTPUT, self.tr('Invalid output'), QgsProcessing.TypeVectorAnyGeometry, '', True)) self.addOutput(QgsProcessingOutputNumber(self.INVALID_COUNT, self.tr('Count of invalid features'))) - self.addParameter(QgsProcessingParameterFeatureSink(self.ERROR_OUTPUT, self.tr('Error output'), QgsProcessing.TypeVectorAny, '', True)) + self.addParameter(QgsProcessingParameterFeatureSink(self.ERROR_OUTPUT, self.tr('Error output'), QgsProcessing.TypeVectorAnyGeometry, '', True)) self.addOutput(QgsProcessingOutputNumber(self.ERROR_COUNT, self.tr('Count of errors'))) def name(self): diff --git a/python/plugins/processing/algs/qgis/Datasources2Vrt.py b/python/plugins/processing/algs/qgis/Datasources2Vrt.py index e0220a32854..ac158a9da52 100644 --- a/python/plugins/processing/algs/qgis/Datasources2Vrt.py +++ b/python/plugins/processing/algs/qgis/Datasources2Vrt.py @@ -56,7 +56,7 @@ class Datasources2Vrt(QgisAlgorithm): def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterMultipleLayers(self.INPUT, self.tr('Input datasources'), - QgsProcessing.TypeTable)) + QgsProcessing.TypeVector)) self.addParameter(QgsProcessingParameterBoolean(self.UNIONED, self.tr('Create "unioned" VRT'), defaultValue=False)) diff --git a/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py b/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py index 048aa7553bc..76c485a5f80 100644 --- a/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py +++ b/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py @@ -54,7 +54,7 @@ class ExtractSpecificNodes(QgisAlgorithm): def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Input layer'), [QgsProcessing.TypeVectorAny])) + self.tr('Input layer'), [QgsProcessing.TypeVectorAnyGeometry])) self.addParameter(QgsProcessingParameterString(self.NODES, self.tr('Node indices'), defaultValue='0')) diff --git a/python/plugins/processing/algs/qgis/Merge.py b/python/plugins/processing/algs/qgis/Merge.py index 56bdc5ed479..72252a3fd39 100644 --- a/python/plugins/processing/algs/qgis/Merge.py +++ b/python/plugins/processing/algs/qgis/Merge.py @@ -60,7 +60,7 @@ class Merge(QgisAlgorithm): def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterMultipleLayers(self.LAYERS, self.tr('Layers to merge'), - QgsProcessing.TypeVectorAny)) + QgsProcessing.TypeVectorAnyGeometry)) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Merged'))) diff --git a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py b/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py index aa767cc54e3..c3b1914bab1 100644 --- a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py +++ b/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py @@ -56,7 +56,7 @@ class OrientedMinimumBoundingBox(QgisAlgorithm): def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Input layer'), [QgsProcessing.TypeVectorAny])) + self.tr('Input layer'), [QgsProcessing.TypeVectorAnyGeometry])) self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE, self.tr('Calculate bounds for each feature separately'), defaultValue=True)) diff --git a/python/plugins/processing/algs/qgis/PointsLayerFromTable.py b/python/plugins/processing/algs/qgis/PointsLayerFromTable.py index 54e20fc2041..2597acdf0c9 100644 --- a/python/plugins/processing/algs/qgis/PointsLayerFromTable.py +++ b/python/plugins/processing/algs/qgis/PointsLayerFromTable.py @@ -64,7 +64,7 @@ class PointsLayerFromTable(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'), types=[QgsProcessing.TypeTable])) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'), types=[QgsProcessing.TypeVector])) self.addParameter(QgsProcessingParameterField(self.XFIELD, self.tr('X field'), parentLayerParameterName=self.INPUT, type=QgsProcessingParameterField.Any)) diff --git a/python/plugins/processing/gui/BatchInputSelectionPanel.py b/python/plugins/processing/gui/BatchInputSelectionPanel.py index 0a14f876c7c..8c94aee1dde 100644 --- a/python/plugins/processing/gui/BatchInputSelectionPanel.py +++ b/python/plugins/processing/gui/BatchInputSelectionPanel.py @@ -109,13 +109,13 @@ class BatchInputSelectionPanel(QWidget): elif isinstance(self.param, QgsProcessingParameterVectorLayer): layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance()) else: - datatypes = [QgsProcessing.TypeVectorAny] + datatypes = [QgsProcessing.TypeVectorAnyGeometry] if isinstance(self.param, QgsProcessingParameterFeatureSource): datatypes = self.param.dataTypes() elif isinstance(self.param, QgsProcessingParameterMultipleLayers): datatypes = [self.param.layerType()] - if QgsProcessing.TypeVectorAny not in datatypes: + if QgsProcessing.TypeVectorAnyGeometry not in datatypes: layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), datatypes) else: layers = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance()) diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index 420ae5fc264..4d986d7ea3d 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -184,7 +184,7 @@ def createTest(text): # Handle datatype detection dataType = param.layerType() - if dataType in [QgsProcessing.TypeVectorAny, QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorLine, QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeTable]: + if dataType in [QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorLine, QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeVector]: dataType = 'vector' else: dataType = 'raster' diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 6373dd26fc8..6d4afe46fa7 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -524,20 +524,20 @@ class FixedTableWidgetWrapper(WidgetWrapper): class MultipleInputWidgetWrapper(WidgetWrapper): def _getOptions(self): - if self.param.layerType() == QgsProcessing.TypeVectorAny: + if self.param.layerType() == QgsProcessing.TypeVectorAnyGeometry: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer) elif self.param.layerType() == QgsProcessing.TypeVectorPoint: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer, - [QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorAny]) + [QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorAnyGeometry]) elif self.param.layerType() == QgsProcessing.TypeVectorLine: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer, - [QgsProcessing.TypeVectorLine, QgsProcessing.TypeVectorAny]) + [QgsProcessing.TypeVectorLine, QgsProcessing.TypeVectorAnyGeometry]) elif self.param.layerType() == QgsProcessing.TypeVectorPolygon: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputVectorLayer, - [QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeVectorAny]) + [QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeVectorAnyGeometry]) elif self.param.layerType() == QgsProcessing.TypeRaster: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterRasterLayer, QgsProcessingParameterMultipleLayers), QgsProcessingOutputRasterLayer) - elif self.param.layerType() == QgsProcessing.TypeTable: + elif self.param.layerType() == QgsProcessing.TypeVector: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), OutputTable) else: options = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFile, OutputFile) @@ -551,7 +551,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): else: if self.param.layerType() == QgsProcessing.TypeRaster: options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False) - elif self.param.layerType() in (QgsProcessing.TypeVectorAny, QgsProcessing.TypeTable): + elif self.param.layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) else: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], False) @@ -569,7 +569,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): if self.param.layerType() != QgsProcessing.TypeFile: if self.param.layerType() == QgsProcessing.TypeRaster: options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False) - elif self.param.layerType() in (QgsProcessing.TypeVectorAny, QgsProcessing.TypeTable): + elif self.param.layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) else: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], False) @@ -605,7 +605,7 @@ class MultipleInputWidgetWrapper(WidgetWrapper): else: if self.param.layerType() == QgsProcessing.TypeRaster: options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False) - elif self.param.layerType() in (QgsProcessing.TypeVectorAny, QgsProcessing.TypeTable): + elif self.param.layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) else: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], False) @@ -827,7 +827,7 @@ class VectorWidgetWrapper(WidgetWrapper): widget.setLayout(vl) filters = QgsMapLayerProxyModel.Filters() - if QgsProcessing.TypeVectorAny in self.param.dataTypes() or len(self.param.dataTypes()) == 0: + if QgsProcessing.TypeVectorAnyGeometry in self.param.dataTypes() or len(self.param.dataTypes()) == 0: filters = QgsMapLayerProxyModel.HasGeometry if QgsProcessing.TypeVectorPoint in self.param.dataTypes(): filters |= QgsMapLayerProxyModel.PointLayer @@ -1107,7 +1107,7 @@ class TableWidgetWrapper(WidgetWrapper): self.combo.setAllowEmptyLayer(True) filters = QgsMapLayerProxyModel.Filters() - if QgsProcessing.TypeVectorAny in self.param.dataTypes() or len(self.param.dataTypes()) == 0: + if QgsProcessing.TypeVectorAnyGeometry in self.param.dataTypes() or len(self.param.dataTypes()) == 0: filters = QgsMapLayerProxyModel.VectorLayer if QgsProcessing.TypeVectorPoint in self.param.dataTypes(): filters |= QgsMapLayerProxyModel.PointLayer diff --git a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp index 2d8a62d5fe9..76b1b673ae3 100644 --- a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp +++ b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp @@ -596,7 +596,7 @@ QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSo bool ok = !sourceDef->dataTypes().isEmpty(); Q_FOREACH ( int type, sourceDef->dataTypes() ) { - if ( dataTypes.contains( type ) || type == QgsProcessing::TypeAny ) + if ( dataTypes.contains( type ) || type == QgsProcessing::TypeMapLayer ) { ok = true; break; @@ -636,7 +636,7 @@ QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSo if ( out->type() == QgsProcessingOutputVectorLayer::typeName() ) { const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out ); - if ( !( dataTypes.contains( vectorOut->dataType() ) || vectorOut->dataType() == QgsProcessing::TypeAny ) ) + if ( !( dataTypes.contains( vectorOut->dataType() ) || vectorOut->dataType() == QgsProcessing::TypeMapLayer ) ) { continue; } diff --git a/src/core/processing/qgsnativealgorithms.cpp b/src/core/processing/qgsnativealgorithms.cpp index ff531c800f7..1854fb6214a 100644 --- a/src/core/processing/qgsnativealgorithms.cpp +++ b/src/core/processing/qgsnativealgorithms.cpp @@ -708,7 +708,7 @@ void QgsExtractByExpressionAlgorithm::initAlgorithm( const QVariantMap & ) addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Matching features" ) ) ); QgsProcessingParameterFeatureSink *failOutput = new QgsProcessingParameterFeatureSink( QStringLiteral( "FAIL_OUTPUT" ), QObject::tr( "Non-matching" ), - QgsProcessing::TypeVectorAny, QVariant(), true ); + QgsProcessing::TypeVectorAnyGeometry, QVariant(), true ); failOutput->setCreateByDefault( false ); addParameter( failOutput ); } @@ -837,7 +837,7 @@ void QgsExtractByAttributeAlgorithm::initAlgorithm( const QVariantMap & ) addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted (attribute)" ) ) ); QgsProcessingParameterFeatureSink *failOutput = new QgsProcessingParameterFeatureSink( QStringLiteral( "FAIL_OUTPUT" ), QObject::tr( "Extracted (non-matching)" ), - QgsProcessing::TypeVectorAny, QVariant(), true ); + QgsProcessing::TypeVectorAnyGeometry, QVariant(), true ); failOutput->setCreateByDefault( false ); addParameter( failOutput ); } @@ -1019,9 +1019,9 @@ void QgsRemoveNullGeometryAlgorithm::initAlgorithm( const QVariantMap & ) addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) ); addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Non null geometries" ), - QgsProcessing::TypeVectorAny, QVariant(), true ) ); + QgsProcessing::TypeVectorAnyGeometry, QVariant(), true ) ); QgsProcessingParameterFeatureSink *nullOutput = new QgsProcessingParameterFeatureSink( QStringLiteral( "NULL_OUTPUT" ), QObject::tr( "Null geometries" ), - QgsProcessing::TypeTable, QVariant(), true ); + QgsProcessing::TypeVector, QVariant(), true ); nullOutput->setCreateByDefault( false ); addParameter( nullOutput ); } diff --git a/src/core/processing/qgsnativealgorithms.h b/src/core/processing/qgsnativealgorithms.h index f0485ac8131..d849d10524e 100644 --- a/src/core/processing/qgsnativealgorithms.h +++ b/src/core/processing/qgsnativealgorithms.h @@ -65,7 +65,7 @@ class QgsCentroidAlgorithm : public QgsProcessingFeatureBasedAlgorithm protected: QString outputName() const override { return QObject::tr( "Centroids" ); } - QgsProcessing::LayerType outputLayerType() const override { return QgsProcessing::TypeVectorPoint; } + QgsProcessing::SourceType outputLayerType() const override { return QgsProcessing::TypeVectorPoint; } QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override { Q_UNUSED( inputWkbType ); return QgsWkbTypes::Point; } QgsFeature processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) override; diff --git a/src/core/processing/qgsprocessing.h b/src/core/processing/qgsprocessing.h index 497c693ae60..a2f9bbbef0a 100644 --- a/src/core/processing/qgsprocessing.h +++ b/src/core/processing/qgsprocessing.h @@ -41,17 +41,17 @@ class CORE_EXPORT QgsProcessing public: - //! Layer types enum - enum LayerType + //! Data source types enum + enum SourceType { - TypeAny = -2, //!< Any layer - TypeVectorAny = -1, //!< Any vector layer with geometry + TypeMapLayer = -2, //!< Any map layer type (raster or vector) + TypeVectorAnyGeometry = -1, //!< Any vector layer with geometry TypeVectorPoint = 0, //!< Vector point layers TypeVectorLine = 1, //!< Vector line layers TypeVectorPolygon = 2, //!< Vector polygon layers TypeRaster = 3, //!< Raster layers - TypeFile = 4, //!< Files - TypeTable = 5, //!< Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink has no geometry. + TypeFile = 4, //!< Files (i.e. non map layer sources, such as text files) + TypeVector = 5, //!< Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink has no geometry. }; diff --git a/src/core/processing/qgsprocessingalgorithm.h b/src/core/processing/qgsprocessingalgorithm.h index 13f69a05156..6c0a901530c 100644 --- a/src/core/processing/qgsprocessingalgorithm.h +++ b/src/core/processing/qgsprocessingalgorithm.h @@ -754,7 +754,7 @@ class CORE_EXPORT QgsProcessingFeatureBasedAlgorithm : public QgsProcessingAlgor * Returns the layer type for layers generated by this algorithm, if * this is possible to determine in advance. */ - virtual QgsProcessing::LayerType outputLayerType() const { return QgsProcessing::TypeVectorAny; } + virtual QgsProcessing::SourceType outputLayerType() const { return QgsProcessing::TypeVectorAnyGeometry; } /** * Maps the input WKB geometry type (\a inputWkbType) to the corresponding diff --git a/src/core/processing/qgsprocessingoutputs.cpp b/src/core/processing/qgsprocessingoutputs.cpp index 940cbc5667c..c891bcd7733 100644 --- a/src/core/processing/qgsprocessingoutputs.cpp +++ b/src/core/processing/qgsprocessingoutputs.cpp @@ -24,17 +24,17 @@ QgsProcessingOutputDefinition::QgsProcessingOutputDefinition( const QString &nam } -QgsProcessingOutputVectorLayer::QgsProcessingOutputVectorLayer( const QString &name, const QString &description, QgsProcessing::LayerType type ) +QgsProcessingOutputVectorLayer::QgsProcessingOutputVectorLayer( const QString &name, const QString &description, QgsProcessing::SourceType type ) : QgsProcessingOutputDefinition( name, description ) , mDataType( type ) {} -QgsProcessing::LayerType QgsProcessingOutputVectorLayer::dataType() const +QgsProcessing::SourceType QgsProcessingOutputVectorLayer::dataType() const { return mDataType; } -void QgsProcessingOutputVectorLayer::setDataType( QgsProcessing::LayerType type ) +void QgsProcessingOutputVectorLayer::setDataType( QgsProcessing::SourceType type ) { mDataType = type; } diff --git a/src/core/processing/qgsprocessingoutputs.h b/src/core/processing/qgsprocessingoutputs.h index 139ba90e6db..641a37f22a6 100644 --- a/src/core/processing/qgsprocessingoutputs.h +++ b/src/core/processing/qgsprocessingoutputs.h @@ -128,7 +128,7 @@ class CORE_EXPORT QgsProcessingOutputVectorLayer : public QgsProcessingOutputDef /** * Constructor for QgsProcessingOutputVectorLayer. */ - QgsProcessingOutputVectorLayer( const QString &name, const QString &description = QString(), QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny ); + QgsProcessingOutputVectorLayer( const QString &name, const QString &description = QString(), QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry ); /** * Returns the type name for the output class. @@ -140,17 +140,17 @@ class CORE_EXPORT QgsProcessingOutputVectorLayer : public QgsProcessingOutputDef * Returns the layer type for the output layer. * \see setDataType() */ - QgsProcessing::LayerType dataType() const; + QgsProcessing::SourceType dataType() const; /** * Sets the layer \a type for the output layer. * \see dataType() */ - void setDataType( QgsProcessing::LayerType type ); + void setDataType( QgsProcessing::SourceType type ); private: - QgsProcessing::LayerType mDataType = QgsProcessing::TypeVectorAny; + QgsProcessing::SourceType mDataType = QgsProcessing::TypeVectorAnyGeometry; }; /** diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 2860fba4b16..d9bb6361e0c 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -1424,7 +1424,7 @@ QgsProcessingParameterMatrix *QgsProcessingParameterMatrix::fromScriptCode( cons return new QgsProcessingParameterMatrix( name, description, 0, false, QStringList(), definition.isEmpty() ? QVariant() : definition, isOptional ); } -QgsProcessingParameterMultipleLayers::QgsProcessingParameterMultipleLayers( const QString &name, const QString &description, QgsProcessing::LayerType layerType, const QVariant &defaultValue, bool optional ) +QgsProcessingParameterMultipleLayers::QgsProcessingParameterMultipleLayers( const QString &name, const QString &description, QgsProcessing::SourceType layerType, const QVariant &defaultValue, bool optional ) : QgsProcessingParameterDefinition( name, description, defaultValue, optional ) , mLayerType( layerType ) { @@ -1562,12 +1562,12 @@ QString QgsProcessingParameterMultipleLayers::asScriptCode() const return code.trimmed(); } -QgsProcessing::LayerType QgsProcessingParameterMultipleLayers::layerType() const +QgsProcessing::SourceType QgsProcessingParameterMultipleLayers::layerType() const { return mLayerType; } -void QgsProcessingParameterMultipleLayers::setLayerType( QgsProcessing::LayerType type ) +void QgsProcessingParameterMultipleLayers::setLayerType( QgsProcessing::SourceType type ) { mLayerType = type; } @@ -1594,7 +1594,7 @@ QVariantMap QgsProcessingParameterMultipleLayers::toVariantMap() const bool QgsProcessingParameterMultipleLayers::fromVariantMap( const QVariantMap &map ) { QgsProcessingParameterDefinition::fromVariantMap( map ); - mLayerType = static_cast< QgsProcessing::LayerType >( map.value( QStringLiteral( "layer_type" ) ).toInt() ); + mLayerType = static_cast< QgsProcessing::SourceType >( map.value( QStringLiteral( "layer_type" ) ).toInt() ); mMinimumNumberInputs = map.value( QStringLiteral( "min_inputs" ) ).toInt(); return true; } @@ -1610,9 +1610,9 @@ QgsProcessingParameterMultipleLayers *QgsProcessingParameterMultipleLayers::from type = m.captured( 1 ).toLower().trimmed(); defaultVal = m.captured( 2 ); } - QgsProcessing::LayerType layerType = QgsProcessing::TypeVectorAny; + QgsProcessing::SourceType layerType = QgsProcessing::TypeVectorAnyGeometry; if ( type == QStringLiteral( "vector" ) ) - layerType = QgsProcessing::TypeVectorAny; + layerType = QgsProcessing::TypeVectorAnyGeometry; else if ( type == QStringLiteral( "raster" ) ) layerType = QgsProcessing::TypeRaster; else if ( type == QStringLiteral( "file" ) ) @@ -2663,7 +2663,7 @@ QgsProcessingParameterFeatureSource *QgsProcessingParameterFeatureSource::fromSc return new QgsProcessingParameterFeatureSource( name, description, types, def, isOptional ); } -QgsProcessingParameterFeatureSink::QgsProcessingParameterFeatureSink( const QString &name, const QString &description, QgsProcessing::LayerType type, const QVariant &defaultValue, bool optional ) +QgsProcessingParameterFeatureSink::QgsProcessingParameterFeatureSink( const QString &name, const QString &description, QgsProcessing::SourceType type, const QVariant &defaultValue, bool optional ) : QgsProcessingDestinationParameter( name, description, defaultValue, optional ) , mDataType( type ) { @@ -2743,7 +2743,7 @@ QString QgsProcessingParameterFeatureSink::asScriptCode() const code += QStringLiteral( "polygon " ); break; - case QgsProcessing::TypeTable: + case QgsProcessing::TypeVector: code += QStringLiteral( "table " ); break; @@ -2773,7 +2773,7 @@ QString QgsProcessingParameterFeatureSink::defaultFileExtension() const } } -QgsProcessing::LayerType QgsProcessingParameterFeatureSink::dataType() const +QgsProcessing::SourceType QgsProcessingParameterFeatureSink::dataType() const { return mDataType; } @@ -2782,12 +2782,12 @@ bool QgsProcessingParameterFeatureSink::hasGeometry() const { switch ( mDataType ) { - case QgsProcessing::TypeAny: - case QgsProcessing::TypeVectorAny: + case QgsProcessing::TypeMapLayer: + case QgsProcessing::TypeVectorAnyGeometry: case QgsProcessing::TypeVectorPoint: case QgsProcessing::TypeVectorLine: case QgsProcessing::TypeVectorPolygon: - case QgsProcessing::TypeTable: + case QgsProcessing::TypeVector: return true; case QgsProcessing::TypeRaster: @@ -2797,7 +2797,7 @@ bool QgsProcessingParameterFeatureSink::hasGeometry() const return true; } -void QgsProcessingParameterFeatureSink::setDataType( QgsProcessing::LayerType type ) +void QgsProcessingParameterFeatureSink::setDataType( QgsProcessing::SourceType type ) { mDataType = type; } @@ -2812,7 +2812,7 @@ QVariantMap QgsProcessingParameterFeatureSink::toVariantMap() const bool QgsProcessingParameterFeatureSink::fromVariantMap( const QVariantMap &map ) { QgsProcessingDestinationParameter::fromVariantMap( map ); - mDataType = static_cast< QgsProcessing::LayerType >( map.value( QStringLiteral( "data_type" ) ).toInt() ); + mDataType = static_cast< QgsProcessing::SourceType >( map.value( QStringLiteral( "data_type" ) ).toInt() ); return true; } @@ -2826,7 +2826,7 @@ QString QgsProcessingParameterFeatureSink::generateTemporaryDestination() const QgsProcessingParameterFeatureSink *QgsProcessingParameterFeatureSink::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) { - QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny; + QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry; QString def = definition; if ( def.startsWith( QStringLiteral( "point" ), Qt::CaseInsensitive ) ) { @@ -2845,7 +2845,7 @@ QgsProcessingParameterFeatureSink *QgsProcessingParameterFeatureSink::fromScript } else if ( def.startsWith( QStringLiteral( "table" ), Qt::CaseInsensitive ) ) { - type = QgsProcessing::TypeTable; + type = QgsProcessing::TypeVector; def = def.mid( 6 ); } @@ -3116,7 +3116,7 @@ void QgsProcessingDestinationParameter::setCreateByDefault( bool createByDefault mCreateByDefault = createByDefault; } -QgsProcessingParameterVectorDestination::QgsProcessingParameterVectorDestination( const QString &name, const QString &description, QgsProcessing::LayerType type, const QVariant &defaultValue, bool optional ) +QgsProcessingParameterVectorDestination::QgsProcessingParameterVectorDestination( const QString &name, const QString &description, QgsProcessing::SourceType type, const QVariant &defaultValue, bool optional ) : QgsProcessingDestinationParameter( name, description, defaultValue, optional ) , mDataType( type ) { @@ -3222,7 +3222,7 @@ QString QgsProcessingParameterVectorDestination::defaultFileExtension() const } } -QgsProcessing::LayerType QgsProcessingParameterVectorDestination::dataType() const +QgsProcessing::SourceType QgsProcessingParameterVectorDestination::dataType() const { return mDataType; } @@ -3231,12 +3231,12 @@ bool QgsProcessingParameterVectorDestination::hasGeometry() const { switch ( mDataType ) { - case QgsProcessing::TypeAny: - case QgsProcessing::TypeVectorAny: + case QgsProcessing::TypeMapLayer: + case QgsProcessing::TypeVectorAnyGeometry: case QgsProcessing::TypeVectorPoint: case QgsProcessing::TypeVectorLine: case QgsProcessing::TypeVectorPolygon: - case QgsProcessing::TypeTable: + case QgsProcessing::TypeVector: return true; case QgsProcessing::TypeRaster: @@ -3246,7 +3246,7 @@ bool QgsProcessingParameterVectorDestination::hasGeometry() const return true; } -void QgsProcessingParameterVectorDestination::setDataType( QgsProcessing::LayerType type ) +void QgsProcessingParameterVectorDestination::setDataType( QgsProcessing::SourceType type ) { mDataType = type; } @@ -3261,13 +3261,13 @@ QVariantMap QgsProcessingParameterVectorDestination::toVariantMap() const bool QgsProcessingParameterVectorDestination::fromVariantMap( const QVariantMap &map ) { QgsProcessingDestinationParameter::fromVariantMap( map ); - mDataType = static_cast< QgsProcessing::LayerType >( map.value( QStringLiteral( "data_type" ) ).toInt() ); + mDataType = static_cast< QgsProcessing::SourceType >( map.value( QStringLiteral( "data_type" ) ).toInt() ); return true; } QgsProcessingParameterVectorDestination *QgsProcessingParameterVectorDestination::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) { - QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny; + QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry; QString def = definition; if ( def.startsWith( QStringLiteral( "point" ), Qt::CaseInsensitive ) ) { diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index 4b944e7460a..80fed2eee49 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -952,7 +952,7 @@ class CORE_EXPORT QgsProcessingParameterMultipleLayers : public QgsProcessingPar /** * Constructor for QgsProcessingParameterMultipleLayers. */ - QgsProcessingParameterMultipleLayers( const QString &name, const QString &description = QString(), QgsProcessing::LayerType layerType = QgsProcessing::TypeVectorAny, + QgsProcessingParameterMultipleLayers( const QString &name, const QString &description = QString(), QgsProcessing::SourceType layerType = QgsProcessing::TypeVectorAnyGeometry, const QVariant &defaultValue = QVariant(), bool optional = false ); @@ -970,13 +970,13 @@ class CORE_EXPORT QgsProcessingParameterMultipleLayers : public QgsProcessingPar * Returns the layer type for layers acceptable by the parameter. * \see setLayerType() */ - QgsProcessing::LayerType layerType() const; + QgsProcessing::SourceType layerType() const; /** * Sets the layer \a type for layers acceptable by the parameter. * \see layerType() */ - void setLayerType( QgsProcessing::LayerType type ); + void setLayerType( QgsProcessing::SourceType type ); /** * Returns the minimum number of layers required for the parameter. If the return value is < 1 @@ -1002,7 +1002,7 @@ class CORE_EXPORT QgsProcessingParameterMultipleLayers : public QgsProcessingPar private: - QgsProcessing::LayerType mLayerType = QgsProcessing::TypeVectorAny; + QgsProcessing::SourceType mLayerType = QgsProcessing::TypeVectorAnyGeometry; int mMinimumNumberInputs = 0; }; @@ -1401,7 +1401,7 @@ class CORE_EXPORT QgsProcessingParameterVectorLayer : public QgsProcessingParame private: - QList< int > mDataTypes = QList< int >() << QgsProcessing::TypeVectorAny; + QList< int > mDataTypes = QList< int >() << QgsProcessing::TypeVectorAnyGeometry; }; @@ -1546,7 +1546,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSource : public QgsProcessingPara private: - QList< int > mDataTypes = QList< int >() << QgsProcessing::TypeVectorAny; + QList< int > mDataTypes = QList< int >() << QgsProcessing::TypeVectorAnyGeometry; }; @@ -1641,7 +1641,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingDestin /** * Constructor for QgsProcessingParameterFeatureSink. */ - QgsProcessingParameterFeatureSink( const QString &name, const QString &description = QString(), QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny, const QVariant &defaultValue = QVariant(), + QgsProcessingParameterFeatureSink( const QString &name, const QString &description = QString(), QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry, const QVariant &defaultValue = QVariant(), bool optional = false ); /** @@ -1660,7 +1660,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingDestin * Returns the layer type for sinks associated with the parameter. * \see setDataType() */ - QgsProcessing::LayerType dataType() const; + QgsProcessing::SourceType dataType() const; /** * Returns true if sink is likely to include geometries. In cases were presence of geometry @@ -1672,7 +1672,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingDestin * Sets the layer \a type for the sinks associated with the parameter. * \see dataType() */ - void setDataType( QgsProcessing::LayerType type ); + void setDataType( QgsProcessing::SourceType type ); QVariantMap toVariantMap() const override; bool fromVariantMap( const QVariantMap &map ) override; @@ -1685,7 +1685,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingDestin private: - QgsProcessing::LayerType mDataType = QgsProcessing::TypeVectorAny; + QgsProcessing::SourceType mDataType = QgsProcessing::TypeVectorAnyGeometry; }; @@ -1706,7 +1706,7 @@ class CORE_EXPORT QgsProcessingParameterVectorDestination : public QgsProcessing /** * Constructor for QgsProcessingParameterVectorDestination. */ - QgsProcessingParameterVectorDestination( const QString &name, const QString &description = QString(), QgsProcessing::LayerType type = QgsProcessing::TypeVectorAny, const QVariant &defaultValue = QVariant(), + QgsProcessingParameterVectorDestination( const QString &name, const QString &description = QString(), QgsProcessing::SourceType type = QgsProcessing::TypeVectorAnyGeometry, const QVariant &defaultValue = QVariant(), bool optional = false ); /** @@ -1725,7 +1725,7 @@ class CORE_EXPORT QgsProcessingParameterVectorDestination : public QgsProcessing * Returns the layer type for this created vector layer. * \see setDataType() */ - QgsProcessing::LayerType dataType() const; + QgsProcessing::SourceType dataType() const; /** * Returns true if the created layer is likely to include geometries. In cases were presence of geometry @@ -1737,7 +1737,7 @@ class CORE_EXPORT QgsProcessingParameterVectorDestination : public QgsProcessing * Sets the layer \a type for the created vector layer. * \see dataType() */ - void setDataType( QgsProcessing::LayerType type ); + void setDataType( QgsProcessing::SourceType type ); QVariantMap toVariantMap() const override; bool fromVariantMap( const QVariantMap &map ) override; @@ -1750,7 +1750,7 @@ class CORE_EXPORT QgsProcessingParameterVectorDestination : public QgsProcessing private: - QgsProcessing::LayerType mDataType = QgsProcessing::TypeVectorAny; + QgsProcessing::SourceType mDataType = QgsProcessing::TypeVectorAnyGeometry; }; /** diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index f64b8496799..7ea91298df0 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -2194,7 +2194,7 @@ void TestQgsProcessing::parameterLayerList() context.setProject( &p ); // not optional! - std::unique_ptr< QgsProcessingParameterMultipleLayers > def( new QgsProcessingParameterMultipleLayers( "non_optional", QString(), QgsProcessing::TypeAny, QString(), false ) ); + std::unique_ptr< QgsProcessingParameterMultipleLayers > def( new QgsProcessingParameterMultipleLayers( "non_optional", QString(), QgsProcessing::TypeMapLayer, QString(), false ) ); QVERIFY( !def->checkValueIsAcceptable( false ) ); QVERIFY( !def->checkValueIsAcceptable( true ) ); QVERIFY( !def->checkValueIsAcceptable( 5 ) ); @@ -2288,7 +2288,7 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) ); QCOMPARE( fromCode->flags(), def->flags() ); QVERIFY( !fromCode->defaultValue().isValid() ); - QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAny ); + QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAnyGeometry ); QVariantMap map = def->toVariantMap(); QgsProcessingParameterMultipleLayers fromMap( "x" ); @@ -2303,7 +2303,7 @@ void TestQgsProcessing::parameterLayerList() QVERIFY( dynamic_cast< QgsProcessingParameterMultipleLayers *>( def.get() ) ); // optional with one default layer - def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeAny, v1->id(), true ) ); + def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, v1->id(), true ) ); QVERIFY( !def->checkValueIsAcceptable( false ) ); QVERIFY( !def->checkValueIsAcceptable( true ) ); QVERIFY( !def->checkValueIsAcceptable( 5 ) ); @@ -2335,10 +2335,10 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) ); QCOMPARE( fromCode->flags(), def->flags() ); QCOMPARE( fromCode->defaultValue(), def->defaultValue() ); - QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAny ); + QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAnyGeometry ); // optional with two default layers - def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeAny, QVariantList() << v1->id() << r1->publicSource(), true ) ); + def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, QVariantList() << v1->id() << r1->publicSource(), true ) ); params.insert( "optional", QVariant() ); QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << r1 ); @@ -2350,14 +2350,14 @@ void TestQgsProcessing::parameterLayerList() QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) ); QCOMPARE( fromCode->flags(), def->flags() ); QCOMPARE( fromCode->defaultValue().toString(), v1->id() + "," + r1->publicSource() ); - QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAny ); + QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAnyGeometry ); // optional with one default direct layer - def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeAny, QVariant::fromValue( v1 ), true ) ); + def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, QVariant::fromValue( v1 ), true ) ); QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 ); // optional with two default direct layers - def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeAny, QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( r1 ), true ) ); + def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( r1 ), true ) ); QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << r1 ); def.reset( new QgsProcessingParameterMultipleLayers( "type", QString(), QgsProcessing::TypeRaster ) ); @@ -3395,7 +3395,7 @@ void TestQgsProcessing::parameterFeatureSource() context.setProject( &p ); // not optional! - std::unique_ptr< QgsProcessingParameterFeatureSource > def( new QgsProcessingParameterFeatureSource( "non_optional", QString(), QList< int >() << QgsProcessing::TypeVectorAny, QString(), false ) ); + std::unique_ptr< QgsProcessingParameterFeatureSource > def( new QgsProcessingParameterFeatureSource( "non_optional", QString(), QList< int >() << QgsProcessing::TypeVectorAnyGeometry, QString(), false ) ); QVERIFY( !def->checkValueIsAcceptable( false ) ); QVERIFY( !def->checkValueIsAcceptable( true ) ); QVERIFY( !def->checkValueIsAcceptable( 5 ) ); @@ -3482,7 +3482,7 @@ void TestQgsProcessing::parameterFeatureSource() // optional - def.reset( new QgsProcessingParameterFeatureSource( "optional", QString(), QList< int >() << QgsProcessing::TypeVectorAny, v1->id(), true ) ); + def.reset( new QgsProcessingParameterFeatureSource( "optional", QString(), QList< int >() << QgsProcessing::TypeVectorAnyGeometry, v1->id(), true ) ); params.insert( "optional", QVariant() ); QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() ); QVERIFY( def->checkValueIsAcceptable( false ) ); @@ -3505,7 +3505,7 @@ void TestQgsProcessing::parameterFeatureSource() //optional with direct layer default - def.reset( new QgsProcessingParameterFeatureSource( "optional", QString(), QList< int >() << QgsProcessing::TypeVectorAny, QVariant::fromValue( v1 ), true ) ); + def.reset( new QgsProcessingParameterFeatureSource( "optional", QString(), QList< int >() << QgsProcessing::TypeVectorAnyGeometry, QVariant::fromValue( v1 ), true ) ); QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() ); } @@ -3518,7 +3518,7 @@ void TestQgsProcessing::parameterFeatureSink() context.setProject( &p ); // not optional! - std::unique_ptr< QgsProcessingParameterFeatureSink > def( new QgsProcessingParameterFeatureSink( "non_optional", QString(), QgsProcessing::TypeVectorAny, QString(), false ) ); + std::unique_ptr< QgsProcessingParameterFeatureSink > def( new QgsProcessingParameterFeatureSink( "non_optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), false ) ); QVERIFY( !def->checkValueIsAcceptable( false ) ); QVERIFY( !def->checkValueIsAcceptable( true ) ); QVERIFY( !def->checkValueIsAcceptable( 5 ) ); @@ -3580,14 +3580,14 @@ void TestQgsProcessing::parameterFeatureSink() QCOMPARE( code, QStringLiteral( "##non_optional=sink polygon" ) ); fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) ); QCOMPARE( fromCode->dataType(), def->dataType() ); - def->setDataType( QgsProcessing::TypeTable ); + def->setDataType( QgsProcessing::TypeVector ); code = def->asScriptCode(); QCOMPARE( code, QStringLiteral( "##non_optional=sink table" ) ); fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) ); QCOMPARE( fromCode->dataType(), def->dataType() ); // optional - def.reset( new QgsProcessingParameterFeatureSink( "optional", QString(), QgsProcessing::TypeVectorAny, QString(), true ) ); + def.reset( new QgsProcessingParameterFeatureSink( "optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), true ) ); QVERIFY( !def->checkValueIsAcceptable( false ) ); QVERIFY( !def->checkValueIsAcceptable( true ) ); QVERIFY( !def->checkValueIsAcceptable( 5 ) ); @@ -3610,14 +3610,14 @@ void TestQgsProcessing::parameterFeatureSink() QVERIFY( !def->createByDefault() ); // test hasGeometry - QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeAny ).hasGeometry() ); - QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorAny ).hasGeometry() ); + QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeMapLayer ).hasGeometry() ); + QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorAnyGeometry ).hasGeometry() ); QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorPoint ).hasGeometry() ); QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorLine ).hasGeometry() ); QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorPolygon ).hasGeometry() ); QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeRaster ).hasGeometry() ); QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeFile ).hasGeometry() ); - QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeTable ).hasGeometry() ); + QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVector ).hasGeometry() ); } @@ -3630,7 +3630,7 @@ void TestQgsProcessing::parameterVectorOut() context.setProject( &p ); // not optional! - std::unique_ptr< QgsProcessingParameterVectorDestination > def( new QgsProcessingParameterVectorDestination( "non_optional", QString(), QgsProcessing::TypeVectorAny, QString(), false ) ); + std::unique_ptr< QgsProcessingParameterVectorDestination > def( new QgsProcessingParameterVectorDestination( "non_optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), false ) ); QVERIFY( !def->checkValueIsAcceptable( false ) ); QVERIFY( !def->checkValueIsAcceptable( true ) ); QVERIFY( !def->checkValueIsAcceptable( 5 ) ); @@ -3691,7 +3691,7 @@ void TestQgsProcessing::parameterVectorOut() QCOMPARE( fromCode->dataType(), def->dataType() ); // optional - def.reset( new QgsProcessingParameterVectorDestination( "optional", QString(), QgsProcessing::TypeVectorAny, QString(), true ) ); + def.reset( new QgsProcessingParameterVectorDestination( "optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), true ) ); QVERIFY( !def->checkValueIsAcceptable( false ) ); QVERIFY( !def->checkValueIsAcceptable( true ) ); QVERIFY( !def->checkValueIsAcceptable( 5 ) ); @@ -3711,14 +3711,14 @@ void TestQgsProcessing::parameterVectorOut() QCOMPARE( fromCode->dataType(), def->dataType() ); // test hasGeometry - QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeAny ).hasGeometry() ); - QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorAny ).hasGeometry() ); + QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeMapLayer ).hasGeometry() ); + QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorAnyGeometry ).hasGeometry() ); QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorPoint ).hasGeometry() ); QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorLine ).hasGeometry() ); QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorPolygon ).hasGeometry() ); QVERIFY( !QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeRaster ).hasGeometry() ); QVERIFY( !QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeFile ).hasGeometry() ); - QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeTable ).hasGeometry() ); + QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVector ).hasGeometry() ); } @@ -4200,7 +4200,7 @@ void TestQgsProcessing::processingFeatureSink() // non optional sink - def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeAny, QVariant(), false ) ); + def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeMapLayer, QVariant(), false ) ); QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "memory:test" ) ) ); QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "memory:test" ) ) ); QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( "memory:test" ) ) ); @@ -4212,7 +4212,7 @@ void TestQgsProcessing::processingFeatureSink() QVERIFY( sink.get() ); // optional sink - def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeAny, QVariant(), true ) ); + def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeMapLayer, QVariant(), true ) ); QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "memory:test" ) ) ); QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "memory:test" ) ) ); QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( "memory:test" ) ) ); @@ -4228,7 +4228,7 @@ void TestQgsProcessing::processingFeatureSink() QVERIFY( !sink.get() ); //.... unless there's a default set - def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeAny, QStringLiteral( "memory:defaultlayer" ), true ) ); + def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeMapLayer, QStringLiteral( "memory:defaultlayer" ), true ) ); params.insert( QStringLiteral( "layer" ), QVariant() ); sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3113" ), context, dest ) ); QVERIFY( sink.get() ); From 370b26798f459e4aabea8a174346b13b3f90b8ea Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 03:24:28 +1000 Subject: [PATCH 043/364] Fix wrapped c++ object has been deleted error when editing model parameters Fixes #16858 --- .../processing/modeler/ModelerGraphicItem.py | 3 +-- .../ModelerParameterDefinitionDialog.py | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/python/plugins/processing/modeler/ModelerGraphicItem.py b/python/plugins/processing/modeler/ModelerGraphicItem.py index 30166be1b08..0012beed9d1 100644 --- a/python/plugins/processing/modeler/ModelerGraphicItem.py +++ b/python/plugins/processing/modeler/ModelerGraphicItem.py @@ -195,8 +195,7 @@ class ModelerGraphicItem(QGraphicsItem): if isinstance(self.element, QgsProcessingModelParameter): dlg = ModelerParameterDefinitionDialog(self.model, param=self.model.parameterDefinition(self.element.parameterName())) - dlg.exec_() - if dlg.param is not None: + if dlg.exec_() and dlg.param is not None: self.model.removeModelParameter(self.element.parameterName()) self.element.setParameterName(dlg.param.name()) self.element.setDescription(dlg.param.name()) diff --git a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py index 30859731a57..14bb89f21fc 100644 --- a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py +++ b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py @@ -297,15 +297,15 @@ class ModelerParameterDefinitionDialog(QDialog): self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.buttonBox.setObjectName('buttonBox') - self.buttonBox.accepted.connect(self.okPressed) - self.buttonBox.rejected.connect(self.cancelPressed) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) self.verticalLayout.addStretch() self.verticalLayout.addWidget(self.buttonBox) self.setLayout(self.verticalLayout) - def okPressed(self): + def accept(self): description = str(self.nameTextBox.text()) if description.strip() == '': QMessageBox.warning(self, self.tr('Unable to define parameter'), @@ -401,8 +401,16 @@ class ModelerParameterDefinitionDialog(QDialog): self.param = QgsProcessingParameterCrs(name, description, self.selector.crs().authid()) if not self.requiredCheck.isChecked(): self.param.setFlags(self.param.flags() | QgsProcessingParameterDefinition.FlagOptional) - self.close() - def cancelPressed(self): + settings = QgsSettings() + settings.setValue("/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) + + QDialog.accept(self) + + def reject(self): self.param = None - self.close() + + settings = QgsSettings() + settings.setValue("/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) + + QDialog.reject(self) From 7d69e5f5afbe8647f19429ff28c3a56079f411c7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 03:36:33 +1000 Subject: [PATCH 044/364] Expose all layer type filters to both model feature source, vector layer and multi layer inputs Refs #17030 --- .../ModelerParameterDefinitionDialog.py | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py index 14bb89f21fc..c69ab3baf4b 100644 --- a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py +++ b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py @@ -30,6 +30,7 @@ import math from qgis.gui import QgsExpressionLineEdit, QgsProjectionSelectionWidget from qgis.core import (QgsSettings, + QgsProcessing, QgsCoordinateReferenceSystem, QgsProcessingParameterDefinition, QgsProcessingParameterBoolean, @@ -184,29 +185,32 @@ class ModelerParameterDefinitionDialog(QDialog): self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) - elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or - isinstance(self.param, QgsProcessingParameterFeatureSource)): - self.verticalLayout.addWidget(QLabel(self.tr('Shape type'))) + elif (self.paramType in (ModelerParameterDefinitionDialog.PARAMETER_VECTOR, ModelerParameterDefinitionDialog.PARAMETER_TABLE) or + isinstance(self.param, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer))): + self.verticalLayout.addWidget(QLabel(self.tr('Geometry type'))) self.shapetypeCombo = QComboBox() - self.shapetypeCombo.addItem(self.tr('Any')) - self.shapetypeCombo.addItem(self.tr('Point')) - self.shapetypeCombo.addItem(self.tr('Line')) - self.shapetypeCombo.addItem(self.tr('Polygon')) + self.shapetypeCombo.addItem(self.tr('Geometry Not Required'), QgsProcessing.TypeVector) + self.shapetypeCombo.addItem(self.tr('Point'), QgsProcessing.TypeVectorPoint) + self.shapetypeCombo.addItem(self.tr('Line'), QgsProcessing.TypeVectorLine) + self.shapetypeCombo.addItem(self.tr('Polygon'), QgsProcessing.TypeVectorPolygon) + self.shapetypeCombo.addItem(self.tr('Any Geometry Type'), QgsProcessing.TypeVectorAnyGeometry) if self.param is not None: - self.shapetypeCombo.setCurrentIndex(self.param.dataTypes()[0] + 1) + self.shapetypeCombo.setCurrentIndex(self.shapetypeCombo.findData(self.param.dataTypes()[0])) self.verticalLayout.addWidget(self.shapetypeCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or isinstance(self.param, QgsProcessingParameterMultipleLayers)): self.verticalLayout.addWidget(QLabel(self.tr('Data type'))) self.datatypeCombo = QComboBox() - self.datatypeCombo.addItem(self.tr('Vector (any)')) - self.datatypeCombo.addItem(self.tr('Vector (point)')) - self.datatypeCombo.addItem(self.tr('Vector (line)')) - self.datatypeCombo.addItem(self.tr('Vector (polygon)')) - self.datatypeCombo.addItem(self.tr('Raster')) - self.datatypeCombo.addItem(self.tr('File')) + self.datatypeCombo.addItem(self.tr('Any Map Layer'), QgsProcessing.TypeMapLayer) + self.datatypeCombo.addItem(self.tr('Vector (No Geometry Required)'), QgsProcessing.TypeVector) + self.datatypeCombo.addItem(self.tr('Vector (Point)'), QgsProcessing.TypeVectorPoint) + self.datatypeCombo.addItem(self.tr('Vector (Line)'), QgsProcessing.TypeVectorLine) + self.datatypeCombo.addItem(self.tr('Vector (Polygon)'), QgsProcessing.TypeVectorPolygon) + self.datatypeCombo.addItem(self.tr('Vector (Any Geometry Type)'), QgsProcessing.TypeVectorAnyGeometry) + self.datatypeCombo.addItem(self.tr('Raster'), QgsProcessing.TypeRaster) + self.datatypeCombo.addItem(self.tr('File'), QgsProcessing.TypeFile) if self.param is not None: - self.datatypeCombo.setCurrentIndex(self.param.layerType() + 1) + self.datatypeCombo.setCurrentIndex(self.datatypeCombo.findData(self.param.layerType())) self.verticalLayout.addWidget(self.datatypeCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or isinstance(self.param, QgsProcessingParameterNumber)): @@ -349,17 +353,18 @@ class ModelerParameterDefinitionDialog(QDialog): elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE or isinstance(self.param, QgsProcessingParameterVectorLayer)): self.param = QgsProcessingParameterVectorLayer( - name, description) + name, description, + [self.shapetypeCombo.currentData()]) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or isinstance(self.param, QgsProcessingParameterFeatureSource)): self.param = QgsProcessingParameterFeatureSource( name, description, - [self.shapetypeCombo.currentIndex() - 1]) + [self.shapetypeCombo.currentData()]) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or isinstance(self.param, QgsProcessingParameterMultipleLayers)): self.param = QgsProcessingParameterMultipleLayers( name, description, - self.datatypeCombo.currentIndex() - 1) + self.datatypeCombo.currentData()) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or isinstance(self.param, QgsProcessingParameterNumber)): try: From 355cff191d931e47292e6fe88e78e9dfd2274e97 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 03:45:51 +1000 Subject: [PATCH 045/364] Add Map Layer input parameter type for models Since algorithms now use this parameter type (render map alg) we also need to allow its use in models --- .../modeler/ModelerParameterDefinitionDialog.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py index c69ab3baf4b..47b41f23bef 100644 --- a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py +++ b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py @@ -78,7 +78,8 @@ class ModelerParameterDefinitionDialog(QDialog): PARAMETER_POINT = 'Point' PARAMETER_CRS = 'CRS' PARAMETER_MULTIPLE = 'Multiple Input' - PARAMETER_BAND = 'Raster band' + PARAMETER_BAND = 'Raster Band' + PARAMETER_MAP_LAYER = 'Map Layer' paramTypes = [ PARAMETER_BOOLEAN, @@ -88,6 +89,7 @@ class ModelerParameterDefinitionDialog(QDialog): PARAMETER_RASTER, PARAMETER_STRING, PARAMETER_EXPRESSION, + PARAMETER_MAP_LAYER, PARAMETER_TABLE, PARAMETER_TABLE_FIELD, PARAMETER_VECTOR, @@ -346,6 +348,10 @@ class ModelerParameterDefinitionDialog(QDialog): return parent = self.parentCombo.currentData() self.param = QgsProcessingParameterBand(name, description, None, parent) + elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MAP_LAYER or + isinstance(self.param, QgsProcessingParameterMapLayer)): + self.param = QgsProcessingParameterMapLayer( + name, description) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_RASTER or isinstance(self.param, QgsProcessingParameterRasterLayer)): self.param = QgsProcessingParameterRasterLayer( From a07ea33340094aa6873c4c1ebdf1136b0ecd673a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 04:03:50 +1000 Subject: [PATCH 046/364] Port IDW Interpolation alg to new API --- .../processing/algs/qgis/IdwInterpolation.py | 169 ++++++++---------- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- .../tests/testdata/qgis_algorithm_tests.yaml | 68 +++---- 3 files changed, 112 insertions(+), 130 deletions(-) diff --git a/python/plugins/processing/algs/qgis/IdwInterpolation.py b/python/plugins/processing/algs/qgis/IdwInterpolation.py index 462ddf2fa9e..97d7371668c 100644 --- a/python/plugins/processing/algs/qgis/IdwInterpolation.py +++ b/python/plugins/processing/algs/qgis/IdwInterpolation.py @@ -31,23 +31,58 @@ from qgis.PyQt.QtGui import QIcon from qgis.core import (QgsRectangle, QgsProcessingUtils, - QgsProcessingParameterDefinition) + QgsProcessingParameterDefinition, + QgsProcessingParameterNumber, + QgsProcessingParameterExtent, + QgsProcessingParameterRasterDestination, + QgsProcessingException) from qgis.analysis import (QgsInterpolator, QgsIDWInterpolator, - QgsGridFileWriter - ) + QgsGridFileWriter) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import (Parameter, - ParameterNumber, - ParameterExtent, - _splitParameterOptions) -from processing.core.outputs import OutputRaster pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] +class ParameterInterpolationData(QgsProcessingParameterDefinition): + + def __init__(self, name='', description=''): + super().__init__(name, description) + self.setMetadata({ + 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper' + }) + + def type(self): + return 'idw_interpolation_data' + + def clone(self): + return ParameterInterpolationData(self.name(), self.description()) + + @staticmethod + def parseValue(value): + if value is None: + return None + + if value == '': + return None + + if isinstance(value, str): + return value if value != '' else None + else: + return ParameterInterpolationData.dataToString(value) + + @staticmethod + def dataToString(data): + s = '' + for c in data: + s += '{}, {}, {:d}, {:d};'.format(c[0], + c[1], + c[2], + c[3]) + return s[:-1] + + class IdwInterpolation(QgisAlgorithm): INTERPOLATION_DATA = 'INTERPOLATION_DATA' @@ -57,7 +92,7 @@ class IdwInterpolation(QgisAlgorithm): CELLSIZE_X = 'CELLSIZE_X' CELLSIZE_Y = 'CELLSIZE_Y' EXTENT = 'EXTENT' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' def icon(self): return QIcon(os.path.join(pluginPath, 'images', 'interpolation.png')) @@ -69,78 +104,29 @@ class IdwInterpolation(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - class ParameterInterpolationData(Parameter): - default_metadata = { - 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper' - } - - def __init__(self, name='', description=''): - Parameter.__init__(self, name, description) - - def setValue(self, value): - if value is None: - if not self.flags() & QgsProcessingParameterDefinition.FlagOptional: - return False - self.value = None - return True - - if value == '': - if not self.flags() & QgsProcessingParameterDefinition.FlagOptional: - return False - - if isinstance(value, str): - self.value = value if value != '' else None - else: - self.value = ParameterInterpolationData.dataToString(value) - return True - - def getValueAsCommandLineParameter(self): - return '"{}"'.format(self.value) - - def getAsScriptCode(self): - param_type = '' - param_type += 'interpolation data ' - return '##' + self.name + '=' + param_type - - @classmethod - def fromScriptCode(self, line): - isOptional, name, definition = _splitParameterOptions(line) - descName = QgsProcessingParameters.descriptionFromName(name) - parent = definition.lower().strip()[len('interpolation data') + 1:] - return ParameterInterpolationData(name, descName, parent) - - @staticmethod - def dataToString(data): - s = '' - for c in data: - s += '{}, {}, {:d}, {:d};'.format(c[0], - c[1], - c[2], - c[3]) - return s[:-1] self.addParameter(ParameterInterpolationData(self.INTERPOLATION_DATA, self.tr('Input layer(s)'))) - self.addParameter(ParameterNumber(self.DISTANCE_COEFFICIENT, - self.tr('Distance coefficient P'), - 0.0, 99.99, 2.0)) - self.addParameter(ParameterNumber(self.COLUMNS, - self.tr('Number of columns'), - 0, 10000000, 300)) - self.addParameter(ParameterNumber(self.ROWS, - self.tr('Number of rows'), - 0, 10000000, 300)) - self.addParameter(ParameterNumber(self.CELLSIZE_X, - self.tr('Cell Size X'), - 0.0, 999999.000000, 0.0)) - self.addParameter(ParameterNumber(self.CELLSIZE_Y, - self.tr('Cell Size Y'), - 0.0, 999999.000000, 0.0)) - self.addParameter(ParameterExtent(self.EXTENT, - self.tr('Extent'), - optional=False)) - self.addOutput(OutputRaster(self.OUTPUT_LAYER, - self.tr('Interpolated'))) + self.addParameter(QgsProcessingParameterNumber(self.DISTANCE_COEFFICIENT, + self.tr('Distance coefficient P'), type=QgsProcessingParameterNumber.Double, + minValue=0.0, maxValue=99.99, defaultValue=2.0)) + self.addParameter(QgsProcessingParameterNumber(self.COLUMNS, + self.tr('Number of columns'), + minValue=0, maxValue=10000000, defaultValue=300)) + self.addParameter(QgsProcessingParameterNumber(self.ROWS, + self.tr('Number of rows'), + minValue=0, maxValue=10000000, defaultValue=300)) + self.addParameter(QgsProcessingParameterNumber(self.CELLSIZE_X, + self.tr('Cell Size X'), type=QgsProcessingParameterNumber.Double, + minValue=0.0, maxValue=999999.000000, defaultValue=0.0)) + self.addParameter(QgsProcessingParameterNumber(self.CELLSIZE_Y, + self.tr('Cell Size Y'), type=QgsProcessingParameterNumber.Double, + minValue=0.0, maxValue=999999.000000, defaultValue=0.0)) + self.addParameter(QgsProcessingParameterExtent(self.EXTENT, + self.tr('Extent'), + optional=False)) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, + self.tr('Interpolated'))) def name(self): return 'idwinterpolation' @@ -149,25 +135,19 @@ class IdwInterpolation(QgisAlgorithm): return self.tr('IDW interpolation') def processAlgorithm(self, parameters, context, feedback): - interpolationData = self.getParameterValue(self.INTERPOLATION_DATA) - coefficient = self.getParameterValue(self.DISTANCE_COEFFICIENT) - columns = self.getParameterValue(self.COLUMNS) - rows = self.getParameterValue(self.ROWS) - cellsizeX = self.getParameterValue(self.CELLSIZE_X) - cellsizeY = self.getParameterValue(self.CELLSIZE_Y) - extent = self.getParameterValue(self.EXTENT).split(',') - output = self.getOutputValue(self.OUTPUT_LAYER) + interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA]) + coefficient = self.parameterAsDouble(parameters, self.DISTANCE_COEFFICIENT, context) + columns = self.parameterAsInt(parameters, self.COLUMNS, context) + rows = self.parameterAsInt(parameters, self.ROWS, context) + cellsizeX = self.parameterAsDouble(parameters, self.CELLSIZE_X, context) + cellsizeY = self.parameterAsDouble(parameters, self.CELLSIZE_Y, context) + bbox = self.parameterAsExtent(parameters, self.EXTENT, context) + output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) if interpolationData is None: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('You need to specify at least one input layer.')) - xMin = float(extent[0]) - xMax = float(extent[1]) - yMin = float(extent[2]) - yMax = float(extent[3]) - bbox = QgsRectangle(xMin, yMin, xMax, yMax) - layerData = [] layers = [] for row in interpolationData.split(';'): @@ -201,3 +181,4 @@ class IdwInterpolation(QgisAlgorithm): cellsizeY) writer.writeFile() + return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index a601bfe6813..1be400aaf66 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -83,6 +83,7 @@ from .HubDistanceLines import HubDistanceLines from .HubDistancePoints import HubDistancePoints from .HubLines import HubLines from .HypsometricCurves import HypsometricCurves +from .IdwInterpolation import IdwInterpolation from .ImportIntoPostGIS import ImportIntoPostGIS from .ImportIntoSpatialite import ImportIntoSpatialite from .Intersection import Intersection @@ -168,7 +169,6 @@ from .ZonalStatistics import ZonalStatistics # from .FieldPyculator import FieldsPyculator # from .SelectByAttributeSum import SelectByAttributeSum # from .DefineProjection import DefineProjection -# from .IdwInterpolation import IdwInterpolation # from .TinInterpolation import TinInterpolation # from .RasterCalculator import RasterCalculator # from .ExecuteSQL import ExecuteSQL @@ -194,7 +194,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # FieldsPyculator(), # FieldsMapper(), SelectByAttributeSum() # DefineProjection(), - # IdwInterpolation(), TinInterpolation(), + # TinInterpolation(), # RasterCalculator(), # ExecuteSQL(), FindProjection(), # ] @@ -241,6 +241,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): HubDistancePoints(), HubLines(), HypsometricCurves(), + IdwInterpolation(), ImportIntoPostGIS(), ImportIntoSpatialite(), Intersection(), diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 42be1f3c818..0cb2a2a73c5 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -1494,40 +1494,40 @@ tests: name: expected/multipoint_delaunay.gml type: vector -# - algorithm: qgis:idwinterpolation -# name: IDW interpolation using attribute -# params: -# CELLSIZE_X: 0.02667 -# CELLSIZE_Y: 0.02667 -# COLUMNS: 300 -# DISTANCE_COEFFICIENT: 2.0 -# EXTENT: 0, 8, -5, 3 -# INTERPOLATION_DATA: -# name: pointsz.gml,False,1,0 -# type: interpolation -# ROWS: 300 -# results: -# OUTPUT_LAYER: -# hash: 56d2671d50444f8571affba3f9e585830b82af5e380394178f521065 -# type: rasterhash -# -# - algorithm: qgis:idwinterpolation -# name: IDW interpolation using Z value -# params: -# CELLSIZE_X: 0.02667 -# CELLSIZE_Y: 0.02667 -# COLUMNS: 300 -# DISTANCE_COEFFICIENT: 2.0 -# EXTENT: 0, 8, -5, 3 -# INTERPOLATION_DATA: -# name: pointsz.gml,True,-1,0 -# type: interpolation -# ROWS: 300 -# results: -# OUTPUT_LAYER: -# hash: 56d2671d50444f8571affba3f9e585830b82af5e380394178f521065 -# type: rasterhash -# + - algorithm: qgis:idwinterpolation + name: IDW interpolation using attribute + params: + CELLSIZE_X: 0.02667 + CELLSIZE_Y: 0.02667 + COLUMNS: 300 + DISTANCE_COEFFICIENT: 2.0 + EXTENT: 0, 8, -5, 3 + INTERPOLATION_DATA: + name: pointsz.gml,False,1,0 + type: interpolation + ROWS: 300 + results: + OUTPUT: + hash: 56d2671d50444f8571affba3f9e585830b82af5e380394178f521065 + type: rasterhash + + - algorithm: qgis:idwinterpolation + name: IDW interpolation using Z value + params: + CELLSIZE_X: 0.02667 + CELLSIZE_Y: 0.02667 + COLUMNS: 300 + DISTANCE_COEFFICIENT: 2.0 + EXTENT: 0, 8, -5, 3 + INTERPOLATION_DATA: + name: pointsz.gml,True,-1,0 + type: interpolation + ROWS: 300 + results: + OUTPUT: + hash: 56d2671d50444f8571affba3f9e585830b82af5e380394178f521065 + type: rasterhash + # - algorithm: qgis:tininterpolation # name: TIN interpolation using attribute # params: From 356588abf0d6c3f44f97f2ba635c668f836f735e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 04:08:04 +1000 Subject: [PATCH 047/364] Use QgsFeedback in QgsGridFileWriter instead of QProgressDialog --- doc/api_break.dox | 7 +++++++ .../interpolation/qgsgridfilewriter.sip | 4 ++-- .../processing/algs/qgis/IdwInterpolation.py | 2 +- .../interpolation/qgsgridfilewriter.cpp | 18 +++++------------- src/analysis/interpolation/qgsgridfilewriter.h | 5 +++-- src/server/qgsinterpolationlayerbuilder.cpp | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/doc/api_break.dox b/doc/api_break.dox index f5e5e9e493f..0ff3b52c16c 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -1348,6 +1348,13 @@ QgsGraduatedRenderer {#qgis_api_break_3_0_QgsGraduatedRenderer} - setInvertedColorRamp() and invertedColorRamp() functions are gone, QgsColorRamp now responsible for invert - createRenderer() and updateColorRamp()'s inverted parameter is gone + +QgsGridFileWriter {#qgis_api_break_3_0_QgsGridFileWriter} +----------------- + +- writeFile() now takes an optional QgsFeedback argument instead of using a QProgressDialog + + QgsGroupWMSDataDialog {#qgis_api_break_3_0_QgsGroupWMSDataDialog} --------------------- diff --git a/python/analysis/interpolation/qgsgridfilewriter.sip b/python/analysis/interpolation/qgsgridfilewriter.sip index d781a406875..e7f308658cb 100644 --- a/python/analysis/interpolation/qgsgridfilewriter.sip +++ b/python/analysis/interpolation/qgsgridfilewriter.sip @@ -20,10 +20,10 @@ class QgsGridFileWriter QgsGridFileWriter( QgsInterpolator *i, const QString &outputPath, const QgsRectangle &extent, int nCols, int nRows, double cellSizeX, double cellSizeY ); - int writeFile( bool showProgressDialog = false ); + int writeFile( QgsFeedback *feedback = 0 ); %Docstring Writes the grid file. -\param showProgressDialog shows a dialog with the possibility to cancel +\param feedback optional feedback object for progress reports and cancelation support :return: 0 in case of success* :rtype: int %End diff --git a/python/plugins/processing/algs/qgis/IdwInterpolation.py b/python/plugins/processing/algs/qgis/IdwInterpolation.py index 97d7371668c..658e5f91623 100644 --- a/python/plugins/processing/algs/qgis/IdwInterpolation.py +++ b/python/plugins/processing/algs/qgis/IdwInterpolation.py @@ -180,5 +180,5 @@ class IdwInterpolation(QgisAlgorithm): cellsizeX, cellsizeY) - writer.writeFile() + writer.writeFile(feedback) return {self.OUTPUT: output} diff --git a/src/analysis/interpolation/qgsgridfilewriter.cpp b/src/analysis/interpolation/qgsgridfilewriter.cpp index 0a6a4ed9500..50e2bba5e0c 100644 --- a/src/analysis/interpolation/qgsgridfilewriter.cpp +++ b/src/analysis/interpolation/qgsgridfilewriter.cpp @@ -18,9 +18,9 @@ #include "qgsgridfilewriter.h" #include "qgsinterpolator.h" #include "qgsvectorlayer.h" +#include "qgsfeedback.h" #include #include -#include QgsGridFileWriter::QgsGridFileWriter( QgsInterpolator *i, const QString &outputPath, const QgsRectangle &extent, int nCols, int nRows, double cellSizeX, double cellSizeY ) : mInterpolator( i ) @@ -44,7 +44,7 @@ QgsGridFileWriter::QgsGridFileWriter() } -int QgsGridFileWriter::writeFile( bool showProgressDialog ) +int QgsGridFileWriter::writeFile( QgsFeedback *feedback ) { QFile outputFile( mOutputFilePath ); @@ -67,13 +67,6 @@ int QgsGridFileWriter::writeFile( bool showProgressDialog ) double currentXValue; double interpolatedValue; - QProgressDialog *progressDialog = nullptr; - if ( showProgressDialog ) - { - progressDialog = new QProgressDialog( QObject::tr( "Interpolating..." ), QObject::tr( "Abort" ), 0, mNumRows, nullptr ); - progressDialog->setWindowModality( Qt::WindowModal ); - } - for ( int i = 0; i < mNumRows; ++i ) { currentXValue = mInterpolationExtent.xMinimum() + mCellSizeX / 2.0; //calculate value in the center of the cell @@ -92,14 +85,14 @@ int QgsGridFileWriter::writeFile( bool showProgressDialog ) outStream << endl; currentYValue -= mCellSizeY; - if ( showProgressDialog ) + if ( feedback ) { - if ( progressDialog->wasCanceled() ) + if ( feedback->isCanceled() ) { outputFile.remove(); return 3; } - progressDialog->setValue( i ); + feedback->setProgress( 100.0 * i / static_cast< double >( mNumRows ) ); } } @@ -120,7 +113,6 @@ int QgsGridFileWriter::writeFile( bool showProgressDialog ) prjStream << endl; prjFile.close(); - delete progressDialog; return 0; } diff --git a/src/analysis/interpolation/qgsgridfilewriter.h b/src/analysis/interpolation/qgsgridfilewriter.h index d2da7901926..770bc5f6975 100644 --- a/src/analysis/interpolation/qgsgridfilewriter.h +++ b/src/analysis/interpolation/qgsgridfilewriter.h @@ -24,6 +24,7 @@ #include "qgis_analysis.h" class QgsInterpolator; +class QgsFeedback; /** \ingroup analysis * A class that does interpolation to a grid and writes the results to an ascii grid*/ @@ -34,10 +35,10 @@ class ANALYSIS_EXPORT QgsGridFileWriter QgsGridFileWriter( QgsInterpolator *i, const QString &outputPath, const QgsRectangle &extent, int nCols, int nRows, double cellSizeX, double cellSizeY ); /** Writes the grid file. - \param showProgressDialog shows a dialog with the possibility to cancel + \param feedback optional feedback object for progress reports and cancelation support \returns 0 in case of success*/ - int writeFile( bool showProgressDialog = false ); + int writeFile( QgsFeedback *feedback = nullptr ); private: diff --git a/src/server/qgsinterpolationlayerbuilder.cpp b/src/server/qgsinterpolationlayerbuilder.cpp index 0097ccc845d..25a5b9a6408 100644 --- a/src/server/qgsinterpolationlayerbuilder.cpp +++ b/src/server/qgsinterpolationlayerbuilder.cpp @@ -140,7 +140,7 @@ QgsMapLayer *QgsInterpolationLayerBuilder::createMapLayer( const QDomElement &el QgsRectangle extent = mVectorLayer->extent(); QgsGridFileWriter gridWriter( interpolator, tmpFile->fileName(), extent, nCols, nRows, extent.width() / nCols, extent.height() / nRows ); - if ( gridWriter.writeFile( false ) != 0 ) + if ( gridWriter.writeFile() != 0 ) { QgsDebugMsg( "Interpolation of raster failed" ); return nullptr; From 4bba95fc79bb2a12a03dbff4056fc6a6dbdee14e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 04:56:35 +1000 Subject: [PATCH 048/364] Fix broken TIN interpolation (loss of point z coordinates) --- .../interpolation/CloughTocherInterpolator.cc | 2 - .../interpolation/CloughTocherInterpolator.h | 60 ++++++++++++------- .../interpolation/DualEdgeTriangulation.cc | 22 +++---- .../interpolation/NormVecDecorator.cc | 1 - .../interpolation/qgstininterpolator.cpp | 2 +- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/analysis/interpolation/CloughTocherInterpolator.cc b/src/analysis/interpolation/CloughTocherInterpolator.cc index 0a3694c2390..c15bdfe2b70 100644 --- a/src/analysis/interpolation/CloughTocherInterpolator.cc +++ b/src/analysis/interpolation/CloughTocherInterpolator.cc @@ -164,7 +164,6 @@ bool CloughTocherInterpolator::calcNormVec( double x, double y, Vector3D *result return true; } - QgsDebugMsg( "warning, point outside the triangle" ); result->setX( 0 );//return a vertical normal if failed result->setY( 0 ); result->setZ( 1 ); @@ -240,7 +239,6 @@ bool CloughTocherInterpolator::calcPoint( double x, double y, QgsPoint *result ) } else { - QgsDebugMsg( "warning, point outside the triangle" ); result->setX( x ); result->setY( y ); result->setZ( 0 ); diff --git a/src/analysis/interpolation/CloughTocherInterpolator.h b/src/analysis/interpolation/CloughTocherInterpolator.h index c0660ecd943..1687733107f 100644 --- a/src/analysis/interpolation/CloughTocherInterpolator.h +++ b/src/analysis/interpolation/CloughTocherInterpolator.h @@ -33,27 +33,43 @@ class ANALYSIS_EXPORT CloughTocherInterpolator : public TriangleInterpolator //! Tolerance of the barycentric coordinates at the borders of the triangles (to prevent errors because of very small negativ baricentric coordinates) double mEdgeTolerance; //! First point of the triangle in x-,y-,z-coordinates - QgsPoint point1; + QgsPoint point1 = QgsPoint( 0, 0, 0 ); //! Second point of the triangle in x-,y-,z-coordinates - QgsPoint point2; + QgsPoint point2 = QgsPoint( 0, 0, 0 ); //! Third point of the triangle in x-,y-,z-coordinates - QgsPoint point3; - QgsPoint cp1; - QgsPoint cp2; - QgsPoint cp3; - QgsPoint cp4; - QgsPoint cp5; - QgsPoint cp6; - QgsPoint cp7; - QgsPoint cp8; - QgsPoint cp9; - QgsPoint cp10; - QgsPoint cp11; - QgsPoint cp12; - QgsPoint cp13; - QgsPoint cp14; - QgsPoint cp15; - QgsPoint cp16; + QgsPoint point3 = QgsPoint( 0, 0, 0 ); + //! Control point 1 + QgsPoint cp1 = QgsPoint( 0, 0, 0 ); + //! Control point 2 + QgsPoint cp2 = QgsPoint( 0, 0, 0 ); + //! Control point 3 + QgsPoint cp3 = QgsPoint( 0, 0, 0 ); + //! Control point 4 + QgsPoint cp4 = QgsPoint( 0, 0, 0 ); + //! Control point 5 + QgsPoint cp5 = QgsPoint( 0, 0, 0 ); + //! Control point 6 + QgsPoint cp6 = QgsPoint( 0, 0, 0 ); + //! Control point 7 + QgsPoint cp7 = QgsPoint( 0, 0, 0 ); + //! Control point 8 + QgsPoint cp8 = QgsPoint( 0, 0, 0 ); + //! Control point 9 + QgsPoint cp9 = QgsPoint( 0, 0, 0 ); + //! Control point 10 + QgsPoint cp10 = QgsPoint( 0, 0, 0 ); + //! Control point 11 + QgsPoint cp11 = QgsPoint( 0, 0, 0 ); + //! Control point 12 + QgsPoint cp12 = QgsPoint( 0, 0, 0 ); + //! Control point 13 + QgsPoint cp13 = QgsPoint( 0, 0, 0 ); + //! Control point 14 + QgsPoint cp14 = QgsPoint( 0, 0, 0 ); + //! Control point 15 + QgsPoint cp15 = QgsPoint( 0, 0, 0 ); + //! Control point 16 + QgsPoint cp16 = QgsPoint( 0, 0, 0 ); //! Derivative in x-direction at point1 double der1X; //! Derivative in y-direction at point1 @@ -67,11 +83,11 @@ class ANALYSIS_EXPORT CloughTocherInterpolator : public TriangleInterpolator //! Derivative in y-direction at point3 double der3Y; //! Stores point1 of the last run - QgsPoint lpoint1; + QgsPoint lpoint1 = QgsPoint( 0, 0, 0 ); //! Stores point2 of the last run - QgsPoint lpoint2; + QgsPoint lpoint2 = QgsPoint( 0, 0, 0 ); //! Stores point3 of the last run - QgsPoint lpoint3; + QgsPoint lpoint3 = QgsPoint( 0, 0, 0 ); //! Finds out, in which triangle the point with the coordinates x and y is void init( double x, double y ); //! Calculates the Bernsteinpolynomials to calculate the Beziertriangle. 'n' is three in the cubical case, 'i', 'j', 'k' are the indices of the controllpoint and 'u', 'v', 'w' are the barycentric coordinates of the point diff --git a/src/analysis/interpolation/DualEdgeTriangulation.cc b/src/analysis/interpolation/DualEdgeTriangulation.cc index d4cfe02adba..4e15fb7663b 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.cc +++ b/src/analysis/interpolation/DualEdgeTriangulation.cc @@ -976,7 +976,6 @@ bool DualEdgeTriangulation::getTriangle( double x, double y, QgsPoint *p1, int * int edge = baseEdgeOfTriangle( &point ); if ( edge == -10 )//the point is outside the convex hull { - QgsDebugMsg( "edge outside the convex hull" ); return false; } @@ -1094,7 +1093,6 @@ bool DualEdgeTriangulation::getTriangle( double x, double y, QgsPoint *p1, QgsPo int edge = baseEdgeOfTriangle( &point ); if ( edge == -10 )//the point is outside the convex hull { - QgsDebugMsg( "edge outside the convex hull" ); return false; } else if ( edge >= 0 )//the point is inside the convex hull @@ -1178,14 +1176,12 @@ bool DualEdgeTriangulation::getTriangle( double x, double y, QgsPoint *p1, QgsPo } else//problems { - QgsDebugMsg( QString( "problems: the edge is: %1" ).arg( edge ) ); return false; } } else { - QgsDebugMsg( "warning, null pointer" ); return false; } } @@ -1270,7 +1266,7 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) { if ( mHalfEdge[mHalfEdge[actedge]->getNext()]->getForced() && mForcedCrossBehavior == Triangulation::SnappingTypeVertex )//if the crossed edge is a forced edge, we have to snap the forced line to the next node { - QgsPoint crosspoint; + QgsPoint crosspoint( 0, 0, 0 ); int p3, p4; p3 = mHalfEdge[mHalfEdge[actedge]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[actedge]->getNext()]->getDual()]->getPoint(); @@ -1292,7 +1288,7 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) } else if ( mHalfEdge[mHalfEdge[actedge]->getNext()]->getForced() && mForcedCrossBehavior == Triangulation::InsertVertex )//if the crossed edge is a forced edge, we have to insert a new vertice on this edge { - QgsPoint crosspoint; + QgsPoint crosspoint( 0, 0, 0 ); int p3, p4; p3 = mHalfEdge[mHalfEdge[actedge]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[actedge]->getNext()]->getDual()]->getPoint(); @@ -1358,7 +1354,7 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) { if ( mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getForced() && mForcedCrossBehavior == Triangulation::SnappingTypeVertex )//if the crossed edge is a forced edge and mForcedCrossBehavior is SnappingType_VERTICE, we have to snap the forced line to the next node { - QgsPoint crosspoint; + QgsPoint crosspoint( 0, 0, 0 ); int p3, p4; p3 = mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); @@ -1380,7 +1376,7 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) } else if ( mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getForced() && mForcedCrossBehavior == Triangulation::InsertVertex )//if the crossed edge is a forced edge, we have to insert a new vertice on this edge { - QgsPoint crosspoint; + QgsPoint crosspoint( 0, 0, 0 ); int p3, p4; p3 = mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); @@ -1405,7 +1401,7 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) { if ( mHalfEdge[mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getNext()]->getForced() && mForcedCrossBehavior == Triangulation::SnappingTypeVertex )//if the crossed edge is a forced edge and mForcedCrossBehavior is SnappingType_VERTICE, we have to snap the forced line to the next node { - QgsPoint crosspoint; + QgsPoint crosspoint( 0, 0, 0 ); int p3, p4; p3 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getNext()]->getPoint(); @@ -1427,7 +1423,7 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) } else if ( mHalfEdge[mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getNext()]->getForced() && mForcedCrossBehavior == Triangulation::InsertVertex )//if the crossed edge is a forced edge, we have to insert a new vertice on this edge { - QgsPoint crosspoint; + QgsPoint crosspoint( 0, 0, 0 ); int p3, p4; p3 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getNext()]->getPoint(); @@ -1769,7 +1765,7 @@ void DualEdgeTriangulation::ruppertRefinement() int minedgenext; int minedgenextnext; - QgsPoint circumcenter; + QgsPoint circumcenter( 0, 0, 0 ); while ( !edge_angle.empty() ) { @@ -2205,7 +2201,7 @@ void DualEdgeTriangulation::ruppertRefinement() /*******otherwise, try to add the circumcenter to the triangulation************************************************************************************************/ - QgsPoint *p = new QgsPoint(); + QgsPoint *p = new QgsPoint( 0, 0, 0 ); mDecorator->calcPoint( circumcenter.x(), circumcenter.y(), p ); int pointno = mDecorator->addPoint( p ); @@ -3218,7 +3214,7 @@ int DualEdgeTriangulation::splitHalfEdge( int edge, float position ) QgsPoint *p = new QgsPoint( mPointVector[mHalfEdge[edge]->getPoint()]->x()*position + mPointVector[mHalfEdge[mHalfEdge[edge]->getDual()]->getPoint()]->x() * ( 1 - position ), mPointVector[mHalfEdge[edge]->getPoint()]->y()*position + mPointVector[mHalfEdge[mHalfEdge[edge]->getDual()]->getPoint()]->y() * ( 1 - position ), 0 ); //calculate the z-value of the point to insert - QgsPoint zvaluepoint; + QgsPoint zvaluepoint( 0, 0, 0 ); mDecorator->calcPoint( p->x(), p->y(), &zvaluepoint ); p->setZ( zvaluepoint.z() ); diff --git a/src/analysis/interpolation/NormVecDecorator.cc b/src/analysis/interpolation/NormVecDecorator.cc index f0a45e9d28f..c4a44eec8e2 100644 --- a/src/analysis/interpolation/NormVecDecorator.cc +++ b/src/analysis/interpolation/NormVecDecorator.cc @@ -350,7 +350,6 @@ bool NormVecDecorator::getTriangle( double x, double y, QgsPoint *p1, int *ptn1, } else { - QgsDebugMsg( "warning, getTriangle returned false" ); return false; } diff --git a/src/analysis/interpolation/qgstininterpolator.cpp b/src/analysis/interpolation/qgstininterpolator.cpp index 448991954bd..d681bbc2f31 100644 --- a/src/analysis/interpolation/qgstininterpolator.cpp +++ b/src/analysis/interpolation/qgstininterpolator.cpp @@ -58,7 +58,7 @@ int QgsTINInterpolator::interpolatePoint( double x, double y, double &result ) return 1; } - QgsPoint r; + QgsPoint r( 0, 0, 0 ); if ( !mTriangleInterpolator->calcPoint( x, y, &r ) ) { return 2; From 000c86e7de9ead617408e76803c8a66936e715ce Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 04:56:48 +1000 Subject: [PATCH 049/364] Restore TIN interpolation algorithm --- .../algs/qgis/QGISAlgorithmProvider.py | 4 +- .../processing/algs/qgis/TinInterpolation.py | 202 ++++++++---------- .../tests/testdata/qgis_algorithm_tests.yaml | 78 +++---- 3 files changed, 132 insertions(+), 152 deletions(-) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 1be400aaf66..3f9e953b177 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -151,6 +151,7 @@ from .StatisticsByCategories import StatisticsByCategories from .SumLines import SumLines from .SymmetricalDifference import SymmetricalDifference from .TextToFloat import TextToFloat +from .TinInterpolation import TinInterpolation from .TopoColors import TopoColor from .Translate import Translate from .TruncateTable import TruncateTable @@ -169,7 +170,6 @@ from .ZonalStatistics import ZonalStatistics # from .FieldPyculator import FieldsPyculator # from .SelectByAttributeSum import SelectByAttributeSum # from .DefineProjection import DefineProjection -# from .TinInterpolation import TinInterpolation # from .RasterCalculator import RasterCalculator # from .ExecuteSQL import ExecuteSQL # from .FindProjection import FindProjection @@ -194,7 +194,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # FieldsPyculator(), # FieldsMapper(), SelectByAttributeSum() # DefineProjection(), - # TinInterpolation(), # RasterCalculator(), # ExecuteSQL(), FindProjection(), # ] @@ -309,6 +308,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): SumLines(), SymmetricalDifference(), TextToFloat(), + TinInterpolation(), TopoColor(), Translate(), TruncateTable(), diff --git a/python/plugins/processing/algs/qgis/TinInterpolation.py b/python/plugins/processing/algs/qgis/TinInterpolation.py index 88b69dc5995..ebd8fe6fb9a 100644 --- a/python/plugins/processing/algs/qgis/TinInterpolation.py +++ b/python/plugins/processing/algs/qgis/TinInterpolation.py @@ -29,29 +29,61 @@ import os from qgis.PyQt.QtGui import QIcon -from qgis.core import (QgsRectangle, - QgsProcessingUtils, - QgsProcessingParameterDefinition) +from qgis.core import (QgsProcessingUtils, + QgsProcessingParameterDefinition, + QgsProcessingParameterEnum, + QgsProcessingParameterNumber, + QgsProcessingParameterExtent, + QgsProcessingParameterRasterDestination, + QgsProcessingParameterFileDestination, + QgsProcessingException) from qgis.analysis import (QgsInterpolator, QgsTINInterpolator, - QgsGridFileWriter - ) + QgsGridFileWriter) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import (Parameter, - ParameterNumber, - ParameterExtent, - ParameterSelection, - _splitParameterOptions - ) -from processing.core.outputs import (OutputRaster, - OutputVector - ) pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] +class ParameterInterpolationData(QgsProcessingParameterDefinition): + + def __init__(self, name='', description=''): + super().__init__(name, description) + self.setMetadata({ + 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper' + }) + + def type(self): + return 'tin_interpolation_data' + + def clone(self): + return ParameterInterpolationData(self.name(), self.description()) + + @staticmethod + def parseValue(value): + if value is None: + return None + + if value == '': + return None + + if isinstance(value, str): + return value if value != '' else None + else: + return ParameterInterpolationData.dataToString(value) + + @staticmethod + def dataToString(data): + s = '' + for c in data: + s += '{}, {}, {:d}, {:d};'.format(c[0], + c[1], + c[2], + c[3]) + return s[:-1] + + class TinInterpolation(QgisAlgorithm): INTERPOLATION_DATA = 'INTERPOLATION_DATA' @@ -61,8 +93,8 @@ class TinInterpolation(QgisAlgorithm): CELLSIZE_X = 'CELLSIZE_X' CELLSIZE_Y = 'CELLSIZE_Y' EXTENT = 'EXTENT' - OUTPUT_LAYER = 'OUTPUT_LAYER' - TRIANULATION_FILE = 'TRIANULATION_FILE' + OUTPUT = 'OUTPUT' + TRIANGULATION_FILE = 'TRIANGULATION_FILE' def icon(self): return QIcon(os.path.join(pluginPath, 'images', 'interpolation.png')) @@ -78,82 +110,34 @@ class TinInterpolation(QgisAlgorithm): self.tr('Clough-Toucher (cubic)') ] - class ParameterInterpolationData(Parameter): - default_metadata = { - 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper' - } - - def __init__(self, name='', description=''): - Parameter.__init__(self, name, description) - - def setValue(self, value): - if value is None: - if not self.flags() & QgsProcessingParameterDefinition.FlagOptional: - return False - self.value = None - return True - - if value == '': - if not self.flags() & QgsProcessingParameterDefinition.FlagOptional: - return False - - if isinstance(value, str): - self.value = value if value != '' else None - else: - self.value = ParameterInterpolationData.dataToString(value) - return True - - def getValueAsCommandLineParameter(self): - return '"{}"'.format(self.value) - - def getAsScriptCode(self): - param_type = '' - param_type += 'interpolation data ' - return '##' + self.name + '=' + param_type - - @classmethod - def fromScriptCode(self, line): - isOptional, name, definition = _splitParameterOptions(line) - descName = QgsProcessingParameters.descriptionFromName(name) - parent = definition.lower().strip()[len('interpolation data') + 1:] - return ParameterInterpolationData(name, descName, parent) - - @staticmethod - def dataToString(data): - s = '' - for c in data: - s += '{}, {}, {:d}, {:d};'.format(c[0], - c[1], - c[2], - c[3]) - return s[:-1] - self.addParameter(ParameterInterpolationData(self.INTERPOLATION_DATA, self.tr('Input layer(s)'))) - self.addParameter(ParameterSelection(self.METHOD, - self.tr('Interpolation method'), - self.METHODS, - 0)) - self.addParameter(ParameterNumber(self.COLUMNS, - self.tr('Number of columns'), - 0, 10000000, 300)) - self.addParameter(ParameterNumber(self.ROWS, - self.tr('Number of rows'), - 0, 10000000, 300)) - self.addParameter(ParameterNumber(self.CELLSIZE_X, - self.tr('Cell size X'), - 0.0, 999999.000000, 0.0)) - self.addParameter(ParameterNumber(self.CELLSIZE_Y, - self.tr('Cell size Y'), - 0.0, 999999.000000, 0.0)) - self.addParameter(ParameterExtent(self.EXTENT, - self.tr('Extent'), - optional=False)) - self.addOutput(OutputRaster(self.OUTPUT_LAYER, - self.tr('Interpolated'))) - self.addOutput(OutputVector(self.TRIANULATION_FILE, - self.tr('Triangulation'), - )) # datatype=dataobjects.TYPE_VECTOR_LINE)) + self.addParameter(QgsProcessingParameterEnum(self.METHOD, + self.tr('Interpolation method'), + options=self.METHODS, + defaultValue=0)) + self.addParameter(QgsProcessingParameterNumber(self.COLUMNS, + self.tr('Number of columns'), + minValue=0, maxValue=10000000, defaultValue=300)) + self.addParameter(QgsProcessingParameterNumber(self.ROWS, + self.tr('Number of rows'), + minValue=0, maxValue=10000000, defaultValue=300)) + self.addParameter(QgsProcessingParameterNumber(self.CELLSIZE_X, + self.tr('Cell size X'), type=QgsProcessingParameterNumber.Double, + minValue=0.0, maxValue=999999.000000, defaultValue=0.0)) + self.addParameter(QgsProcessingParameterNumber(self.CELLSIZE_Y, + self.tr('Cell size Y'), type=QgsProcessingParameterNumber.Double, + minValue=0.0, maxValue=999999.000000, defaultValue=0.0)) + self.addParameter(QgsProcessingParameterExtent(self.EXTENT, + self.tr('Extent'), + optional=False)) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, + self.tr('Interpolated'))) + + self.addParameter(QgsProcessingParameterFileDestination(self.TRIANGULATION_FILE, + self.tr('Triangulation'), + self.tr('SHP files (*.shp)'), + optional=True)) def name(self): return 'tininterpolation' @@ -162,30 +146,24 @@ class TinInterpolation(QgisAlgorithm): return self.tr('TIN interpolation') def processAlgorithm(self, parameters, context, feedback): - interpolationData = self.getParameterValue(self.INTERPOLATION_DATA) - method = self.getParameterValue(self.METHOD) - columns = self.getParameterValue(self.COLUMNS) - rows = self.getParameterValue(self.ROWS) - cellsizeX = self.getParameterValue(self.CELLSIZE_X) - cellsizeY = self.getParameterValue(self.CELLSIZE_Y) - extent = self.getParameterValue(self.EXTENT).split(',') - output = self.getOutputValue(self.OUTPUT_LAYER) - triangulation = self.getOutputValue(self.TRIANULATION_FILE) + interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA]) + method = self.parameterAsEnum(parameters, self.METHOD, context) + columns = self.parameterAsInt(parameters, self.COLUMNS, context) + rows = self.parameterAsInt(parameters, self.ROWS, context) + cellsizeX = self.parameterAsDouble(parameters, self.CELLSIZE_X, context) + cellsizeY = self.parameterAsDouble(parameters, self.CELLSIZE_Y, context) + bbox = self.parameterAsExtent(parameters, self.EXTENT, context) + output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) + triangulation = self.parameterAsFileOutput(parameters, self.TRIANGULATION_FILE, context) if interpolationData is None: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('You need to specify at least one input layer.')) if cellsizeX == 0.0 or cellsizeY == 0.0: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Cellsize should be greater than 0.')) - xMin = float(extent[0]) - xMax = float(extent[1]) - yMin = float(extent[2]) - yMax = float(extent[3]) - bbox = QgsRectangle(xMin, yMin, xMax, yMax) - layerData = [] layers = [] for row in interpolationData.split(';'): @@ -213,8 +191,9 @@ class TinInterpolation(QgisAlgorithm): interpolationMethod = QgsTINInterpolator.CloughTocher interpolator = QgsTINInterpolator(layerData, interpolationMethod) - interpolator.setExportTriangulationToFile(True) - interpolator.setTriangulationFilePath(triangulation) + if triangulation is not None and triangulation != '': + interpolator.setExportTriangulationToFile(True) + interpolator.setTriangulationFilePath(triangulation) writer = QgsGridFileWriter(interpolator, output, @@ -224,4 +203,5 @@ class TinInterpolation(QgisAlgorithm): cellsizeX, cellsizeY) - writer.writeFile() + writer.writeFile(feedback) + return {self.OUTPUT: output} diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 0cb2a2a73c5..8fa3bf5dd9e 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -1528,45 +1528,45 @@ tests: hash: 56d2671d50444f8571affba3f9e585830b82af5e380394178f521065 type: rasterhash -# - algorithm: qgis:tininterpolation -# name: TIN interpolation using attribute -# params: -# CELLSIZE_X: 0.02667 -# CELLSIZE_Y: 0.02667 -# COLUMNS: 300 -# EXTENT: 0, 8, -5, 3 -# INTERPOLATION_DATA: -# name: pointsz.gml,False,1,0 -# type: interpolation -# METHOD: '0' -# ROWS: 300 -# results: -# OUTPUT_LAYER: -# hash: 87f40be6ec08f3fcbb5707762de71f6be35bb265c61f594335562a26 -# type: rasterhash -# #TRIANULATION_FILE: -# # name: expected/triangulation.gml -# # type: vector -# -# - algorithm: qgis:tininterpolation -# name: TIN interpolation using Z value -# params: -# CELLSIZE_X: 0.02667 -# CELLSIZE_Y: 0.02667 -# COLUMNS: 300 -# EXTENT: 0, 8, -5, 3 -# INTERPOLATION_DATA: -# name: pointsz.gml,True,-1,0 -# type: interpolation -# METHOD: '1' -# ROWS: 300 -# results: -# OUTPUT_LAYER: -# hash: 5e14dd0b879884b8b8da56c082947dad681feb4e9f1137f5cda126f8 -# type: rasterhash -# #TRIANULATION_FILE: -# # name: expected/triangulation.gml -# # type: vector + - algorithm: qgis:tininterpolation + name: TIN interpolation using attribute + params: + CELLSIZE_X: 0.02667 + CELLSIZE_Y: 0.02667 + COLUMNS: 300 + EXTENT: 0, 8, -5, 3 + INTERPOLATION_DATA: + name: pointsz.gml,False,1,0 + type: interpolation + METHOD: '0' + ROWS: 300 + results: + OUTPUT: + hash: 87f40be6ec08f3fcbb5707762de71f6be35bb265c61f594335562a26 + type: rasterhash + #TRIANGULATION_FILE: + # name: expected/triangulation.gml + # type: vector + + - algorithm: qgis:tininterpolation + name: TIN interpolation using Z value + params: + CELLSIZE_X: 0.02667 + CELLSIZE_Y: 0.02667 + COLUMNS: 300 + EXTENT: 0, 8, -5, 3 + INTERPOLATION_DATA: + name: pointsz.gml,True,-1,0 + type: interpolation + METHOD: '1' + ROWS: 300 + results: + OUTPUT: + hash: 5e14dd0b879884b8b8da56c082947dad681feb4e9f1137f5cda126f8 + type: rasterhash + #TRIANULATION_FILE: + # name: expected/triangulation.gml + # type: vector - algorithm: native:removenullgeometries name: Remove null geometries From 090bb9bba4742e5cd8d2c69a59295be39c604a9d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 05:05:40 +1000 Subject: [PATCH 050/364] Use QgsFeedback instead of QProgressDialog in tin interpolator --- doc/api_break.dox | 5 +++ .../interpolation/NormVecDecorator.sip | 2 +- .../interpolation/qgstininterpolator.sip | 8 ++++- .../processing/algs/qgis/TinInterpolation.py | 2 +- .../interpolation/NormVecDecorator.cc | 23 ++++--------- src/analysis/interpolation/NormVecDecorator.h | 4 +-- .../interpolation/qgstininterpolator.cpp | 32 +++++-------------- .../interpolation/qgstininterpolator.h | 11 +++++-- 8 files changed, 39 insertions(+), 48 deletions(-) diff --git a/doc/api_break.dox b/doc/api_break.dox index 0ff3b52c16c..fd63bf35ee5 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -2321,6 +2321,11 @@ QgsSymbolsListWidget {#qgis_api_break_3_0_QgsSymbolsListWidget} - expressionContext(), setExpressionContext(), setMapCanvas() and mapCanvas() have been removed in favor of setContext()/context() +QgsTINInterpolator {#qgis_api_break_3_0_QgsTINInterpolator} +------------------ + +- The constructor takes a QgsFeedback argument instead of using a QProgressDialog. + QgsTolerance {#qgis_api_break_3_0_QgsTolerance} ------------ diff --git a/python/analysis/interpolation/NormVecDecorator.sip b/python/analysis/interpolation/NormVecDecorator.sip index 17c9f24561b..926c4a8bf0c 100644 --- a/python/analysis/interpolation/NormVecDecorator.sip +++ b/python/analysis/interpolation/NormVecDecorator.sip @@ -59,7 +59,7 @@ Eliminates the horizontal triangles by swapping or by insertion of new points. I Estimates the first derivative a point. Return true in case of succes and false otherwise :rtype: bool %End - bool estimateFirstDerivatives( QProgressDialog *d = 0 ); + bool estimateFirstDerivatives( QgsFeedback *feedback = 0 ); %Docstring This method adds the functionality of estimating normals at the data points. Return true in the case of success and false otherwise :rtype: bool diff --git a/python/analysis/interpolation/qgstininterpolator.sip b/python/analysis/interpolation/qgstininterpolator.sip index 7c14fe56c10..390a1323f0f 100644 --- a/python/analysis/interpolation/qgstininterpolator.sip +++ b/python/analysis/interpolation/qgstininterpolator.sip @@ -25,7 +25,13 @@ class QgsTINInterpolator: QgsInterpolator Linear, CloughTocher }; - QgsTINInterpolator( const QList &inputData, TINInterpolation interpolation = Linear, bool showProgressDialog = false ); + + QgsTINInterpolator( const QList &inputData, TINInterpolation interpolation = Linear, QgsFeedback *feedback = 0 ); +%Docstring + Constructor for QgsTINInterpolator. + The ``feedback`` object specifies an optional QgsFeedback object for progress reports and cancelation support. + Ownership of ``feedback`` is not transferred and callers must ensure that it exists for the lifetime of this object. +%End ~QgsTINInterpolator(); virtual int interpolatePoint( double x, double y, double &result ); diff --git a/python/plugins/processing/algs/qgis/TinInterpolation.py b/python/plugins/processing/algs/qgis/TinInterpolation.py index ebd8fe6fb9a..e5af69d2323 100644 --- a/python/plugins/processing/algs/qgis/TinInterpolation.py +++ b/python/plugins/processing/algs/qgis/TinInterpolation.py @@ -190,7 +190,7 @@ class TinInterpolation(QgisAlgorithm): else: interpolationMethod = QgsTINInterpolator.CloughTocher - interpolator = QgsTINInterpolator(layerData, interpolationMethod) + interpolator = QgsTINInterpolator(layerData, interpolationMethod, feedback) if triangulation is not None and triangulation != '': interpolator.setExportTriangulationToFile(True) interpolator.setTriangulationFilePath(triangulation) diff --git a/src/analysis/interpolation/NormVecDecorator.cc b/src/analysis/interpolation/NormVecDecorator.cc index c4a44eec8e2..e9318d444d2 100644 --- a/src/analysis/interpolation/NormVecDecorator.cc +++ b/src/analysis/interpolation/NormVecDecorator.cc @@ -15,9 +15,9 @@ ***************************************************************************/ #include "NormVecDecorator.h" +#include "qgsfeedback.h" #include "qgslogger.h" #include -#include NormVecDecorator::~NormVecDecorator() { @@ -511,29 +511,18 @@ bool NormVecDecorator::estimateFirstDerivative( int pointno ) } //weighted method of little -bool NormVecDecorator::estimateFirstDerivatives( QProgressDialog *d ) +bool NormVecDecorator::estimateFirstDerivatives( QgsFeedback *feedback ) { - if ( d ) + int numberPoints = getNumberOfPoints(); + for ( int i = 0; i < numberPoints; i++ ) { - d->setMinimum( 0 ); - d->setMaximum( getNumberOfPoints() ); - d->setCancelButton( nullptr ); //we cannot cancel derivative estimation - d->show(); - } - - for ( int i = 0; i < getNumberOfPoints(); i++ ) - { - if ( d ) + if ( feedback ) { - d->setValue( i ); + feedback->setProgress( 100.0 * static_cast< double >( i ) / numberPoints ); } estimateFirstDerivative( i ); } - if ( d ) - { - d->setValue( getNumberOfPoints() ); - } return true; } diff --git a/src/analysis/interpolation/NormVecDecorator.h b/src/analysis/interpolation/NormVecDecorator.h index 075163233b6..5d0e9ec85e9 100644 --- a/src/analysis/interpolation/NormVecDecorator.h +++ b/src/analysis/interpolation/NormVecDecorator.h @@ -25,7 +25,7 @@ #include "qgslogger.h" #include "qgis_analysis.h" -class QProgressDialog; +class QgsFeedback; /** \ingroup analysis * Decorator class which adds the functionality of estimating normals at the data points*/ @@ -50,7 +50,7 @@ class ANALYSIS_EXPORT NormVecDecorator: public TriDecorator //! Estimates the first derivative a point. Return true in case of succes and false otherwise bool estimateFirstDerivative( int pointno ); //! This method adds the functionality of estimating normals at the data points. Return true in the case of success and false otherwise - bool estimateFirstDerivatives( QProgressDialog *d = nullptr ); + bool estimateFirstDerivatives( QgsFeedback *feedback = nullptr ); //! Returns a pointer to the normal vector for the point with the number n Vector3D *getNormal( int n ) const; //! Finds out, in which triangle a point with coordinates x and y is and assigns the triangle points to p1, p2, p3 and the estimated normals to v1, v2, v3. The vectors are normally taken from 'mNormVec', except if p1, p2 or p3 is a point on a breakline. In this case, the normal is calculated on-the-fly. Returns false, if something went wrong and true otherwise diff --git a/src/analysis/interpolation/qgstininterpolator.cpp b/src/analysis/interpolation/qgstininterpolator.cpp index d681bbc2f31..332fb08f82e 100644 --- a/src/analysis/interpolation/qgstininterpolator.cpp +++ b/src/analysis/interpolation/qgstininterpolator.cpp @@ -27,14 +27,15 @@ #include "qgsgeometry.h" #include "qgsvectorlayer.h" #include "qgswkbptr.h" +#include "qgsfeedback.h" #include -QgsTINInterpolator::QgsTINInterpolator( const QList &inputData, TINInterpolation interpolation, bool showProgressDialog ) +QgsTINInterpolator::QgsTINInterpolator( const QList &inputData, TINInterpolation interpolation, QgsFeedback *feedback ) : QgsInterpolator( inputData ) , mTriangulation( nullptr ) , mTriangleInterpolator( nullptr ) , mIsInitialized( false ) - , mShowProgressDialog( showProgressDialog ) + , mFeedback( feedback ) , mExportTriangulationToFile( false ) , mInterpolation( interpolation ) { @@ -84,7 +85,7 @@ void QgsTINInterpolator::initialize() //get number of features if we use a progress bar int nFeatures = 0; int nProcessedFeatures = 0; - if ( mShowProgressDialog ) + if ( mFeedback ) { Q_FOREACH ( const LayerData &layer, mLayerData ) { @@ -95,14 +96,6 @@ void QgsTINInterpolator::initialize() } } - QProgressDialog *progressDialog = nullptr; - if ( mShowProgressDialog ) - { - progressDialog = new QProgressDialog( QObject::tr( "Building triangulation..." ), QObject::tr( "Abort" ), 0, nFeatures, nullptr ); - progressDialog->setWindowModality( Qt::WindowModal ); - } - - QgsFeature f; Q_FOREACH ( const LayerData &layer, mLayerData ) { @@ -118,13 +111,13 @@ void QgsTINInterpolator::initialize() while ( fit.nextFeature( f ) ) { - if ( mShowProgressDialog ) + if ( mFeedback ) { - if ( progressDialog->wasCanceled() ) + if ( mFeedback->isCanceled() ) { break; } - progressDialog->setValue( nProcessedFeatures ); + mFeedback->setProgress( 100.0 * static_cast< double >( nProcessedFeatures ) / nFeatures ); } insertData( &f, layer.zCoordInterpolation, layer.interpolationAttribute, layer.mInputType ); ++nProcessedFeatures; @@ -132,22 +125,13 @@ void QgsTINInterpolator::initialize() } } - delete progressDialog; - if ( mInterpolation == CloughTocher ) { CloughTocherInterpolator *ctInterpolator = new CloughTocherInterpolator(); NormVecDecorator *dec = dynamic_cast( mTriangulation ); if ( dec ) { - QProgressDialog *progressDialog = nullptr; - if ( mShowProgressDialog ) //show a progress dialog because it can take a long time... - { - progressDialog = new QProgressDialog(); - progressDialog->setLabelText( QObject::tr( "Estimating normal derivatives..." ) ); - } - dec->estimateFirstDerivatives( progressDialog ); - delete progressDialog; + dec->estimateFirstDerivatives( mFeedback ); ctInterpolator->setTriangulation( dec ); dec->setTriangleInterpolator( ctInterpolator ); mTriangleInterpolator = ctInterpolator; diff --git a/src/analysis/interpolation/qgstininterpolator.h b/src/analysis/interpolation/qgstininterpolator.h index a298502a318..4e1cc240b98 100644 --- a/src/analysis/interpolation/qgstininterpolator.h +++ b/src/analysis/interpolation/qgstininterpolator.h @@ -25,6 +25,7 @@ class Triangulation; class TriangleInterpolator; class QgsFeature; +class QgsFeedback; /** \ingroup analysis * Interpolation in a triangular irregular network*/ @@ -37,7 +38,13 @@ class ANALYSIS_EXPORT QgsTINInterpolator: public QgsInterpolator Linear, CloughTocher }; - QgsTINInterpolator( const QList &inputData, TINInterpolation interpolation = Linear, bool showProgressDialog = false ); + + /** + * Constructor for QgsTINInterpolator. + * The \a feedback object specifies an optional QgsFeedback object for progress reports and cancelation support. + * Ownership of \a feedback is not transferred and callers must ensure that it exists for the lifetime of this object. + */ + QgsTINInterpolator( const QList &inputData, TINInterpolation interpolation = Linear, QgsFeedback *feedback = nullptr ); ~QgsTINInterpolator(); /** Calculates interpolation value for map coordinates x, y @@ -54,7 +61,7 @@ class ANALYSIS_EXPORT QgsTINInterpolator: public QgsInterpolator Triangulation *mTriangulation = nullptr; TriangleInterpolator *mTriangleInterpolator = nullptr; bool mIsInitialized; - bool mShowProgressDialog; + QgsFeedback *mFeedback = nullptr; //! If true: export triangulation to shapefile after initialization bool mExportTriangulationToFile; //! File path to export the triangulation From 114f071cf8672d820b418b24bb155bdd8c85a5d7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 05:08:40 +1000 Subject: [PATCH 051/364] Don't generate triangulation files by default in TIN algorithm --- .../plugins/processing/algs/qgis/TinInterpolation.py | 10 ++++++---- src/analysis/interpolation/MathUtils.cc | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/python/plugins/processing/algs/qgis/TinInterpolation.py b/python/plugins/processing/algs/qgis/TinInterpolation.py index e5af69d2323..9c157b275bb 100644 --- a/python/plugins/processing/algs/qgis/TinInterpolation.py +++ b/python/plugins/processing/algs/qgis/TinInterpolation.py @@ -134,10 +134,12 @@ class TinInterpolation(QgisAlgorithm): self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Interpolated'))) - self.addParameter(QgsProcessingParameterFileDestination(self.TRIANGULATION_FILE, - self.tr('Triangulation'), - self.tr('SHP files (*.shp)'), - optional=True)) + triangulation_file_param = QgsProcessingParameterFileDestination(self.TRIANGULATION_FILE, + self.tr('Triangulation'), + self.tr('SHP files (*.shp)'), + optional=True) + triangulation_file_param.setCreateByDefault(False) + self.addParameter(triangulation_file_param) def name(self): return 'tininterpolation' diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index d321296ad03..29033a21c57 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -29,7 +29,6 @@ bool MathUtils::calcBarycentricCoordinates( double x, double y, QgsPoint *p1, Qg double area = triArea( p1, p2, p3 ); if ( area == 0 )//p1, p2, p3 are in a line { - QgsDebugMsg( "warning, triangle area should not be 0" ); return false; } double area1 = triArea( &p, p2, p3 ); From 3cc3946029cc7cd4944430eecd45a010941ca75b Mon Sep 17 00:00:00 2001 From: Giovanni Manghi Date: Fri, 18 Aug 2017 18:19:38 +0100 Subject: [PATCH 052/364] improves grass7 v.in.dxf making a couple of parameters optional/mandatory --- .../plugins/processing/algs/grass7/description/v.in.dxf.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/algs/grass7/description/v.in.dxf.txt b/python/plugins/processing/algs/grass7/description/v.in.dxf.txt index 0e9641ef6ca..3b9e62a8368 100644 --- a/python/plugins/processing/algs/grass7/description/v.in.dxf.txt +++ b/python/plugins/processing/algs/grass7/description/v.in.dxf.txt @@ -1,8 +1,8 @@ v.in.dxf Converts files in DXF format to GRASS vector map format. Vector (v.*) -ParameterFile|input|Name of input DXF file|False -ParameterString|layers|List of layers to import| +ParameterFile|input|Name of input DXF file|False|False +ParameterString|layers|List of layers to import||False|True ParameterBoolean|-e|Ignore the map extent of DXF file|True ParameterBoolean|-t|Do not create attribute tables|False ParameterBoolean|-f|Import polyface meshes as 3D wire frame|True From bc193c0034b16dc1c99eeff43193fb5b81ec3ed9 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Wed, 2 Aug 2017 16:07:48 +0200 Subject: [PATCH 053/364] =?UTF-8?q?fix=20QgsRasterHistogram::histogramVect?= =?UTF-8?q?or=20for=20SIP=C2=A0<=3D=204.17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/core/raster/qgsrasterhistogram.sip | 2 +- src/core/raster/qgsrasterhistogram.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/core/raster/qgsrasterhistogram.sip b/python/core/raster/qgsrasterhistogram.sip index a1ecb6c8ffc..014f41e19a9 100644 --- a/python/core/raster/qgsrasterhistogram.sip +++ b/python/core/raster/qgsrasterhistogram.sip @@ -47,7 +47,7 @@ class QgsRasterHistogram Whether histogram includes out of range values (in first and last bin) %End - HistogramVector histogramVector; + QgsRasterHistogram::HistogramVector histogramVector; %Docstring Store the histogram for a given layer .. note:: diff --git a/src/core/raster/qgsrasterhistogram.h b/src/core/raster/qgsrasterhistogram.h index 73dbe34cf64..14825218d31 100644 --- a/src/core/raster/qgsrasterhistogram.h +++ b/src/core/raster/qgsrasterhistogram.h @@ -74,7 +74,7 @@ class CORE_EXPORT QgsRasterHistogram /** \brief Store the histogram for a given layer * \note not available via Python binding */ - HistogramVector histogramVector; + QgsRasterHistogram::HistogramVector histogramVector; //! \brief The maximum histogram value. double maximum; From dae6666aa5b2db814c7bedf0b434cd50a6896925 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Sat, 19 Aug 2017 13:51:43 +0200 Subject: [PATCH 054/364] translation string fixes --- python/plugins/processing/gui/MultipleInputDialog.py | 5 ++--- src/providers/ogr/qgsgeopackagedataitems.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/python/plugins/processing/gui/MultipleInputDialog.py b/python/plugins/processing/gui/MultipleInputDialog.py index 222f9e556fe..72cf3eab3f1 100644 --- a/python/plugins/processing/gui/MultipleInputDialog.py +++ b/python/plugins/processing/gui/MultipleInputDialog.py @@ -35,8 +35,7 @@ from qgis.core import (QgsSettings, QgsProviderRegistry, QgsProcessingModelChildParameterSource) from qgis.PyQt import uic -from qgis.PyQt.QtCore import Qt -from qgis.PyQt.QtCore import QByteArray +from qgis.PyQt.QtCore import Qt, QByteArray, QCoreApplication from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox, QFileDialog from qgis.PyQt.QtGui import QStandardItemModel, QStandardItem from processing.tools import dataobjects @@ -76,7 +75,7 @@ class MultipleInputDialog(BASE, WIDGET): self.buttonBox.addButton(self.btnToggleSelection, QDialogButtonBox.ActionRole) if self.datatype is not None: - btnAddFile = QPushButton(self.tr('Add file(s)…')) + btnAddFile = QPushButton(QCoreApplication.translate("MultipleInputDialog", 'Add file(s)…')) btnAddFile.clicked.connect(self.addFiles) self.buttonBox.addButton(btnAddFile, QDialogButtonBox.ActionRole) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index f02a25542eb..067fed5ae83 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -403,7 +403,7 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct { // TODO: implement raster import QgsMessageOutput *output = QgsMessageOutput::createMessageOutput(); - output->setTitle( tr( "Import to GeoPackage database faile" ) ); + output->setTitle( tr( "Import to GeoPackage database failed" ) ); output->setMessage( tr( "Failed to import some layers!\n\n" ) + QStringLiteral( "Raster import is not yet implemented!\n" ), QgsMessageOutput::MessageText ); output->showMessage(); } From 62d4c55513dc3afaf891af0d1edb4d8782d7aa4b Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Sat, 19 Aug 2017 14:07:34 +0200 Subject: [PATCH 055/364] add typo to spelling.dat --- scripts/spell_check/spelling.dat | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/spell_check/spelling.dat b/scripts/spell_check/spelling.dat index d9cbc0c7292..c2f8243fa09 100644 --- a/scripts/spell_check/spelling.dat +++ b/scripts/spell_check/spelling.dat @@ -2909,6 +2909,7 @@ facillitate:facilitate facinated:fascinated facist:fascist faild:failed +faile:failed failue:failure failuer:failure failues:failures From bb8203b809d4112505524084384666e8686c029a Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Sat, 19 Aug 2017 17:39:47 +0200 Subject: [PATCH 056/364] updat splash --- images/splash/splash.png | Bin 474469 -> 426921 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/splash/splash.png b/images/splash/splash.png index 1126559633c3d081c8777694c6719cbae530916b..5d4512c156efc6a93c188ee7da716ce72a7997cb 100644 GIT binary patch literal 426921 zcmV*aKvlnqP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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

BGz|syH5~O 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

dpHon@YtFil`Q!3{@{ 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

`W7aPMkW0>-h-N z;%FT5z4t#PmKM@$vl;csyCI`E$FwYb$L8Z}AM(m;uX5$t=kRP3%P^ShwCOZG;%q>c zxmczo$xBLv4m-~?Y}=wg8RMB2XHT7>cXWhjSsJh?4J^k*I6k8+!>d-gdG8*J^BqpD zEMW@8>`aH(zx+$wy7vfw95I|kgmFs0e?ZyWCk@BE|Ms_W%8WeG2@+C()tIFy1)gn^ z_lMm4=qk2pU>C6I9uQo{LmpFQ~ z&LoU!&&{*4a$K8$P;6|kv3Io3g59Ro>9GIk0gKDWsXIRZ*MIk~kr=RcpHnAKP^(p- zEV+E)0zdxAn;5RkFTC&^OSZwoyK9(^O%cbmmygkyThz|ojmP|SV+(6x5z8=1;*cUq z5lM_!Z!kIPQsybqXav9@YlCwczbM(=-r~h8&#^K;Gkp%gB#e2qbwFTy7!txLB3A{M z&Yov}c9E4?y+W-90fyAU*5#7`t+L>#-(#nHNVQ(mT(Bp_>)*8m3Mk5wc54|KnLOCP zk6*7KO%o|iy1U!#9qjPTi3>mfej$)Tl2c6K))uy96DJ{iM?0*{o&Y6qOh;=pOU3Nc z3dfGkp-M%y8nC(VrHV$o zM%W7}Re}=;te{F~c7>Z)xA47!jYk?v+w8#FXA^35izu>LI@{*hxqvK92@j44M;S*) z2S^LdlP@(H9E6Paa#X4C1Dj(feP-re_O~PAamF)eJIpM$xwo}Nt!~okIEXxF=TU|y zJ^Te293~W5$>3m@I7x`J3`0t!Z(^7>u4!Tz2EG^2AB~8Uh@(M|rR4?9vnh2hSz6?j z6r^#1R1#^qJb1WCo@Gy6KTIzY%d+sEzz0!8QACo&2QnWm%LE6*?fVXA&YYe;R7yU(b%$}7kxGZOELmJVLEr~amXujaKI(I_6;Q3$IoY|yE1egR z2yIg}3{;t8TaF%oyB=8{QRD*-_8-wZ9MEdCnO|Q1JJx!Afg84iVObP;Nwd+Q({7=J z#4ru+Ki=S=*T*s}iae!A6a1QmG6dNq)FIGCNv}7aa;P9LOB~an?z@EdH|YHM9hTqt z8kb+5#kX80y(5}s#F>Uedhra^Mh9uT`rkAhj-5Eo@~LxdjB~=-1rjG9%QMo^5ToLf z?jN#0N~xG_edtPoZJGG4&oft^#rFb6;~|D);V1*IYGWQ>rdjv#9S7U>8I2>7tYq(T zm&5HHCV4_0mWW2cb6RNeZad94|os>iux)5he3G0L1zx5 z+2Lw9K^={7i-dpn;u&U6o+s@cVpXavNy%Kj&ahI~{3Ku;rF4@5!_?wy&o!n&$R(x{ z^oA35b~d?u=~>7@%A6=(qf`+TP;;9o^c2=GeGJ29FxfrNu^rHvK?Isl{uy% zF$_&m&!4QnP=slQk|uGM@px|+h2WrHkmUufst<)O=0F1^Q6(x**zX>ZX2sLB;NSh) z*xca$+I^%U$dZ^cFEks=)bayWDjsiaPO}JgXYJVb)AK|K!TkK3F31dnjr~J*`#n5U zQZ)_2e$3eRk+y@8rr4(bOpbOp30`>#+td&3YPH6xrFn{?Bug{Kqb^xqQu7^r&te?L z3?>sMX+jZ4qzdLQUM4SdZPhOdloD7%l80k9Hy^Q;j`1h^to)PTLwGKRP@Ft{nlJsr zH|YQAA0g2J>9%cCppf{io;=53^AYzp?hvObR;7wpZK5L?CDG9i9mbrUYq8cF<0J`} zpScW5aqISNq-i4zgU@c=WHcDi=*$vSt1NapJYHKz879liO9VlMvdGEuj9U-Z5T->D zjrFs7 z8I!9L&-EA&dU!#F$!J8Drr3^yWm!0WK$gV_L$R{FOtW3rg#LH~95m_$vRm|MqPj509|BhdkQZ=DE&giY#MpejeM9R1LV&s&V-E0m_p4IGirOJucCC#YIkD zWYeL^ll&ZsZMlpl1G>X5Gxb>tkr9StEYsxlh4Xlh!g6g40Y7=?O?rbNoy7*DN4u=w z*`Vt9RBJ8^wGOsn<5n|vb|Rz!mTh5KuyuEjJX6>X*nvZl=RCZ%&Hj3y)?6L0<{~Jm zv}WiH6Umq^!hnbJO~0@ z*PDub3wF1+N#Y1u78FT>>y|WU9JGW>Aq@k^_vjY|qezPy6@r5>VvuCyp~Z_=zRvP$ zn+40lwGHAj!6Y099y4S6LPelbmJ8!SY<XIPd=7-u-O8H5FSQD|tW)RP0DGAcEjTFe7t<6n(hX+5W=uaoyD4k!1 z6vSzw<9??2jHzu5CH3ETT~|+lgN(uLc?%ekWEmCP!Bvn; zi_*}^5-7;BjNV{KQW)Ia4;iE-2+84K#BdU6PMxd8_emJBdihy`K^LZlUQraJ;gBMU z@obAmt;KM6lb`+M`@H;>Z&2}U%A(|z*FVSFgM0k+t+$XU@;s+fsgel+S;w+;#3bFhuX&90w6Kp>KO3Ez9w*_aHXHk?yqaIQ&U`Rn=3+!w{ zIX#clXiTkH#kOr!sU4Sdb2H3zG~GSR5|SjQT5EEA$fjZ9mgzp#W*r_L-~<7A*2jgnlv;4_leut0x6Wn@(A`Hp%3xH(;1p& zfzY}N4T3g)j%LV88zJ=hnC1yb!vpF;0|Us*0>}56pPMC)j}Qi|t*>+Y;U}CtKF>Qp z+$75~g5y3#T+m()7=_!+&YOrxa_RC6SrT*S(?gXJWM~ zlu^#s?QN36;oK|781Iki?oCLNgj&m_-f_uuf#JF|+g0*(i0RlC-R+d^^)d4c1}m4U zjKiFn*%p=ygZ)Edv*2;>h-$sc?%IGT4>|1R*g?tsY>jf<pb=$e>p;f5OMB)>*suKs&Oe zoURtRfTUQtR;tgdQ~HJXdz`1QJFUh0-K|w0;LLy5`v(DU8@tg zu6EQZMJw>x8gH-4l$)IawG|mL=m+NSv5V!jOh*5ZExxO73_I zC||pE0@uT6G?}cP%2x4XOD7o0|`jhQn|&rYH*P6_>-&kpJ?%_xSix7uj6ETU=yzVTF3VK@hl9 z92XD-rcY569FB&(cl{A}KYE+zUU`}7+%Z7V+ux$mnxU8JVm(e%q8wb$#&ZNonp5Vf zexFN)=kRniFNHvef-oB3x)r1}h~n{-S7ifAyP!<{|Dxj~wC>7rO{z5yX}Q$vRb0m* zD|3t~jamq)QyHeklx3FF=pDUUGYktU3`$V~62lP8%z7LTEhOY=k$rFAcgNjM?RG*by7iIap%Q02_&bA0&eb+&f)xOn#n4Q5GVOWUb@5I?m8?7wMraBk{({?coIn|6vq-kpW zL0&u!oLA@wpR!01!p1a=>6xL6F3+~`Rl&S#@chNoG-?%gA8()x0}<2+Jcs_#5xF$L zau`bsk(%5e3T}@ptao#^4kv6rnh-`MxymV(!t-1V*9Rdn6qMruLAA!x{2XzvaUMlk zG6_R4BsI^c;Yun@xPSdBOUG7t<@L`I#xX)le*Ig&iJJ5=rJ%@i@+>7!A~qi0VldG9 znpQPnu3pjM;!2n;*RL+H3=4sr zCR!9sMgwfmVLTo&x3Gfe`1&=cg|m?QeoRlwyB(j|(roM59{6 zp7QE=!m&alY1H4jM-TfwR4GZiA(4zRJ%@3eP-)I_{MgB-gA;+kww!6RqO`Q3Os7+3 zea4NWG5f=P=A9;KoG>@PK)Y6FKOd0W1^2gZ@_1vH6LV)MGKDGx@kCK?8gvI4^GkIW z+D&%0LTqW#?+@{6CV>&qnDOXt_EAUa!k7!Op#X4mbLw zagJTJIoQhR%-5-PTq?f9!~5Gfc7gfm)|l>AM3E^BLvrx2pf&4~)dl_02-A}YLy?LC z%W`n49$6?rjHpyBN>Ok$IwZ`-l$4CaAz3`ethbpY37ViI@m-&#rGPLFd3?A}UX;9e z_B2&Lple$6w-Vx^jw=Y`97|LfXNqq3X!-z=Q%6ZjQ4}=lRbGAhMWoP``Dire&Ye3? zr&5#2kT{NTT?eTow{PF%>eUYjf;xV^f$exGCCIXvB#BAFiH?XdO>D;{s5jX-IHJ9H z44Et9(TG-ahW(=vqe;Z<>H$8~L7zoOqUDfHxF3W+pKi~U}Yq!ie0fbYAQQjcyY z!_gD~tDnXKo#`h`ldM!^d4Xd)fTAAw6k$l2B~TQczR;jO+hOmpOFWK|!oaaz+LZvQ z6yDT6yWqS0EJ=8H^HaVs{|MvEs|d$INkt}Ha_O);D!8*Z;C6412VqGnB^Ty8yf$Mp ztX3HZ4oiSFE!rH@q+V-KjS37DX;I*s29{y4Js$Jf)oUE?><}dp^RtVLCPR`mA&n;} zCAhSDf@ZTxQ5aAbBzb}9H@WlhA;L5`DcB8ThG9mpFtO)Xa9Z^Xx{|=RDOG{(>PQLGkO-+C7?xQliif%ZZ~}dnmx?ItPor5%q!2pW&U2{MYq*sP zhUH-h5W;-A4!e8zHXmHQ%JJ0|zUl(6@qzhcPhGhqUL6*g=KiXt{?GCmj zX|*jX^#aoriIwv={;JjU7JAJEWdZ zUv6P|9<@Rjqs^+1kS38^C5$4{Jfj@-h!6L1=da+^>O{i2v%PB z5;Kpx96r3w&e20IojOn8INZK_e_GH$DHN^=o~yfqvQSLoCk0JOIGO18UkFUoq~2&^ zb^DO!Sf))Dhp58iqfMFO|jt#kfrWHko zz#>mG2IG(uzDJrS)M`G4Y3gD@2#PF2NP{#l>5lruX+oN6)?D`xzWcpf+^lu^`s=SV z)0*dx|MqYA>Xn!HzyHObvzNvgDC(5}p^3gk;e;fOI2?qe#}{e%6ep~>7i)CHC>l`}K0&q4$0QbxUmJX7p$ z40!PIE=%WT=^rLMUJFT!ocfH!v_0HFF&P)AQqWoM(B0@U?uQha$+447=1#R3_abBo z(h-QVz;XofD5H1KXEHHZTxrr-sE`jL%*QtdNJC(|2K)PCN|j@Jf^0oOT9PCdkPC{* z7=sYIu!+)~VLzc-cL_X;jdVb5M(>U-vml!6P%}@Fr0G-3rb0t3t3icH9CA1u(i;tF zG^$vxjWA&Tl#M%d822+yojk+Z#y)A3=&rjgDU`O43nBRO7r(&j@s+9Q9d6#b$@=4U zZFkOdy4^z#4-e^dI@6BJ$599-Ao z(c^VShsOzAz4nR1kng_rQx+Nx4l5~@ii;OiaU5q#0aqB7gJqZ)mdVk0LY|j+j!iNV zByr64eve=N#y6?f>rY3rlutrb1^wZG!DvLq(`np|DH?C#aC;1>fBo@i*^(LMSE1 z%rc(q6ZI$T6L5B>NlltK(!iV+ZF-Pa0E&K+VvIxPYr!-sM9~`!xqo|&gYF)IbDR5* z?%_H<-Tnc2skpUzj+IWEB+0R5$#(yUz^{wmk~VgGArB@xcyh|A=l` zlGy>J;h?eviUc#yuq+3!GmmWLh%_O~4gTl{KjRN;HLA5bg-98PA*JDwm5MNqDNAsw zZARO>dfI46l#&PwJjcMY#FR)bK}l-08AhYWy124@-NA|J;`@>)R9L3P&f|3k`6k_&3f|0d@=|M8#*>i0{LZ)e!3Upma=yXe z|LPZzro+b821*z-Y7%8g(mc^tZ7J}5AJ_AdmP5aPL>fmN9qy4A3eWQ}6g=8lqbxJM z&JjYdWTxY$Q5Z2vQq6XgzFvcX4k9)T?dWtpM5(j}WJ={mmE=iAmZX%^wUCgyC^Kyr z$E#pj7I~I%|K2@q%8>>$GabJ2+ABP`f0ORPKBg*}ec?4?A*ln={uYfqX5r)+4AVk5 z4yF(+EG;upg5jW#QUyo7Jp!Z3G1nv)Hj}KxZ?=$?6r~Dw4-aYFxq-O+3_-2VxZl(F zaG6sv4FWQ14TlGh_Soo7s2v=U{n3Bnmw)S@P_0(+Y?GyvXPC*dX?CK3Qck0v;7FrCQgNxo%TFa?n&VT36K z&8kCg3`vp%%kaq49M4*!Fm7^p5_0s`+q|}Xj8FR`9>4ixLdVAT9O{0+iIXRvZcw_1 zhm1yJ1aNb&N5!r4-mM4h=LQweXR}b0jW)7Ws4PbqCU(1xnvBTDdNQ9U30ab$@{~MH zaBDTLJad^}`Px@8O@k!Up~>sJ`>Y=vGT&@dW~u)9e3wa*Qb@i2OY?$C;Ndzpxq$uN zfVox^6f`OU%}T&{JYl9aLmGvQmBMj6tisgOLn-m5=kAlQFisebCn!^L;=&~c?gRv+ zX-E`?gh`}P6h%Uu#B6NeBP$iYSEW*E5%?8+rPDJEAt_3Q3I{|g=gfukG#d@ABQ*pc zzVi|5ch+z`hrOdMgbOkk#G{lbFQ$XGk}NH_`Tho3S`hdyMP9Ibe~eLrHZWSWIt^^s z!?Gg?6c@gHn!r>TGGic3qHM%M#UoB&)GzTW22#SQv$HHN%&`8TOB`F6wT?>~7Zka| zkqW;KyBkAlW0(58N1iGM`w^Z6^{UPB6BYWmyHo;$R>S4O=^B2+VRvVrXsAcx$1 zz;<*Bdzhu9Q9`F7v22BFJ6J6XX&M+(VwpCEsqkwBr=M+MD32NMIM;r1U5mA|0@KhB zhib* z;i%6<^>kpRF#dlcGdi6PM@L|)coxTT=(Ji~ zzIcg?r%z#;dXN0s&08FGyVLF{N9F}Bqrf&Sl1htCyFpeIyz<7ExVAs!qj!G9x8MB$ z*OoYzfn`aeP@4{fFle_s4EjfaB+qk%C26+&4HIcQIDQ4Yy+F%!c-9Rt(u6V&NycNM(Ew3jpr|iU26g7zE!;{4 zQKa~$)+W?lo1jv~Q8|szy-YG5@%H*5^G=sXyZgNMs~r|kU&JyEYC#pp^(e9g+cutR z=!7UK%8bdVB#v@SQy_(=ycc;&o@Lm!o@^*p=*|}H5RCGYya3l0NGZsR5+MbxR)eGQ zCP>L7PH-Iyh2Z3glf3ro>vWHHFohuS9m+DtwktSJ4MUm;!@{x~q{%j>>5>Vu8J8}craiyR!Tugn zKp0I(CLvigK}bU<6{r&1&_wydFj+Wu0#g_q^^dr9|2~5#;{34%oaqWphocJ0Jf}<| z=IS2X8$Eq(!&9D{kP_2%aT;~XEJNi5Qb@`?B@M&r16evP_CBCK->o zd+Qb(+gqGIcOJ7W`L$pC8h>jPZ0zkYDxhvV6w-h^M*$$3tF;n*$3J(W^0fIQss4cIO93681(Id{>xW)_T&th2?B^iNZb5ItT*@&iR z(5kqs?++M71-EZ}%EHo5c;(B#L{TbS+r+aS9LK@7ZE~ehR+T~4XObJN?H^DXn4Dj` z$Fbul$S|mSF70{+A(!~ofAag(RK#cR{1BPMNLheVRO$`hcyNeV8 zgu(9K9?@h>tFE1UUwq*iCL8zp?D{o!Mn{-lz#vSybr{i}Z%~zjGbfJIYBiZ8g`P%b zDQO&$rz!CyB#$CwnsDd#9TsQWlx2bE`YbFQV}E~Vs`E3LUp&F~)Rasit+dGfSRj3@r;$DkR#GUTUU=a}uHSpi{k4Y-^Mb!#Tf=C#*$zW8be7}mFTTJ`yRCzz zmEzW|+eBdus-&L_-suhzN`fIV3xVrbz%US|NuH);sz6#cLRxwnD+Q`7FboOUB;kbB zW6S*EAN(Op3rm`eZ#z8N+2_{Uy3Px2v=CBo8Bbz9 zyn2=1;h3cpO=f17G49`{%(NUQNtI4I@LX(5k*5WwZy?Q#G%PSJ9j~)=e3ra`N~g;1 zqdq~~Aey9vJ7Y$9N>apByeiQoq0%s^H|wZ0V$@AZ%YyB-A<3{H9SVB8W6YCh7T9`x z$Y>mq7mBS%n&Z(s%vi`3mB8Tar3!VgLY@_rR)Me$YITdlUc~plKcLdKF)Tq6L6Iw> zbPtL#rcu|2faO3A<1iu+63e!TlSBu@DuwIX1a*gnl_u3@LCvnPyS~q`Kg5&#w~|tJR{+Grsu7 zYjpQ__{mRx!gw;F%(df4DMe7N@Y-vy@hji_72bXSeSY}kH<5A5<9j-AS16_RWNE^u z*ZSQ1{A*mea2`X#YcIXP?3CM*q%ogd|5Q)zl!gcj6b<0;_I2KSe~y3n&9CFRE}jtl z?$^GEdU%7|cc#L7RZ?b(yim+^W_aTZU*XELSNQh-`X}7KcZV=7D6@K(&z?C=ty;yhOn&`Wf0bl1rq^3zVcBGJFCh$5EZgJK zrAz$YKl`URwVKwj6$OtDd&GG`)Au-5_c>bZ@Sp$quX)uk=)CqCMH(Y5n~Nux_y^zo zI{*E%4JMY(Lv&r2yvT?i+~D}76{_)w;Xop!pnNueT|I>fraA$|Z(E!<)!Ck#Td-V*R z=Q>!Xp15`=2{&)ureYf$#0l!; z1@4ST4BAzc=~7h*fnjqnIY8}gAj(ohc%?!L-F-(K zNyY?~MLIJVX}8Yv=<(aU``!bxMA7R<)SD)oj~=k`_%3s^7bxJT*H0~RXLmq66~e;wn$L6c zC?p+^^>nKIIh;{invbT0zzHfCriqYRt43Lnj0RBXf^0ON@aB)-Vs-T-i;W7^z~^gI zK;+|5$l+kXGb_ib*(R43=MmCi62;htENITn*>yO_~1S# zXKReg5ZiS4`d7coGZ!v^fILfi=V$LSo=iY$7FyCf#2xN(`niip)8h1ehtsv+=Ku5W zkGOmPA(P33&io>k);wiaaAI|l*IsyT>MT`Qr9>5aQr$h+p?5TKh6IB9{piY7lEj!Mv12epsABp zl_ix*mHD|vhJ!wnP)}mIgF`BrkM9Q@KYofvqm7#4J_dU`Jihrc{mGbv;e@sBke|K( zHVZSyupEO%qrq?d%CGPz-~KiaH#Sk0PZ3nH9G{>gdGYLNzVVeW>Xl=bvAes=Pk;I* zMV=xJ6RT3etk!6^JG5tK7)A-K!?b~;#wil^XDs+c^vOJ?+sWS6);-`a^9m^t$W8zHbJeEbt*1;h& ztvbG~cgL{_JeP$|gNo-)oqvLH7%_@s0^g?}h4?{$nHt=` zcb|oo1v;J9)YTYK_v_RuEf(iauy?daTINW@!nACJFe!?X-ndWURjGM37F+W)ybAFo zM3^R@yN4-F(lF!JCpRdC#@ZA@ZvqTMGVFyIrleMP$dVkxR&14 zESzeQrZJsPMp43}`vv!IKgO~R{AR$``VM&<)12`*bFs$C@qo|vQpTfWH-w{G&+-~BGX z{oB8VVMsjJ;qU*>Z}a?f&-3Z^Pubhs#WGA57nitn`7)o1+-dooIQ7*PG=6wGWosV{{#O0 zzyCLUcKaT~aY!6x6iJ3w1TtlM>8@XzTDw&g8A6q;ZEW(xcYnsOf8*;21&v07KluIM zV{2=Tqv#%GG2|D%tT=o2EHAw90_QJ3gXOwpam?EG4i63o)G8GOI*9o~yUsVh`7OHX zh+!C#OvX5lLmDT1;q})z_tNM1Y;&K3!5GhVn6G+V_~Iq%zQNA=8fLXhUMT+Lhdu#)6CAzvbeNJ zmSy}O{&)Y1KmRZPg&%$NF|C!e1apg|c}fz6n1(?ahZ?wNm|Qq}foETRoqZ)49UZYd z9PqvOui{%SFTeaUOEVpkyyX7=KK;C4)%9pDERdG4x^fb+0yE7z%XN>SR>MkW`Qy!7 z6v+@FBypx!cZMmR(xazZS(J{xBhNFkG{$rsN|mFOF(v}G^?x}04m}=ApgC1EJ;?)|Ua%5wc!g3k)dSt>tDS#+jKj7qSjoFGz6ldJr-s67vkN6+|)hx4f3*<$K zQd2gd6Hx0c(5`RNu6iuAYuJXtaDR^nw{Eg{`Blp41NG#R4)TsBAwi|VmtK5^mM!}s~=kKfXHBTuF#lpNl^M)uruc+C!?C}_?u@Y>gZjfaWDyH`KN zEmK;q$%`*M&&$s}gYQ`6X^LZ7xRk_kNf^gWCLvBGAWt&VC}i#NI^X-zkNB-`{RWg8 zle4;dk|d5e>>gkkCRNwx@Nk>Cg(Z$1JBjPKEG;Ynif}S{LTmug;%s<|?8u7@L+YaF z{JD$t21k18Tk$x4;uPo3T*42kPuE;|mh$lWH4YE<87B!xVS*9B?K@YwfB7|@y>y8r zRvbUR!vE$E{(#$S57|6CB6B?gLvUtpmXphim_kov<22!~zWZG^H+QrS%G9CDGqoze z|2x0Q($XRuhe!PRPu^izDzf2#{$xavrQ~s>&yg(C=BZM{YMYe`|MYkN2cErhMJIS< z`g!P3a$=#w`C|)A!U?YD;yLb9LyJaglyv)j-uvvf&VnooCV7rq_-yWVIk`BCkOC8f zlZ$gWuf4#=;Su#pg|aAUH>xbocC=1w`kG0av3YPrlqOh~g-j)e<8u7$MfUFZIoR1F zPh;XFW-=L(rZKMRAe3Zrb_FFQd8wP9FplUAk4W+asY=o$rd6$C7zX7uH}<3VuTrge z1g=RQPdLa >LVJE15`8ts6xP(+gi(-ioDB)3gag37FhbZ4&hes1EQ!pGtI7%oB!K9ngsCjrDlR~B}9arq{C?-RN8wh+yp^TJ4Z=2@w z6^40CmSs-`k!4wuWGSv=>q&u9fQ3*FP4_gSB1u9)pi0!@&!M7vC7`tihG}qk*yk_* z>aY0W55G?w$CQPhW~s6yiQ}i1>;L}W|2v-R@QrVL1IKYNq`~>~=Qw}bv& zT8P65H$M4<2Y2t#%R`2Yi6;}{(Fk)mz;#_*Kj7A9pYfG1zCoj2|9LoYQS#}hpAy9} zj^kpQmc|pHNa8Ws!$St+5&zT85BSzEe}m_ry@G8SGy;z=yzvI6`FU*9(1&lPZ=5_Y zDYBHK?k=`tP_0!^LeZJGDe?rvw4ZhiPi`2!I?>~F!7n!)tv0wO5&)Szzv&GYDhaK@=HL?~pK!kzRn57FQqdaW66`tqRv}KH&1|5~I-p zpM3HGKaGa`&Tst=&tG{S+jg)`o0Vh7dH&@WkwR!Eo?(z@8Bz#Fqao|}*BFh*c$FHI zioo>==z>t8DIVl9dLlD=Tzn=cr~Tr&>+UpFT~+wm2A#D5j2> znMM=40UzF7M>z#%mFBPuiQmxj>a%k3PDHOc(&QIvB9^=Y0 zFFg&wZB!}*=f8?5o+ZmuEYq2;A|+*+vAKPhwTD~eWkJ1V(4KXu*Dbo;b^iEI{xiS* z8~>1tD;<;&*nS1C-qO(6vZOLUA@XYa8n-NxNyz@r7CT2hitd2CC z^)GmMo=2nA;mT{Df67iu(+sCp=iKR4KKIHCv|DYYr90rVD72_|GA2tS>eTr9<&)fa z@R%%3o?i1pN(|G)v~8pz5QdIO5mQH_^#ok&>7O6;G$Rc|qR|M;vcS-YnOBd`a*96t zYj^237BO8PY1x>jfhh#4C`b%Re-d)_!MX-73h|U-mKOzak}w($F?^r;%nUwI8V2KV zf-+6YQcxI%jwG`!8nrsM)S<;j5HL8{BOLWnaY*3UfW_JQCYI0>>QR5J^&SVz~L>5zk*bk7*m&(|~uQ8t^Os z@SlZQrbV7c zv=f=kRW0>i z#3%|0Cllu97x9At&-ZE7Tbw*~8rSu39B(SQ(Esk{GDS9E%aG!Rn(#sn{WRVl$a*gSV+U**^B2{ zJ+=hG;N<)q$7b4mc;_zZ!5(FvV<3nIeNtY(lQ;du@rwG!#;L+Y8gGq=$hq|X}Mx5oeYHd7ev2b!;HL!PJfCqsO%Ld~yCqj_w?;fU_`Ca!Jp`0$XbC&}WHEG-cd z>UE!b&0}YyPY_sGjsjVbBpC{YlS;xN6q#b*=rJBXOL zrRs6?IHD&dJC8#gPf~BXIHtf3N~9ymR8Ep4m_bLw3!_O&HMTi+!4G~wzu(6& z45GCMl+(PnJkJ=7Q^w;F<6)nhH$LO@U-$w_DGX`S9}KvD;|5tAlSUzyX%W<$)7xCq zu2wmA;sk|+pWL`f=6F1R@)#9M;W`d^USb%g9wRFaZ#&xE;^V7taWv==cpjBXK)*la za6e>z-o^3j7^XE%mY35w6A)8jW}c?xlaRE`__ObPhezuV`TEzs%DK}gFbs)p8)R8Y zX%v*IMA|NcH0RpxEvBw0q03rs`MXf!B=L|GQ`Xv~>*g)?7%0b!b4-9F?8?_Fc?)?eTxBMi&t z-~PLQ%a`ByGQaq>Uu1rM5ivDM6lIAi3~bk7G!8k~-sdm=*FR_b@eZb8Gu!TP@tN~< zX6Gn`!9q2_k^)2N0>yQG*6!Y8{`g5=eDNh_S}l^I;PKIz<$9ICl+=Bf3=JK;e149z ztH&8cF|$F1ah7rIqid9DLQ&*8*GwtSE-W$Eo@H%ofGV|a!?K{r3WO5a)^u_qCAMQA zgl;ZMRe;jjTcs)~%Z#irnM}&*XR0v^X#yKtV?4itPLMZraB!ZGr4btkJ)$U~$P!jMb$;ioUnKJDEX~hR zsnr>d#~_|$`AJkBA`RoI$h{N>rP7l?%bGgE6r5N+&P?^2BvH&KckXfd*ew72m)@XZ znXFy^koUc9P>PesR_HWaq*=oL!2#VQAxv{hOwJu!(y0$+iJH1~<7k2~B(;S_>a7;_ zJk!Mb@euq9`FMgkb*WXGZOk&qwX_yhOm$Sf-T`$}i;$CiLfyCNM=AA+ORMEzh(mf| z!u=c9xUqbUSHAR%n5LQ6Z^$9kGQsX9)G-ZAL5r6%?@AKicYvfN56re@;!XVAEX##XF1ZbA&H5mQ|3I05lV}`OQoR6Q93>@YIcn z)N8>;tx8eoM&SvbM=97o=+YmJSej{5t2{B}OeeubjwP&V^QhzQ_WMJ6(@3``yvZk@ zM0;itbyV!GEjPfZwYdNA4iERYn3=!HFMjD;EVMcdCIj-az&0&9 zwFZuDV<0Khoc&&((Qw4=^-Z$8pze7jSxJ&)xW2{QVvVi!K53TYS5+ED!`2Hc7 z0?QA{iv)9i-r(ekfVX}$z?3B<7Nat!6fusnDGHEE;M*pXUPgW1B^)cTCFBNK>=KU0 z8ZB>25EheRjtCBrL|_EOafEMcO-`7^NG0$pE>V)P(>+2dxPP#Zkv_!sCA$wG9O~d= zfG7-^L>nOLsjw{Psf)?9tZAh!r|Lb3qKF^-;0Kt~n@O4` zo10rae*B1132fUz8YbanOp+u>B{Z|6*`xqwX6DE-`SGW>u=`sa>-d;8-I$`nnWwQM}s{IL;*9D;=cx91t{j<;TD;1<+u(PuZswB%2y>co`Z6-jH zB?)i;>|Gw*xy9#RdzKR?PvQm+Qrh&6VlZ7=FTcTF_aP;McD2fWnp3aWC@h1cFrw9j zUYro-CJR}H?^rmt#ko$M|MRbW4!20DTN$pEfoqaalTWZb`dQ9+IK)G7tR9d@5r$CA zo?YPn`XfG!BOIx)Oq0=Y%y+)?m%R70_xbvlf00wC&d_N#^w?MlHn(??cEI|>hm^9y z%b$CN)rBUr9ZjweT!%yn#sVJRzQ=HXkE5eKcD6Se25n9TRhpiQEn%Eww0w`1g*oaK zpAskvg)0qa+bz~NcPO(2WjoYOgRi{&0%exd-`>Kl*ND0YxQ4;<>>}IUb%wnNWQh<8 z(*f6z?Covw(Z^S@3|N{kiKFq;ZdlJC_G?1+GZSf zIqLSLTRyFry+PG;rw%hkn&%8BBhomc(rD3cwFoSWqn%Au zI6!78veqQ)_jRyw5MTyAgMN<@lKa~`Iu&4NmuNC(G#SteO!f|rSZq}pOk$)cIU0_M z;*8~HK&cde``6zk6Bd^*U7|BHPj2h*+({HMFiUQ%?{YYfIB|RxKTBvf8|)q&u+u%_ z-0Cv3ttR6z0#$PB)@|;7c7xrcA=kQN9`$;dZWT{SlAR5by&Y7Vq6~w%MioB@h~t#? zYd@p6^9WV#uuhGc#bw4(!cl(&rp;hH=HadDU^$2)=d*kF$cqv%*jl^C)}3o??)S9x zW4bc)sx|T~hTq}kGeeC+@y>m$|NBg4=M9}TG*JB63QeZ z9uLTqlug^=?YG{-b6pH6FieTEBqyGI0cm+0P4$7MZBeUMFvOFIlWxkRB*iogib6LF z2=#OYuasagj@auyz;!Ih5)9ko!l_djh9pTc(!6-0R+)x1Ym-$nEhO7Dy{h(npNr2u z&rh%an6~`^ehg#hwj5AvGD!bi7(xjl;4A|e*aNwom4Mb6J*p0{z3d&s2o~eKo%$}&S zbbOYB?OlX0=*-U$)LfEr%ra%Pxa{NPXEM8(NHe&^%!hcm<>W1X~H3vkqnz zvb?}R;#el}I7g|Jbi7UOtTK)wOfSGPEUI2bkB9vVYdd?Sk24G*`PnBwWApldW%jtk zjh`sOfy9~8l|8?LQdsPZ)nUqO}kWBa5I@MmFFiex{*RON^`t>QW z)Iu1h#u^wVMLBgiSvJy;q-n(7;Vv7ykC~gDLzX33o{>;;ush`O&394B2){CmQ)>{$ z35IDgH#bi(yMR-zV;&w+tJJAfUF=Fmk*4ebA3@;0?CkMx{=r_6FF1!*$j&YfHA>}+ARj)=;f>+eN;`CI>lb7_uan2bwBMZxo}CgX6zQoTxE z80>R;N(lxQ-Tn z15y}>LgA11QSpS-G>F217VhVYcocKE(Pe*ao48EK{VFG~JcsgqW|kK?ap5%8BIAM) z%2o;f{~N~ARPM77L`BgN+9L;mfpf5ZGj z6GcWCrpz`QP)a7z2-~p;CI2KmPDOM@gZlf2nZTP-@r&fqmWP!^myd5Xc#Lw5EysMT9szqbxA zoMLu`x(8#h`M94-Jj5@1stnLD#;N$ zyE_z8GC#LK;Cl?l5o_Cfs4U~%pS;bd@Ba|9GQ+;t!S<^Zy)Ki7cZf%QeXg1&u4Qw2 zu0yrf#;-OQ9PKeV>T>I&YkYfc$kMT6+qHP~=CVmKOMSq`OAOu{CkVIRx1IP4vsEA^U$(SsS zDUt-Kv{sL4oRDD{c!38};`#wWr9v1d{P=@wbcZ9P=acO3Qd$;<;~>cSY!oxu5l!e z?@?D(Y{zAGcAk@G&k)BkAKdEJj%i!<|ds&%4xLKKgQ64*Jisrpq~)h2nK zv)e!7;qE53=@M@p;b38U9?}#v>Mo`wF-?PN-N3WKu}ze+5wb)m!Q5g%t!`0dCH+B4 zf0&Y&DN8FgJUd|TC?aU4WJSuPpJ18>MUjG0l4Lo4%K}3n9EE91YE^?Q4Y_xHL>T6@ z<~;Tvhb$heVk#UaqXfq>SUF#3xEFyHQ<{>!{gmpAkK-Hkx+BU2VttKbD+}8<$&HXQ zFK~o~U6Dw;q}tREnfvChVWyHuNX>>$8&J#4c<%P0wI6`X2~YE@!+0lw{0>nxC@363SG)T>Ct zphQpzMH(mC(OxR8fi9J1zDP~6W;zy^Q>t~DdBqfexwxgI29&eHlyUfI6p1u4Gt=1fAHcOfps9_Hy9x~}2Ae5j;BZ7s~)Gocq zc3N`n{yLEooLn|>9hbT+@J)eb*(emF@r1qGpJ{%PG{I0LMM}e~@a(x~3A2>l(FpIE zXP7^K4rhLjM#a+)Y$4d&>9Lp&8Tc-l=QBBRk+9XL?HX*}eMAM5aal4ux61yoOH~R= zAvqX~s8$1P)8_d6EUSxiR0CfZ@iT3%lnP}?zVziU(yrDJC3M4x;c!AZx5VD~2&Z?z zq<@5IJ6u>f&W)WKf)vMhC=KX8oKWOYZCjYONh5F>4Er1%2u{3on!E_f(g?$_v<^#U z?Cp(^CVE0yLY7MMR3k-3g9&C3;CjB+8X~c6oAFq8ceW#uQc~C`W z_NV;m+aDv%Dl4l?%vxijqmayL=jSji2L-%;>q9QS>HRI(ab3bJ zXR%sklq+&+u)njz5BE0dJ@k0?{2}x63oNgm!tgxY_A?lG8zxL$>7rAArgY;A3TZR6Hk#A!-V z7AV7@EOMgm0m&qyHZ#kk-EEc^mZ=8;W@nc5xL~#c?TW`U$J;#E?ITTtrI{K^N#u^^ zY~5JfVi=}KBj>Q!XONWSqappnUCkvjObkP>@=OVvj~ozZl{LBt z2NW2roVh@jj(PO(A*Stcm`U#4x#Zj7A(B^%+MIWtNg95vDRo z$3s-3L9e@~kpcq7<36X(ou@U^;q9NkO|9MJ_|hUpmSGyYSuspKMR}q#Qb3WVPaUXW zB88wVavrX)v%GqenR){$1PL0dnI$PIj(L7*g~7PP){svY{G90IDUA;$^_?UhT z)6wj%5(SP$>G??H6v7E*noQk{n*Uc8In_p;nRbV9DS7FYSDBrk=j|J}xb=7oih^|5 zBTpja)Dc-IMX8`nb5v1iZJ4V1VU9ZNXrnut+n3C?&%|syT!u%+%_HSxgp}bh{y0 z4$&y1-7$E0eVgH^z;tag1cu7692>_F48xe2MW3Lm&*AEVLw7GG4GWsf8HdA^$ze(^ zEM%zTRQGp6OiM`Wl{$xeF;0-;dp@dwyhxc03S^mclt$Q=#OX*z-I&QBrP*=2&83#Wnx#Wn8Jco3#6Z9B?%zUa-?lydLG@)O-yOvxE_^O z3(I!2>dMkCg-O3hI2mJ^7D*ZrjfOc@L zX0yZ5;SL6R1X(CSIMgFaMM9B-R65!MAt`gFD=ImSW|11usz9oedaX{W1Zfl#?Zh-} zg@HxWbMXS7$#{qbWD2UT$DHlZ+uvnya6os^BN~snc=`;3zdD0Fcag=VMU0@vxPaj>#K8D`3pgz3i_2X zJ(c35IrpyLz;g`R^Yc7+<`VaIH~9Vse??=)Bu*8+XJXe)YBL&c+us>5nJBJ(x`Pz( z+zaQBu8Uzf8jP8zOrnG|(cs9U&>&sUw+U($kUHc%2x<)b102&Lo96wbn9*v?;R%4XK8gJg&;nAd^eSI(YhcjF=> zg4v#7ROl$uPps&`!!TxTuSYsNN6XMfMY~a>C>7noXd1{};`%)z4GVtx;u1j?QL6H3 zu56`J!*N_JONWa;f8`n4?G8nr5+`FE*P9CC_4{rZ21PEX|1Bq)3<<*tfnUKiECxr1 ztnVH2#`(+Sc}kw=Sf+z(*&Or-B$J5oC}t8xlu1gQ7R0@r#*EM6@eb?vL+$9664UXp z8V#L!nCa+Z{&Ug#MsUZ#X21bTR-u`JSKa zcMT!Qik!U2LBJC=n{C+yl?u*yjFbk0L7$E7ZB8w(Ab@VKhiMp8eIL`%jJ_wsjm4Q+ zT*uZKrKRF%(nt6Xn_Jspn;f5CWIP#=CNXImVY)V#&cDk3{ss>=Zu7~l_ju{j8?@>( zL|Lq}N~aB*Cdb`IYK9>}rQa2CQ#BW-INny*FkO>F9;=MnEkoa6a6!WP`Qxk=y# z%+JrVe{jV3C}q+waI6IB8YJ0>VXsSf98yFPwQ7|lO^BxCVZYr*D8*zv!82VJmX@%^ zh=a|Tk^)sK9eA9Jso>X|hI48mcakJHmdWDENp>HtYe$S>VOcsy@N8uf%d2sJvW<|M z`5~uS5K>BQnO6m>D6mWm%Q6v`fkKmL9oxgK803?XVj2ql1jk_67D$2b2Tc3`LkKF( zHje9STdLG?9=7G;I_}gBrE?OW(C7199|9ZeYn+{{(3+`JYt1pce1iV|Cb4Nf-PH@B zr%cho0TM-f!5~QsT|im3?#%KWX_(VBiK&I|S*`<{Rf%N?PMthPHL!u4G|vb}3a6uU z(6ZhUa(Q}CX>6jFh>K7(TE414cLA6Encd7Ech-WOUdJy$z*_I zR|r~jjH48;z0Gf4xWL9_NP4tITxK9)>-JrYJ8#nQ0u%<#amLFkXZhGF;qvdYbN?QE z@*WdIu)DlMacq^+grh8@QIy1qKAc8bi7lWLxD;NUo-nYI3^&)2J5mT5^Yc9W_r8I% zu)tgE>-3EkwtEMxEG*G#G%!t*xHn*05>Lg-8R2+BTI7JqBrC{NiftG;hGec;W3k;J zj}y{bhei?6u6bAn{Nf9r=g#^qKD&0CTC0I+7&wN9uRQiP4he@LFFn^_5=C6Qu|;nb z^6JZ%@H|i|C(lFXmn4QGadQh*Dr&WWYQ?8ht75v^3}M+e;dqEG1k$qf%0MZ?NlKdJ z#CeWgWJsymbP9oxAyG6Yj#KPf9j`G*b!L%jdlp0ff7$x4UfZ%XJrjGz9Mdmvw%_~s z6DNE`Mnt-*%&KHLRgsjcNVZztR145W3j!no0`wPjAwYf85TF}fG+Pja1|fDeMY6~u z%cb`bks00`f41Mtuj%KQW9VY6h^p?^xyeADM8=67Yt1>v_r34)_}08ottpC%WVJ@s z^=4|@@U_=pxcMZNL^Jn>!-E#&ILZbaLYcN8;xVH#<;HJE7uSBlW%{O z7w+D&O~Ef%RErDV{rS)MH^2ChhpB+=o9s%L)%hv!-+zMJt~t*%AAImRvfW3XT;j<9 zKW^je1~V9A)=PA|aYT@l1FxWYyxH#j<#Tj)~BaK2f3NMV1TU%%uurTn`yyU%4 zPf7BIZ`{6)*B#+G4#Refo5L}84)*x{ue?UOToSl0eh?5uF-qC>$s$?vybMe}X6PhAWhsrS(OTiTRw}HuotC(6fFE}e z!lNo{lGNg3Ea_eH@X-_Ivn%@D4$^B;t(KhUE0)2WqA>)4rYaLYSQpG{hdj?Y=mq>Q zzw-@_0?p&~97jN_-6by)g1|>QlDf+A!+>r28cVR%=bu zDAvmv_dolPzzgW~hg6N=w}0pN+20ytj6!3Ou0xp@6nVxcpMJ`J`0-CDnue^bkfG%6 zOCvBDrgUiV@CO6XnkGvrrW2gN!|{DgS<+M$ItbCOhjATrZM}P5;4>cgDRYG?E8h9_ z`^a{Wpg%;cSBR>@3ql%YqbYJDb8fPfdcDBGY@Co9-?ebmFbwSXvZ(EM(D0>FN^3)1 zl)U@ldn{%%q!5gTV|KQ;84iYcu1j5(EY@q*S!&7bQA8ZabUPi3OX_xcq#BOyy+~db zeEReeH}($5@|>csxqWm*CzhySg%){&o2oE+h8C` z6S75$(1vT*pJ#vX2A@9ukas`*Ij=nTRSrh`6lG3TRrp>&*ox7mA!@e?+b(_A!>c_o zg4LvCy=Yj>N~%)PZiTFiiZm^#YK0fN8`q+t(GEf2psI%Rhclv<$7pvzZE{9OF?a8_ zm`zevS0x7r5ju}?9ZlFxIXx-Kb6ZraDovwod(jw;dwP-(1tsHl#NNRWXJ2x<8t~V@ zKI3wpVWc3L6wJ;%!hVC}HE7eI3PF)6%4ANjJ7TNb;o7bbfyeCP3MoM&DQin4Ppgu% zt8@B;EgGY_yu9Gz>=+63Iz2`^+kE=&IW7~j>6Vw&8sc?j(t}B8dq%#~4#Vw2$8;PiGDK?Eo3B;`yg(Fxmr<+>09aBr` z(ilNiRXEWuLDa|d1A5&K4l$}IEDc*5T*qJJK=(IOn5>kF5XD2(CZ> zl4Z&2X{t!+9{Oyv7? zL!Z1+$f}|?E)s}c2c;~*VDJC1nkJKx~q!yym8`+d?pvoIr$;vt@-@ZQ$jzW6UV&q>OPIhxtyjHxniB3)9c4LGNi0B23=^iTvnN(9Y=Iq zZ4P#Jh-05svcmO!JU?VMosi}wp>NlkR*j)7_FrmZ-zAWW z)C7pF7P3D?`#!aFQ8=vX0#`bC{h_74FQ({pMg1t{6Wy>*6N>c`(I~WOsHz(2I>>5F z``Rs{P9Ia17+uqDclms_`Y_nLm9@V>EudLseHiz5gkp=i}5hi|N!J zOsfi{Lo1X-+9k;=JRxyRgXj8;+AV71aWXF%bzPE5p`}OE>C);AaHL>0J*V4hQL9U8 zZD<_H8?U`acyOIXp3-2roG(d>Y9oxd*pq&Lz+S)0lj)kWXb`@S*)Ru%bdWN@QI2hB zH4aT(Qk4ac>w}P1e%&4-`bTW_B|85R{x)BE{Tu9U?cxAs*;rkb^q5R9sEVA= zAK&Myv^hA_z^E4kIoo?yfGo8}m4#goYGus}S&rBU#adBo>v_Kri42C1N##?xP$ZEYJ zYQ?gz}ZNt)7%LR`;dmF9?+efBh3Q~rhd5my(-q)Ov17Q{ke8inJ9Bzb{xBw-XHgh4m3nl*U7L)>*J zmB4TJshpHLPp$OTXqvJ@IG+7;HI^@Pb#+OrZGmt$ZvchkWqt5$iPLd^TfQ zlsrD45ITZ&Q6Xdou8ZI4Vw9pTD`wbLT{{YCH=3euhyzW__lRAI^gN2*7)O;vD&;G; zU*`VB1Ah7GJ8bQ2Q@0YrgB~YO7F^pOQmck_lHfUl!FHQYD@1EW-K3Od25LO>u_ge~)Lh%&+e|4mju+Axhg*Bwj5=LTXLF2fEOz6I`n#|PBUJU}O8WVu zUH*^%*+1sa&6|LvsY+C3-Dr(*_}(u*;Kyera=4G@+846kpdh-P9;d=*I#2Nqckucn zyvVLd9aoZX4m)+SMtUAj6w_1%YMvo!SkJB?3h{iOdb*^_QX3BlLd@iXBwbODcahyb z?QRd@dMvV(|L=Ey!1w>^ulVDC`k(Th-~WBaov!8oI4-$rm`<-)rYWC2d<5+dk4}!M z(hbZO4Gv5%FQ_&6tv2h~oLV=erziMfMDN;lc6ayCjiOvH(3Rr;2Om)8DZ=v@?(9-3 z&BJGxjC&zPo-$oGq!oO6y1;WCcD6>u-7b!F2tAi@xQ!7KF9@KiIT*x5MT?Ve#IO_b z+MRt41}zwmDK9lf3r;U*L~)4kTCiGa_2Y0b%HLO9;Sl$D(rH4VD1>|9)KE*womQ`p8P2!_J} zy;h6!X-cCF(qP$HWdX|OhgH5I{44b(;-qe@Bi8k809{vX9J&HO@Gz!91HKTHRY|Me zp%cd#Z3tYS2hX1H+H0@!^1bJI`t&IufBGT!KYX9ZpMA=$mtN)Q+AVeu4p^_&TwGo- znOt%F(MMEefo`lF1VE~ax8C?F2fMpOzHc``!l9~aYGwa7s#dJigjO8e17BU!8;!Yj z?G7K@{}o4fZnM3$jVnD=nNm~*1RjI1jcF85&mOVW-{IQsEwVDBR91&3gi90zD0<{p0UBA`50&o+_+e;0M$*M|1FqeCp8G|?>ih(NYSU)$92e>P_<@h- zg$OA(crAmfD^yW#4nbzK-_;a_q9_vd7069Gt5!9_SVOntSWQ5Gykp^o5`8VErgRQL|!>v7xbg8S7Zl_BW1R!0evrEb{q4Zr`N0Hf5i9BpYnU* zgmENUl?{1V@cG4pUp!iHy7F;vzCs-OZ0`>6T5aZ+=M-0GWJSt@XD6Iv(5{0yeN2+2 zq}~F&fJW7jrYL_+6SgQZn57|CHIeT#3Ip0fK;nD!qJYvdNDQ4Yz}-}lyPn5aUwMgN zef%NM=2Px1mfRf;xO1?_FbEm6B3kVjPw|2fCy1%4672*4hVwn00eP_5>4y)i|yqA4wy zRDa3j*{n=mm#E!A#w}dWqaXU{s^a{U5BR~)e@@vSpex0V!$XwTT+Jr@>K8wysw-C0 zDR*9emF>|s4;PWl@G_&V9ZF-EO|Mw57C72) z=f#(J@hfj~eszUGlb)RNX_9i`3I_Xo-2BQ*eE!Z4IG>&pOM`FVc$U)J*&|;}kwJ@F zue`zHWQfb<)9$TeW)wa^%P|cH4dtg$ZnrEZyoYaUcSw12Vy<%`V zW3;nFyVIu3)|_W6%MA2<+PyJ_A2HgRv%Ry+g9nd!?ztz7277pZfZ13+qp-zj>xev0 z8SNeLk8a;%doTncP<2IJl^DlCx*qHG3au(mPCjG0Na*dwc!9^!HJ75SxchP&R}PT` zIGmv!gKHd&<5F$%iMTG(vw+?vO{`?Ou?;}a_ZW_PL{UhkS2Vhzsw(7SiowBgJ-p5L z#fc(xRicxGYP~?$*+x0!;(6Xiift@1NIF)%<9U=tNs_LJ;}%gA;t05Q?T9ol_~kEu z!CSAr%Dvln+1lOb@})O8dHj&Y4S;mcPNAx;f>ZY=U_05IP^;~vFV+0x@B{%l==)^6qA6)0xkKSRiT+tgP>CLiTiP8ZKAP!@4H=)xCp@gPz$kG~DNb0I0%QAM4 zj`+Rb_y!L?d7tyiigq``cWooob6qOc*u0r*;|%A|<}{6A|5_U%48Q)_Qx;b_=Vv*u z$eQERW8B!`+Kqs0DS7|x6=kaEj{{uca{PFOYOJ}&b)`>P*VM|c^1wRxJlC^^a${(; zAzL;$nI;kelcHiitI1NuVv({hV)kjFbIoMEpwsoyg`(3B5u)VdPd_5Ny1>zf{hd9Q z1{Pt2>o}XhS3`s>I|rQ0KZX zK^WnA*3ndz7BV^7-lIR>vWFfG#<&=xFxpPCR9V`*mC=0j>u>Sm>u=yRHQjEXAdV^W z6xX%4tYw;Tc7DNLCx&*LMV3=n2Cv^I2tu;5qEZGS9HPi)u-zaXK`}Sfg(7PAaoZhm zU7XybaWZsO*_UT6!1sycnEs%@k;+1?Qrb~O@LFY;(|LcPAEks+TaB_Di##q5*MaN{o5Uw)OB zUwM)12ivs5fTXJVtG9o_&wlZJqVi2z*KW}N_t=p07*naRM$A;J*0sq zveaIr1XV(rVx*vQ1kVyhFN!$LQig%g3&TF$(5E(nWl_@3T%6dJ7lh+**z3|3g4J@) zYH~$z?+$fY(r#OeyknraI%E0CFZu8O>>>GJ%wqozukLO0^7TUu4xdiu?1diVc0^Iv z)CgP`9O2T!K^ntBZ<~Mc)*td;PyRXEyB*F>R!CVgon-jZ=hlroXf)oe@ut&%kL&lZ2g}A;R&f$`aS{Fv20vQyXXn9M5GqG>patN>ykrPt#DnRrm1m*5b63DrSM#jS}CsY@6&IGl<9)$vxkgs)?7S$!uH{j zof1{{b}BY7L(|wbh^`yE4$%$&;CFwMS8m@RSx#|8f>w&r&Jm3ky!X-PeDA$aC_Nv~ z7@UniDBoo394uzUkF3Nv>~`q%hxGe>B0peVR5(Jgx;n=gjZ}ZT5*?3sP^}j8#}81M z{SFA}(AzzrE=%fqlhYau@p?m~bTL9u*7mz1<2L@@0aaZyPZFM0-h3 z@(M>nwO;e|{r7n3t+%*69bKd%guj9lW((nHT$>kIB+F(M#)^Lle zGh{U0=H`p9^FZ66u~<#0q@id)_#U34d9LO!u0=lSYDrYA`Tggw;TgCw?C`C_5yO^`K$9+}oIm}PpMUfjKYnz@v+0`m zKKukfbm(?EI4%tOTkP%LAa1p(%Zkxxhi-RBrQn@+f6hnm|BAcM6$DX)gQU?FMO6^^ zzCFBqA?+waDZB3WT+3Ya{eU9RSkA6kEoXf8_%pmHW_n&zn>F20o9(SFBCmt%xjgt} z$vidq!!hLs!{N3&xQ?VzCAui7>T+}O5TI?oZR==RV&}HdR9n1U9GwM z+)W%j&aRHZX40+TJ_zgpM@ou9S#YY-q^pt-9$%2I3l3juvs@>9_KPLQPZOR#^7!Vr zck!f;BVCG2;D&~FFQjf1y{!^{H78GIoSm(S-}M=6cWFF-;`pAPI!S@4WQ}SC=y$fBfl2b)cxL9E3)j0H44H*QqE9Mc{ed zxc3UDk3OSHQyQ(%jsDWPWH3lKK>9w7Y7m-DLr`OkMHR~(GTG;v5{plNIy&BeiZE|KHl zdGbqLf-4;cy)LHPAuVp=`Y!vz;jP0POdUgk$L*J2;Baq?Rvh8GE}=0DMq6~VgukC$ z@yD3Uq7+st zo7IZ2(FD=~qltZ&ez(iBOP`|3QDw>1WP->GeAnT~Ra~oPw01-O%by%`l2&v_hkP(w zayg&ywdd|I&2l0~u*!0D;1RmgW?hV+Wh9<1F{B! zl?-OXfP4J7qP>-oCKaBEsg$HBGs?yxNfIkJZw$JsF#^ggBkuM=Ysxgi@qErN&iMTE z_wZav7`BM>HE9DV!;QTIeD_0&varotW6>aAh{mg`;@Y6aZ`|3VPM7#$i}iYe^gW(^ z{vKDkyG_ODVEWm%$%0?!X|g@YSK1aZiq+ar!5qM|19JgUW< zG|y0_Z4meRL+bT{#pwxkSur_1w$k+|rdlp(jmISG#J=Q|eFD3#M>}re2R>Dvqly9< zx0o+h_?6=3wOgE?AM?Qn@6mBxhV2e56!+e^#?7q(x36DgXEdY}1fWX(^lyI1yshYN z?Ql7r(rdK{eV-?1$K1Mco5^y45P~FG(;IJ*<~hetp5kIyogA~6UeStT9NA(vx#H=g z2W%f5vHS8HOixFwE-&bBZ?kvj1@cO9xPL^e*Jp5elgZ^7oz95!lSj;FYqB(B_vj|R zs|iFy`o;RnZrIrB4{@}i0bJ=)lsS{-jH@Ka@APnk9F?WWC`RN497&}OHxG9C?KfT{ z3S(S9KuC}MF5I3g&I?#pHH)&MsubGwV7Lcah87JMaf|f)j6bTc`S8R0Z0+`0FQyzE9?~3XeCboFF`Z7IvMTWWfVbZI z7Oo%j_RoIAeEt)HsLkH~P15C(`=7i^C0t&56$Q#X;i~vddX~h z!PBS5)OEnc$pSxgNmdzkQ4)9sXO}s;S)!Ff=NZBn94YJ}!Wg8K z_^u>POKa|EwB<3n9*)~kr7PPQG?v&d9f;$Y*T3=>WvTe#U;G>9@BfVUdmW0(A&g_T z4zKga|BL^OKY8P;ER%$Jl2DbK18NYmJscuTLn{mkwzpZWR(yW{GtSOVc;Vg)?2N~D zdgBPD^97fyHD0I3c+h8myhSKorpqZpN~8m2QLsu90&m9daG&k|j^&S)1)aEqgSP6e zP4h7fV*=l9Q5pk|BS^ChBP|}m6_T`0h~0?o-7Ut0nB~Pe?|t-u-J=7p9qh1JPbgDG zQDmsb5C%R`=v%;aEC~XaPT$2elBV9YGPUILxIjpk;a-O-tN6taF3EFSBq(!5d(dHL zua9_SD6<-OI2dE}n#Ne1zp5LWMpM`Imtk2~+LQ8~7j~(%WHwjqN0QU0OQM0#*|Q7g zWy)5kj}($(U9*35ZnJmz8gIV*3PoA7TCGsJ;e2|@z3VrLf`GG=6D}_= ziCYd|{q_Ovb&RQM_QqR$^EZEkCl4QC$`ai`{e=L**hZjh1WG7q8r&eH*Y41I;Z@RP zO`axJBT#L~<``R*#x{IB*RugU*To2jY;E03QLBw3oG-a0R=uP+H+Q(C&1I<2LQ#E~=P< zE=aQlrp)ks>+mzeayT5vA&d;|j!&M&sJbSbU*W}VgdgArq4h!uFl!A>MV_QoWy$q} z9sJk^qlF__uNunSVd-YX$(l))<04w3883PVEAwa&39 zpcRI;%`ylXwp$nk+k*k^!H7Tq+1qsDh+d~fuNC7;i2@GB+uVNjEuwV7UZ>6Y#&akH z$;lJKy#pdYAn~ARYMQ)&GDXH+9NpkV5l-OJZ^yJ;httJ^yis&p9eUC>L(RrZR@ODX zbUE1F=gCL!admpkY_Y(XAXn$S**j#nH$XIz<)ZaR7#YwJhH8JET%R zC$k0DMtx{B-e$_Ds*=X!=px6FE|Cd%w#>o@XG|RS z3Bn#tQ*(U$hi%8+P}4#BIst z<&xve1v{?8^Uv+GT4h{KrpyyB*&7>wm{yZ=3yYi_g!~zaxyb#sqEbntd31Jo@k#RK@eOx&y*?m)X@h_a8jw z-~943(y&L^?a`D4qOQ?K6GSZn-=|b1MUhe_Yx4DyT6%=Wt{ajxr7mr`L>O=#iO~w7 z744{lnqM-zyrAApj%k!#gBqyoiYm|WgMcDSF{wwhUPIg_@La~7F7-xmo-gOjuU35T zU;Ybz_mBTEzxnzb%oYo-o<8M3Jmr7;Kl`7uHQvJYT)sf_=}nM2Fe~(GLK;XOKX^pc zj#*D;1g(hK3Yq4I$?X~klA8EV{8a{YkP;rKqwTWtv&Kzd>2n8+#Ri7blDmn^;Pd=O|@AW1ic=D zhh7{Lx*kcI(a@&TiV0jvnio`cg%l3$cAMv4c!{%%a~?l^iZL+i_NeO`KWuS(FvgXV zR^U;rm;CyZkNEihhqwVmo*f_Z%}g+wx7o-EN?u(dtn_Te=`>G7j?-X_;IRh=Wmh;BRL$#e!n5QZ){ukRDM5-)P8 zoD??{WV76EC2qNhGA2n&*21A3`?x_x;&_k?iY%inGyD8n7d*XO;&~o5H zSNW2-Bk@|Ak58VFX^7%B%hi(Q^onA=l-Y`8y+T(tLV$E#Jm21& zjex2wsY`1hZx2VfL2M6hs-`G&$~3b%t)@XrLAyIdx&d0(U%qH`(_k8fQZ=fv#P^q< zzeay&#MSf?C-9iAmXuk_)^KcFA$e*!cnHEU!jUeH6lg7wu1{0jhnQ(n90Q%WMbvI{ zeylK!q)H7c&+ST6Hx!GxmA(csG7NE}80kl-jlZfX3(72^)rxT(I~u)O7bJ_6Z2go} z)tL5xX1Tz`UDj!i=LNJpiSPq^BXuRi!1Bzj@YrG4jyM>P@jah7bO|<9UEw%{2(AtL z?2fl^r6dj`T48{@5ghLgdR)JChi5-Kfn?1qFTKF``vdZ;3#zkI5Ot17(Vh@cfinNtV@!XB2_2ytTJ(_f#}s9`nb3k#npa=C!MX{UW-*iVDet^jGo6+QVVF*>_mB;YR*U9KAEGJ> zjJ87cg=%=`{m&Rh5V?|aKIL>?@GpM!E0RJ{3rXX+Uruc8|A&>@($q{BD=ts@#ZB(C-h)%L?iGM6EX6K}oupG8%7lyY2;OaAd;gjiitHioF(K^vQqa@~MQ zu+670{}GG2MqeJ|9Dj$4bxOPM&AxNUZd@-DWnsWI3Nw*9~=1 zu(dTHThICY<9E4z>t*iTeSs)yS-J4K&&XM}U;LzDrZsNtG+*mo7jz@EP|y+`D^=VSkIV zsF<%aCetbDET`6X_2o)wb4*y0e7D!(x#wS`?soZ&H@||ZYwAYP3IbNkCFk=A*LL@< zS2YY6w_6Bfhc9s$GMO*vwp#X}@}#EG5OhM4Yz0WlqTphdkg1B>uWb_;$=TD4 z8!v{84`WU~o|7j4DnZr0CF!gpiYvU(t|hm2dL)_6ohhX$agd%wHw~@}Zk3m~n;*V0 z1|33HHe^+eZtUq^HHx#d71y2xRC$3imIEbSMbikPo~1hrPtfW?*7@gp{g5H(zRXwuWu) z-nzzWvEX25pWUOFvuDp}#W87`GU#@&5^{rdeWVwV=Oy#YGbR_$h+K#5qq~%jM^mI= zEIY+*xilJz49Y@N7o`n1dV!rNsD`RqBGNU&^9egW+;#_56gaL!sWf$+5ySyWo|9$; zO`~a4jY!sbL5wIRS~{%C6zND($E9}dZ9xdbxi*ABgz;=v@7C@vq36?zBJv_<)ah(2 z_L913z!)Oe#dlr1P;IP0xaIrwOkbasUk zTI#-2m6&9SsB4TXSV;%hH1xUy`u!1&w)A0xMq?jDY)k-^QUtEVl@3Xfk-hgfyz=cg zh#ApTHIDCqu~10r0$D1G$r+1@LpSR1`d40OT2wTSi|hI1RfRDgu5{3O!PR0(>}0ft z+vLDSHX-sIrt_4GtCX@ZoSs!wQ3rp!O)LT)Pp&BPD`r#0+XDjTv=TTQBWtp*>Tv1m! zjxf}#Ipu1F>$=qGipChqyr4QcM$kaJO}3cRsK&N|w8r;5v~dxxL!%X@G-v_&Y>FGl z6lsE4PFbD%pMS>T z&NjkvsMi@!AAG<+`?r6Av)`in^H2EX^C=}Bt?`(@x6|T3e)*8m-Zk9bE{^ngZf_qk z`)~Pw{q%FPWQAI-Z~!kJ60~CcDnuE>u-E7L8#ig1hGqj;+}}Us! zh4gfnG|NaELu+e~L~9&V;u;5CxLl<FA!x@jo^&x<(`t7JJdgFp{?KGA zv?FnXkZ3sMpM3ip9JOQn^9@@(eDsW?pv}h@3BUa0A^-aq-y|(_mW4v(1*Rww*_t2> z3AoxCx@G&2K^dXuLc;#yg{`epM z34u~*RoPvJG1NuEYBuHJr=Rie+rK7JCHG!@fo`wQy8A(dfqNaVl4Gb8Cx!tA%bF!XVnjWjkoIJ^xTu$hB zdZcAe6oeQWj4)_p+mo%$`?;t~gfT>32nbYFp_Rogb>f&|yGx}TlyNxP-@>>F7t4gG zC#jU?;;g|dYqm!NW=VzZyqFDcNg^VNsxzi@2S0MTJkANCh*npU77j?4)vBUZ`M5YF zbxyIa*x4H}o2JxCGw23fJzd&}PbcbZ+H^HpRni)}luJXIR48pv_)7yBGQqk^&6j@Dg8}emJyB%||Gaz$I9PLo7DuidaI)6g5a#$@F^x_VC2M65PKfrAi z)#W*+T+wO=xOL8}w{G(6;T@#w^P_iu#k=o)N|7aYLZvlEXLdBdnHss$L+A!C3@M5N z(`W|cEyCC;WjyH<#4%TwC)CfbX!Uw@dOf;>A(g7A%7Qx2$9dTd&@u({51|ihkUozkfuUmaNtZ zq4aDZ(Q2frmBMj6z!JAh9O}GcN4hknt>HV;uHsao2qQrdXclEdJQ!j;*TVg3>nPM! zY5#nxqF%3%VQ4QdQgS+3qN*G}2nqdwG%vt$kU|oBE{!$J?~q zR-USjvEgO8?~4KO%pWl6UHOwYc+^9vr->2N5DvD*nll7X5$B+2rx%N zI<)&EE+!WUPk@1XwLrKY-F8e=rzAzeVm9Y`*yi7UcF7-_3e|g#V{t~l%vj79q?tyf zYrHU^+llcno{?XkQCyzz;a9)SgGIx|am5c$zYo1FWV>sfln2idJm20VGK_6y*B~61 zP0C?j~~ zrPt|qI@DE3Q`bz+jyX9w;laa4eDL9?JU%`r%`2oU`SmAH+1?uQpZur)5t5SkKKv0^ zs}(^c(M85Is|o#rz1^C@z=67?Nc3iMY7ZDVE;5QR{W0xsmu4}iS}h^ZEn3L;DFef3 z&>|^UgzYXWPbl*oKZ;OkYC*249bR}|h>Tj4y21}!irmI|bfbv_ABCpVZd2q1QD=lo z*SM}^s~vNBb;4K-{=frVLURLy6mwvm2zZt-| zt|Ti8!XRL|T9FnxNt&U`oWOI5;}#c_8OPHpMm1bKdd#Tb<<{*x^an$_Zkw&$eYSV@ zkWw(8&M~e>UDaG%O;~3sqi!Fq8}cHhR2ALU;7hKSH6IC(*0YP!)OAUB+#$TInJh0U zDow6ST5-U=ySKQ1dcvq4lBDHk^{81W7!2D?PUc7fLEteShRh3x{q1coni>6ZpJbl$ z=%Xd$Zot)XMQ7_wy&iFNc$-cTu()`}U;X^22ruMo_wI3T=Q+CF9$665IFeUhdX4GnV?KNIjIyi{ z(zlMf#(toxsz#~GCjT9Ws?4eC64&+UkH)y0eRt@&Y;}Bgx5w;^BEJ8lAG2CakwR{2 zu)f{fc0-i0oGY|pJ)I)T6tzleikz~xA;VU?$Nu%Z_~AzAi@{Vi5(hW1bn4Z7W?vRs z^YqyzM>{(VhJAXYF>x5OcX)_r48!3F;ktY=52$Lpr*Cy)20LSB^9jOrDeHo&ETCy9 zb;)8nN7affX;8Ig0y$w|Q3gecC@QN>krq*5Hu@EvX>cSOL+u!v#v@;)=vdJ-+EVTX z+&vhRdm&FIYnnU-*Vg!rv~+t^1C6qrtww?8BZ3&``PV3nU~jZVJM>v43DR{bYDKLK z$Lo}nuFz47+Vf~1-lWyntutPiSFzB>scV6UZSa6Mx`DZ`R=ykhvTLDe2Nmg^N z9S+H~vH(Fxvd&V9EGJFuD#8djJuCQ^|7yt}{o^?%iaEbbxqYj}aMVNT2*;W5_~Dd# zQ_|2{Q)elDt7To8NoM(Qj-*iem#bAa4>hahX#VzqWz7HIzXoWGwv%pG5`~^Eg4A}6 z|HVPf^X>PvDhkxb(4eXul_p@CFWFqxVu9zM>h;P+}|fJ zN{X_=b6vY~awLP@L;Us9Mkk6KqZ(9}APVAPix4 zxg_ih?%umWzi;qEMXOsQoh6ON7M4PbNDxsBu0%+IsY^sv*iDh|Q1&kqQ^0E9s}g1S+pS%yF}9*<~+ErPHEggkooh<3M+ zsw!kl<<_T4!P+HSz^*|f^4c)*I4vuh8N?VOy z`OJXkDKvZBWIBth4q$qZUa`r84oeCtKN|L=c| zdwr+JayCbl7R}&jxsd_GDy?Xgrfw8hlY|EkA&6t}T@*Puk~=TO9PGDgst#RGk&+>G zhld|NrBpTXmc(f)^7SX2e0<8{>XP-UU~lIIUcB=n-F}zlazd6&2!aS<48yp?ug}*c zNl6ey)T%@`8!dnUt!&`Du5A9PX=TP<$yZ}a@EYdm-TfZfr6IF6WS8A!oj z{>As8DmIywnoK8b09Id8e;pwascZE!8v*99+vWDXmq@grs!Jq-s>)d{XZAxZCHZ=Z za9rG02hr~E-sz0X@4wA=zVa$J4mE-X(bPCzh(O?OWXE-$6L>yx7~r`so)_8)kLOZX zHAP|bRmKr01dXsfDqRcWjl!x0HYM5QHYHrlA=U79?{M|hkJVz*_yedEnj9M`dt?} zA0D4FSuL4WCCelw-iUw!=1EF};wmcYBZ7S5wv?ao{!@Q=Ieyj zYK3VWTI~q!B=}y%{?7As!!EtJgYP(?B~9%xyIA3P_P`xRJv!Zh{?H|glx=d>4PFqi zUe9rarmS+L@8C2Jt9iq8=`x??WWM0XKPk!PL$q|Q zf%DP(Y}Y>jLcGX_M0DeZj%o0L!wnp=F+PpIML8JJ+<6s$dd~5akGb0)u-hBZYqjxQ ziRT)Q_IFt=7aX6Tvq}@L?d{{Kn!L#Ab=q89U9n0MZX6s@E5qZnGp+ zU55Q0Q71-eLzb6lA=uv8K|J^ft!j*}aeNQIy^nnJJACej-0=H6^>VIHW(1usKY3R0 zO0k6AkpIiupP>fZw1mKWcAxCYr<9e##1T%FW3m;i`2@3l4RQM=+X#zdjs^p|fy?Q9 zN!b_{%M>*^M^-7aN>H;eRvWpsOdEfG>8oDMhw)o3bR zQ|1ZE2%ELihH|w)`2k3YV;tPT$5bJ>9!}t4YCA3Q0vF#8H$^lb&-2MwOUk;ykpaSJ zvgI5)JG9$vJSpw7Q&~PiqZ;xw=lzEdIA70j%hG1B#$$f}5B`W(Z{DEgduXGnYDJRf z_P}WD0dFx+n9rw#zQ@VM1lj{+Z$PKhrQ7b%Z?%Ybci7+E05ZX<2Xnxx6Hw%tZD)ov2$Y! zLCDR$eU6_!Vd>8390l@fh$qEVKtvIJQ(1rRm#0v2Yhz_DMh77)&-6y zsB%SBE3`Ivo_$$4jzhaI@S}kC$fMH=81@2U{~A}v7gXL7C(@|0!VN$~5>pGj$j~0o znNFs7U6-CeVtX{^-u{4{?Jn!dDY{8Qrk@W^7gzDr8rHAl z)O8^Qreu@rvZBjBdhI#>;19pe!wkOr_6Hn4J)zNxsMSIFHL5C!x?LPEAnf%meODVM zS;@ct(JweTyW~6H`nu)g^sL}D3}b|J$g+&Gs#u(#viIEcJhmP%8Q^-cu`0c zg@nu0R`aulqA_&3E`e82WS%`hHOhW4wfXXq^)L5Qmhq!c#G>uj2C2J%^RV5EKdCt1dD+`)+oyHXl%BRwCb72Qso!%o+(R_YpGI=Dh25VYfvNO$pqm{yc? zxIf_C%Q;1nvs`5K`vG73y*-A#9zxp2k8~u`Oqk5iY5a)cu!SlGwN@w(Y=IXF#=Cv4F6I<@ZM7tgAxYL$y@>tcO@{sfS0_1>Wk!*kFEdm|+nb-`x@e>9 zCB?PvB-ioCmP@p*{_Zgfg9gn1=x_yW4)J#4^xymM9q9CX2q{T2g}^~K4ON!oMFB{= zCdP4aq+LHlgX?`ki5IA*p)PU|0_l6U85qQP{XVKJ2|bVDaEtM98_%`BubSExQUtWa zh{MAJ8# zQHbL>IJ)5=3JFBbtg5LU!OHmLgB?U~%r7pUaQo3CUb%gXUb}_sN_@vsrX}_AVFm1*82VzWwH#tkX3gJb6lQ(&EP6E`cAA z7bRs`a_ic ziRV|;pZt$i!Z7ery&m=D zW6Fmg(IhMUxDAbsWa!F%)*Hvc2!Sg-d!a#tV|cbIFsn7M+^e|mIe4mIzFsnY^q3!> z=Pb(tN6IfD&<2YzVDsFR(#7>OX*p;iu`KALcN=K%Nq`IHx)zkvt?l2wK;Ty}5tn9g$h4;4tlwnX}Z;Z6@f7Np4v z8bPPuC633$Zb1J~)8r1G>+|WybF@}OEuVBv;n_di#P)52fT3Yh__yPp00HG`)|C+ zYj&mK{x7x=9%QiP<*3db=Bp$Q^Mwp6U=^(K!N+koLF zbL2=%nm2Wgsuf-oAzXV4`0VLp7VDJXzP3#m?y+312s#%CH>5q-;_1~T=T{R1R*=2u-NE-g zhV2%S7ZYq)G};j~4m1E-69|L131oiJM64?-O>VV9UVZTi17ibiY7w4%sMvf8EA6?w8o=a&S&K+>kjuk4g_6Et7W=Xk>B{_zQ) zpPpd65LH>Gj_ca?3mZ42+&p+qtxygKV;i<|!w35B|FYl6|KTBvFL-f9jZ%O6dlpJ7 za6JjaquHzo9oNNg#b4SKFkc+_3=SG2Z0o$I4?LtSPz2bQPkB1ifm zzN^XB3CHifM?Se^_oY|ad+r6AA|+qWP#8|G&S?*aNXJE~+5&A44mteK|99~8Gt9%c zkxt6bFAJtz;TuEbM_i@3&7uVnKCm75v^ZT+hSz zJo2(6YPV?(#v~X_mQW`N^T~w0-~Se>-{Vi0ON#E0znETdm1pGfkpJqUq;wnZ9A0Ps z@h?~{COFn)4@N;Fvfr*m=Lq_hv+Ql<9B|{d0O*$)vzv=ZOVV~+56%% zy}1yW4MDuADjZ-w{Kce5?CJ0+d z&!uS^%Az2U4tbh#{LW8kw88Tvi`9zA3n-hC?Qw@yo@0bS2#Mnev}$m&9O(tPag1pU zo)jDn`<$I*6q{#!5Jd#i7MFzd@FS1a*&Na7F);#N`a8bfwq@buO#)BS>f#Il!bTxbXJ@REr zE(OLoxM7VzH%w&dk9 zsc?OR@#gp=!_)T*Tn7qWGTN1_^M)Yy$Yz3WC#EWwOi$MA4LTg|4Jq?E?Kq^>^;s^j zSk0%b)09sxbBc9>MmWO|5wq!`EJZnZKS)=t_s=>WVU1A^y(GLO71CwfX_=sKp=u##h(w zFZrk6d5cj5|L%J~XR=60)0DX1#|eDk}bXXX}4?lT85VZNmH@`}jt#Lh% zFl^C{+HAl14Qebww$Tp#@c}b&g=s7qz9@3*cru1=&!@;5R;vc6hgAUr0j5 zhE%Pa#y*5L|9qZj-EUu1m4$F{5NHEgmQoZoo)Gk-fFeoId4=7ZY~x!u&!}7Mg!OPC{y!gndHmw#{b|FVkgXCOha9(4V#XQTE$`< z;#N#i7^ENJxmHT8H!m`QK$RuZXxoqy_UF0z7l@Ps*OgY=g*_a)USJCyNn$S(?G`Jg zI6FOOe|tnzE9$x=FH=lor=?mcLMiYZY30OHZ>DE1j@CFqM7dGv=(;8decV>Kk+K_# zvSN02!Q%3-n1B8$rpSnTUE=WwM@l}OTv(>oc$@Kfno_b|2|~Iwjzj2q zU*_h7=i>(<&1_1Yt}zCj@jfQ*U@+W0JmTSE&Ck+`CY|9lidEb~CUYhqy~}z&!BG{W zu5qdY(KIw=j^_%7yN5LUcbR)WYBdAL2S1?Yxd=3$Po{K(kk3z^lAb={*4?|r*KU!1 z_G^2QRti;DoBWLpIveTIhzcVmLfFQrYHE!1d2l|bn$e6f-f zmM5k4CZN7)Q*ShC%T+?VT~L-1Z4~oaPM!vg_FDA&F3U^B&Hgqw_YRTLwWWzhAvSJJ z&vWVaI@~zA!T!U~`S|P^t7XHrYa?1-%z84x_Z^-;7;`$AQ#MvRWlV#k4W2T1jmK|( z_3Mhn&od@hQ+{ie@`6&71_JDa z*9e$hOgPO;{KK2P^4v|n^{wCLg`F)>4a?;U;kh`%C8=t(fmL2}T*Iuh!|`m+Vx6!| za%$s)7jyS(Z}7^|0YAI{j89f;Zf_5{H5%ZWn)%r=AN~BNOfOCt3`cD5?r?tjjCeSp zJ?s#-0{Y$FW@4=fT%W*beAmZyeL&dhv=F$Cix3vIA#u=+!qJ9eQ6YSfyN9<(>i3x# z#lufl)CS^?%V<0L;;}+#BD$Rzv!z+h*9hOG-|?88Ehtt6-C@W}hx=rU0_hk;7xJRv z>dBh@I{`sYFk7ZXVTcqBt)7qTNT%nx^~N5Abhb5a;744}QyUH)42VaHss~n_XHuOg>X_`?cf++St zB#5RYOP5GDXE{lD{P;f0)tbdJBdZOQBD55Bjg@2?3k7u?Xsw=l>*0nW@%A=IhbGJI zt$VX#a6Rjh8v8B>od_uetzM5ZPpR`2IEKIX!VLnc`LF-=cX7iIFN!uV4Xfi3!ohKU z%qGl@F(_mB?t34wvpwSI)(w=_;d( ziUM5UlJ09X;8=fF(@-b}ggp!zpm9BrzGXq-5O_Xb7|`3lM%eD-1|H{=2_HXtM4IOI zB@{{)c-%0CQvdA?M{n>CUo6m6x~5uOp*udt^QfzevS<*(VSBqnJC-yatg}F-57dw05wu%BVuU^Vu6u(@bS*F!s!Uw`y558qFy%Y4J7X;@|%=!RD8vcEG#6_7Uu z5%`E8w3m$4961<36w~MiN4m(sijW=8$2gKm!}9nsR2i++Q|=v(c;Q;Wuq&vw;nVXI z9#0#R*#xI7EhM=qc<$y7!+xK3Q*j`lA+$l4DXYsf)NI1I9Wy(9Mk{Xfa9v>9HrFQ_ zg)b$2-=~a1F5QTWb;5R%QWhm)5a7Blx36F4^yGxcPoB^TLxzI^Q516L`ZXG(nI|c? zZd_y3ZP_Jn)8M%d!(Nx7s_C~|2mzDnoOUast`&hJ&}f^{^F#cD8_ZQh7`4dihG|iu z{Sc?!$E>EvGKa^XGF?p&VGB_!OkLSBjH+$OI|>+Yj|uj-m@iiLE!Q35dmg@&ygDB7 z%j0tzqZnPg!*qVhvmgI8&3b8@y;V(vRhM}|fN>k_ux_(251u8pdu;m>~h zGp@!lM+Z0f)%gVNTC@s|y{w@H-8g2kPVM1JDVnB1m8ES?H#N{u*OJjVWO`L`b)InJ zr5oJ7w}qOYqt*q!^l?24clN^o-?uD79M?{Zm7>*Zvo{#<-jfGJu}5836k5`#hF-tR zt1sMPHM`>T<7X7AX3*(y?b;2xtsxI4OMd6|SNWUo{w1QS=y)FIfHs1pk@Rld;r8<{ z(0%@^#9o`8kvy5MIXSza@&dYX%=GaS7SA3t8ul@PkLiv8#l079^Lwwp%4^q-7wMKKt-jEYzCT?vOH1aeaezr5kEh)AB>A%Y1R#8Ra4cPN2gO7ZD5_S z7ah@R1>CwZqTB6o`gBP;Jz+HJ;6*;|o`dK2*dF?*c8b)J(a_`IW)~qfZfq~yqO3W4 zRM7I;Y`xG!4m*tZJH)=mtyIIH+vWIj#-QJ#4(DXknuZ21cF8A}Ls1qA&u{4TA|j{Z z`q52V-3}+!Qx=Py{b-M-a9c?d*`JYqI&74O)6W!uQx7jgXFOt?=4dp{D_i*yOO9FNyG95Z@D3 z^22w2#ozC>xLV92yNR7nD^2x}nw$Wu9#|GcLYx==UW_E+})2>pGNW zhLy*Ek=C~_RHGFJiE&)}d(DPtZ#36;$Gr0Ts~l{PX~zMc=MzT(p6`>mE$;8!=7Ewt zNnfH}8=OYdHVt)EqN<8wHl=fRMl6MWkVwfgFVGrVp+izC9$Zc=BsNRv-M9lZ2uF|> zIcb@bU6t$&x|}=rc<+8qW?~%2W3))QwY7&AL^Mf`@H|SDp{sNwziuNPNv9PN`1S#d zgq;KWuEdvuz;!{`bkgeLgwFf|y*mcaBd--jRUsUW?+cEPS2V&fU6p(|SNv!p z`N?*V;cunf*mv-~E~{CE(1vK+rPmJ$j39_?F03gu*AK39=Z)Xt&;RtFaWR>YXAO>T zHBLf6T~+LCjp^OG$Afp@B{tTz>H03F2~d^zqSIjyAzFRuFB4y&7K}!^-j|_UW2|)A z_dUvuhwyI?USF)N1deMqlQ5{Nh9K~0bsd`xQ?+GQHMVGBXVTVXC;&7_AT0{KFd&pJ zjk4tHFQ;Z&qfv;awh@8NYDeb{tY zQ^^1kwvb_rPeZpqq>&!)-G9XO?Gf%q>Mo?6lFZIdxR_o*U2*fqEyi18g23nQ_3PZb zeao(Cq@+*^-}jj=7WU^aC0SljmL>K*&|$q!X~!*#Z3#l?jnVxPS)S84f|JHj6qWs~ z1OWswj>?F8eUhtZ2(6$lF;xLgjZqf%+KL0JV#Vs=XJ~(j@j`H1Jis^5sD^`1o9{h( zMz=Fy^!)22k3OcJUC?ev)ElRt#9)LiWSOQ$BXC>~$8m9loy>^MGq84CKE9ms^G9c- zwZ;nrgkv*|j~zR=8vX!XoMo27wD!Y?!-t(F~7Q`DHX0SHs%iS zf&kZb5NM>ZmzDn?S^xEHTb5?`VZUjX-TwG+4Pb$Nw|LN4v|a{ zGJLid)2pz??v2TI?$W>gHp%uL$H&*XxxY`9WN0Z!8p}~X{QLp` z=#T!88@KLIwU$~aCbtgxowsgM^@bEx&DDHCKhIedH4j%MGEPWZ7dWRnsB>rYIlgHr ziw!tKwOZm#i;82a<%0k3Pkzb&;g5cwukCL0vlk7zE_wei|2>nVBgAIG-}}K2dF%E) z{?$MKF$FEhZ`@|IJ3%IzJj*-mNJFd>vN&b7o+F(ijWZB3y#FB}8@vzcd!5kMlD;0Z zs8-C%bH?L7MbU6|YFTd#Stijkr9XDe=PSyM0CSrike zIZ_Lxg+ZF2R2<&l9p`CTR$)*>((ex`(-B$*sOHAluJs^dl*A-jW39n^n7(|%)@p`t z!_Z=ZUO&Pml9wlIE>AZ+f40K^Skl-p*j7KLjZ*%veFd7hGG8fOh!rwA#72g|um@d|6p z02L27k!BNkcdOC`_^tcOA1GdVW^9+1ja^WO@z}bA8f2VMr!ih=yfwiuC`H%NCNS1h z+nQ{U;I$4OtbBkRjX+13)e31^>eZZeQxT;%7;f(%M8x{ZLox+>gNR|4kVGj6g%=8k zLZ>+g!#xgfNZxyM&0l=_gje+pM_Q6khdUioij7X*zl(y?V-P%L#-(Xo4__{`; zP%j=p+rZ=in-3}T0j+V!s^n>XMQuWwG^~6jv5p|hc>mA;l;>Z3&h^{(_^luQ9^d=^ z_egb&R$={BH4WZ*7Mm4XDO4P@DGG|!nxd}R-`yoricL|Hy%v0`sOrzs_~sSEwaLJ-B0{rw3SrG#|>Dn_bs>8`Bf z>3l^N$Mn*a%XRQ44UX^PPam>1=}~MXmlst?b_3FZmElt1oDG)&g8KNXcQ%S5UKTa$ z)v~ja*s${T9u)kwTDv+LRnOjEP9J7Irki|Oy0?r+vCP~$S{qGg(8m} zQ4fCl^fSt`VpWwGYe{rOTUR(7FE7scpMLaLygM9l_QB6Fy_78-@#ru9Evnz=-?atb zdE+K76EyRhPyhU9{MH};KKna2xVCeMH72xE;OQrQin?UeRBR1;WKrXwXj`fV zwD8QXHdJLpx+Qt?<$@po&!;G*NJa%!)o}C1Hs>cRP9Fw@$GhJ+VlW=Dn3sI?(NkVL zSg>=Hqfs#3%+ZZxbZwV+-n_x1`!BdWEASqy4MsN|Nw%-$RIX(A+A-*ihbNzq3~Ijk zc*eW@DF&=0127Ox7m_;w0tY zzW+W?A3S*tFr@Pc2@(FcbUwf!;{*vb&hg*;PydEfBtA}gcCo}id5TYa{Nm+|KU$UK z$G7?D(~tQ2fAThV{}|gm#gqj)%Q{oI2ID%CHyCSBN-^H*qX#KZAALcpCDuEJlRb+1 zl2YgV)sKIPw_O*T@pvzhE&x7}!H=SJ{JQ8{EYPmv8c0ejB>$zGN98I?Q`t2KRjd~nq?@-LH z`1}`t#pdKG@4j)5{%FF37Z2F(CnQ-O)lim}SRJ}0d>2BC^7;l1~N~A&lmm&Begb3fo zR~|4#SD6loCBpL)*A-> z9x~Qk&T2%GfsPSTgp>lMq7X(+4Ivg1dg(-dGhiFuBo{^zhpF?V1aA5Zqd(r6q_}6?--wiPCQqZ z!M~&85K3t3FgTYhF6tG^H5k*dzrVv^kn;GU;`BvGTxzYUTZ1=18$2|=ETC;!7|+Yq zlHDw0kmj7v&uL7{VD}pNjl1~uB`VS+Nlaabz(ea?2=2(>=QCyKn^jSS)^S*W+kkvA zRgH`krfo2FO=ym;UOCCof)d7WKIllI!oh%{NA4>c(=mEO@zCaoEq< zG!0oC^XBmpr)TGEW*6igudZ0H5WN9?ERP?2&T2SDEG`k#8nJUkW*x=p2`{pgzBY_* z?vw0ISe;+;{-6JdJDUZ^lRcDltTqc;(~?IC{j^8j)~u@)y(GoCAQm|9gUaX}LMzfd zXP6~4LLe&7haaryXJEW%u#>Q}of7Mo&B|h0$M$|o)7D&Et{LZw^UE2|Dui`RCO!IT zLb0lO@x_MSo3T;XNQ(=ST0tj}a;n{j_c z$`%85`Op5=O|%}-OGAmVLI&?er#vAArm4G9$A)sVrmAP$-rZqqXPfEiDLxpn5CVjf zSZ@(thnu)DAz6^+d{DM%9iaz9{>RTgCwcS;CpAx8#Gn1ePdO{s+&`Uj?dd7XhQGH+ zlTZz!1P}ncz%&ezpBfUL#3`m4D6t7I0P64CX>2`}+pP&Xl=_Np=>lv?}O=6vTm zH3Y%aMZ@U_U-0PsC4cXSzr}C;jlYlHy~EW<7iSp^nz&k% zUY$?~!Fs*G<0x9ovaDHEEs5~7(=&ej^FQa;@Bf0&Kly~q=_TKI>o$M%H~${V-ZB6E zUwugP_!CBX&UnzL@`AJ3iqosBAQekN(hJo#t+gm2u-;P{ORP0LrHFJ$RT}S+SrRy6 zc$zwZ?OrLOLJHzI0T;L^##m}6(AJY_MWu9*%%$u$!2&NNwyjuRokP|OW^tuJX{Hw| zidBQ~VTh1QfC1+`C>JmxNUGYz=BS&xQ-*@~&$caF8oqbyh+?zh*B3LcswTiZr9z7El9Eck_B07xXMTTW z#qwAm5Fr0cgS*zj^VaKSUG&`$rIIugG?m07yTC;RlCYBLyc0@?M3A#>C}j5*rNKGZ ziLf4}1yLj+l294i)?%9m6{pBNMn-Yh$LIop)<;A_kj9F=-7!s9VO2UoJle*pD43A7 z4(laraW2H~$~h@+JTKzxBK9?IrZ`lph`s`F|YTq7j1M`{57x{onh29zA`|Vln6XwQEcU zL(XS2K7aI>ERNZfCGY>@mwfQ-In8*Fdxv|xxn9xBGn92a|L9kNmxsVjcDve}4OA5( z&GE`0ybDRr;RyfiGZtqL(0HU0j7CG=xIN-vZ^nlo6g+!&j+YWCBO(RTTbya2DlpbC zZw<@Un)B%lk@s*m^cdE{k!2|d!@W>z<{i_k1x;fSI-GFi^s{i}I{ zkU5c%IAubyc=w!i$96bxodIvccSUQ4y_|X362}^6JVjZN^%QMW&=hNQnuPa|X;GUM z(o1HG8Q=fi-{D(tzZFc~cvPJ5cix?_pT_))`=_LF%IVc5PfuS$f5^+nntMk_*s7si zEg7gA6elO#|M*u-uH8cDjAFeelA3Z;kP5}&;U2f{-Npk&ZMayMY?6rcb;JGhDdS#( zu`V#RrgO^mhH|rDytT(5Pf^ZLP0y*W&UoB}4oX|CDYPazIA-(NuRw*WBnF<{TgM#U zI_Bk<4|)2@eWveympeC)G0sq#nk-64E#2ojL}-72%8~9sM5J8Ltk(t z1COt$1@nbv=UR_J7L(|VvRbjXm$0c7(rfbJgx$#&r%xALTy2Q8;`DL>UU2R1kl~IZ zPHMjP?hb8RP_>pw^;w%5gJLmj*dN85ovt{%`3`5#R!m=7R?FZ+)=~(GJ7GHmS}@;v~F) zLig@#ahMhU@Z|LmVLtv1wE6}(oFrmBkW0Fz;D;aXg4 zxVv@4m(QQ@%g;XHXm?Cew+#9N9-duswedW@SR!@Y72X6$DF}r6im3t$?=6l7Z^Q2u z7m9HoK6*&G_zwBrK9k7~ldWyCERd>ATY~|uG)WR+yL3g{loYE5Z#*PneIg@GoTt=f ziEnCjlynKMz`=U;vh^+uT}Y3yKBT@xSQ|QP0`O4;h?~G5l_E}401hX8CX+3yWRIOY z-{jM^<#_{rQ?QgVB2wVPVADF!>3YNB^abhp3!3E>ezb!xOClX{QIu4z!A3E^_}O3a zKm6&x;^OLx)uupeMK4QP6eW)y+~?bGevR#W@ACdfKW8!?akeU`T1RCXbRvm_Buc}; zU2c|WXNg6G70@_KZOm)FjFKTWiPYWA*HgC*ru#lRmj=3d)3Pi!#90q7plA)IDRI^^ z8T5&jB8@_|OXEDP1L;fFW`=bcLdVD`rRNit7bVYUO9s7^?VX(EyuxGAT9M`vb!G4l zmdhHiJVHU7NiJpuu@YR%Q?|DH)RkvCze0$dvRG0UGa6Gcy;_BopbJu^b3s{&)K`a3 z6h&OEH~i}lKE_!Okw$3Ut&Me9*WtS4Y4{vvhei>vrYHp7hT%M{?ZWp%f-k3Yn*KIVeV@z8 zb<}8!U)q?@AG~DiXTQKrPdWMQS9qoHae|Ce{Bnsm!T9aGL$m>WOkE&`L)6|e?%C%- zotJ{ya>ny#7E?*~xB8UJ4Xq3QnXR6nf3{#&=ZO9mrVZX7Z#}LkgR+VP8)}RiV_0k! z!JHq*WW5aMi|*o3hs}VL2&KE`ir2&iymPcoh4lvQLVJhSx>JY3$~p}G3LQtZO-VNB z@%`WbA#dHiLz-j=Uvu~F9?gRl<)$W%V7V|LB)y5IZY(BggUYJI^YQw;wU)+ISQ7>& zDP?G|s6!pvU^HMcH#pbQHf?Cs=q|f$Q=%$KQ#BkP?s0v07v}{kP4UJAHC`CLdHWjE z`I7(c^QRmi9Wwvr&oJ{#ivE}{pFSi>GxBoADESVLKK+OX7Z?14-~VH(O$kw-naH>} zx{J#DDCenaL+e~f;^k9@N4MB{<2_1ipxO{eisEw0;jNo|<9ENygAYIBsgfxS4(b7{rOxqw@i5e&h(^4#2 zOyha@^f^bjwy{k`U0LSS4UK8Inl(sb&a)_x33@Z#o+;`PoH&aOt>od z(mF{ba-4qEW0tX+UIyb_TZ0d`Fz-Eo`sD-u!xxUKHR$Jyu%2)p;~YXLbQHKW$Uv?)_3eJ+GDa-YmVVWXU6mF8#Y^sLM zW)TR~jYGxVn$UtyQ=C_rvcM_VnOM8tIR6{07fWdSjx@HakSbssbQE|0S0DPSq#}yr zkg_TZhJy)z^LPH3-};CDIcFl{Pd@r7aaAI(-(Xf&C>K0(J*81na=KavHSF>frxUy} z#MaX4m?w)Rs;!tLF(17DbEapfAqkQtXdMwnlEq@plgE$v9c%c;H^0fR{$=a0IKDbDE!jN?m@=;pvovR`Wthl3buu z%l2MEUOIZcj3kb@oUJL=hP1D-ScbzKHPX~NpcJG>049ggrF6`j)Ascp`6YeE5W zs%Tn=X+i=}bP2-osE5!YdA4`dCrJfD8oUKmjBmsD&v%!O-z3D-sg1^j#E`XguwRQ* zf+*HBbsKzMT9GCZ2uW4e*rujgu84;cnPxcK(r#+-7Uy%6jya#NSgkf>y#yI2xF#f( zyilPlP72(56AHwQ!Bz!nmeZSziIN;AQdCpmy`yS84=yS$S1nJkN`BXb;r!^@UGkmV zn9Ul~XzI-h*Icn$Tu{^nWyhL}BZaTmMCFDi&L9fS2)-_mS)WJ>WK+@ZA7kSlezgey ztZ@?jUed8xPxD9uroW0E%OTR;wrd+>%gHNA4<^TSd zzoeP$WBS{eqkTjiGu%Cf)=*YK4P6n`S;`_LZL1BkKMKmAso|BPsiUx-l@j8FxUSf` zc7v;rV(^BxH57Bp)A9*1&YOK?=`iUSXYQ)oy4_i*p8{;_&6%F zp=}TzzHvC_Pk!+sPaDfXdFHcogtHWjDVxQV{^1_l8@BJgjTmi{^oPi!H~4om%jwk& zsTAX^AkhjXB}t@l-q9LEQ`czcP^M+J3hB=;rwdXgc;nguS6_V2?CFk*3TL5c+?=FDq(iA;{0O8 z?(GccExkd?@vQ^a=WAB&oV#D&WAS9kVqKx~h=aYHIMLXup%#L4tIz3+X;4m`rrFf= z$1!i*ev@Tc5=j}(FxS%WDbNwO-s+)?vZ1vRB2Jj>Z8MpSIXbw;-J6;>Z{Fd1f0(en zy%XYyUcQcDhucCJINGYfv<+H^556ci&>2{r3q)_{Jvt6CT4Mtg%{z-R27tP0sjG@| zwWMwvs=DT#yEj-~Ub0@U!kQ2|M#xv3BX3)pMj)+0*l;sZDhj25+a*(kk|C9bzfv?DTSaS&Fn3RiiQ1fzm`tMsGCcnVj4Vd#ssqU=oXQ4|HRqxU!;WN<0I`se)NcYmAz z)BolVxvMKqRxhymkZAP`>n-#7oV9aAQH<0PTd`tR*KF#VWI3g%Hu$^`#t>&2m50;m z6zwchd5Y5)BuRoXhBS@QQ7G?jnug~mCv0XjzW&zR^mmS!oj=FQ@H4IF4N0az2+}P@ zWh|T79N#v`G(&kvbx<`+YmseBrd5dE3yIQ_B#&ucDJtF1$~(*X)fLVKso$6e>og(` z9hkN?;bF3ts%^sX7N&x&5P;L_TG7u*<;RDw9y=qS*-BPEzkH{7^$Ky5a}c}lU~&{Qp1o^bQ}HcjI=Ia!lr zp=j1xOA<$9d4zHit!-&GE1VbDHiQFY=OGeNhhyjpvAwf_4~0d@P6`kq5?+?3Q>_H< zRT|a7YE^-N;UuT3TiVvsw1&17NIgR7?wR%C^(x1Azi#M?Y>~p#>m{_Ur8O37I=&b| zg$;feO%!P+lY}d`plVHc@hmM-)<-5O$~sJ0aCR|c5o@G3Skth5?TDp~@xlZZE{XyL zyQ-lM{vF@8xH?z_)|(AGTZfDe?{M|SPhX=|tJaffkI*q!r6-@xnXW(OU7ez(M5Ccz zF@5qYPOeUPF|SY}c>1Ie;4M)xN2NI~%fl~fTAXvtmMapkNcImYcMow7K0$StE-zIu zTuOygnxEW1#r^0Z(b-G>!5{oC$3OT!=T9CpQI7KQM|}M39CP#Qd~S0#_rA#>OWD@p zLSnq9v5v}v&vHcD;>Vy}gPV>}TYI>(=eV?oC#dmwhmT@JlHf}aG(q8#0%Q^qoxR#<^Ghc5w-rZtxddka-8Kd1Xy(~dmjRk}a7j2rVGuGP>?`K&L`&YMXXM<_ocdZf98Jtsywxtb>5S%q^7Bk{BW#{lVw{F~| zm!xD_9z0NW%l@@(KK)|G?0m)k^*%|zhp>*}2o|%7WwoX#OLAjTA`EsyhReRvn%2~` zZA)F3lyybh*kHl%HP&0|#&9~H@#axYpCQ6~l<+uHQ&lCduBq2c;tnAsqqy@V6I9%` z;E;k}UR=@NKSa)E_^VSyl;XT&dh(19fAJ&!;IIENqd4Nve)=o^pYxheR~u?)$*WOtC*d$ySsxqJLCMtV_a1RRok>s7oat|H^vKv zU(LgrBqcAu_=N3##A_yu7G5INs*YJ<0nYKH}+9!<}1i z(X5wz{_zEe!yH=2W-;gS7iZjkdl%;wQB!a@+NNsb*PX|{HjMX&1V$}>kw8bs||5q66KOjS)jl1y&rJv%_DyAcMiFB>yTb=h>~%DQu;8U zxYsuVi|UNju5GZ^(zck7aS|9ox(%x=l?bYh zA&M21fhfyp-B&7HFh6&`tj<&8tV1YCZ?Z*iFd)uzR1_hEq$)O)n-!)kNp!+L`ul&# zKY8y4+w)l*lp(9D81=VVS zO+v3%)6{H=4Q4c8T{j@0HilRO_xdA#8ZD8+K&w%$V3Y|eVTdBZ z_Hc`r@q(giIXD;*#}cgtQ6i~}U>cqba+b>lyL)4jB&Q!a@;D=j6p@422_jJ;Tv+qQ zQ52G*ou^DVhj2w;rQtEHB#wJnW6<4-#MxIx)fR6E9vfbBLjh0QxK~TVpk@Tn8m6^G zv8Grzq`fqRCK^doO46YUYKOI56N7*48>Ca3m9VHpzP^Mg6`oN+Ai8@KdPtS-*m5!` zTU8YjWv+qNt)SBAG=p9r*ED#kabDoOp`l0V1yXs?3a118+ck9{jT?(>DzYR%H`~T= z_4Gc)AMmrk_*4Gr^8&Aq8BKN>B?)^;L?RVJ1ckaZj*0a=Z(3>> zs+a}}uDYQYz^kaE{Fl(xX@%LvfwhP@LB=tw=bs~rMMs$ztcrs1ASd&Vb=5LoZa6!i zk@ZJ3r*qa;z|lU!c3}l;!L-5kB?VNW+}_!ix~{l9yF@~0rtsmyZrc{?JSq+a?bds| z3yP=|GF+JGypEk8%2^B2;JinQSKi)mHa2z5<@p&^xn!6sY`H?{7+bHf?G>>$^d>2* zRYN~k>>cDJs=+!>IbBngp%@z(0xy;Z6~{;^17EAD*_0bLrUhXU!qc`Mq`~8OdU3{V zHK(8DC>a7tbzM_d6^p83F`v>KOv14J>ivYnv@K23a1=XS(}sE?rLaW>ZH1JuSg!eZ zAAiA}H{K;nC;Za~&zTQ~91i*bP2NqSj|_l}Fjip6SyZ5roFV4@X+ZvI?xTXyWU*`~}MMg2v-T~R|H@N!b1DYs^FHu|5n3h;ds)uLP zsiJ>4;okNkw|5S?v44$KRZuk*&KX)0%B9!!hUsd`q&MPt`;bjh&{P#kl8{6ZKF{$; zY~@f{#dd!O6^(Fyh0-lJfsH(}O{fda$A9*On{V%7OT*=K&15j9sV()YrY>u;?VQPW zkAXbq?COG5U67{~VH-N?leM=Yt7XX_V4{#5SElw(CF$ zhjCDlUU_e%3?z7T6xcdicAS>LZIT3K?bS`ydk3%H1rCIC-B8tWdwjStISH6>D{Si; z^XjFgRTx^n;);Lu?_Ry`$WsUggHezfh2+6!pR&BX;2YoiPN-QBlKwd7*}0*aK0}F| zDDgB4L*Cb6-P-z~Hs~-UMExG7_{vu%qz(fF4xPk|_x9;+ZISl}Xio?nbk#C4Qmo1X z+0^{6zWp`+)|R8kC4M_cBq@=Ei|L%RwP)R~DTN9NOd%P@5v}zUZHt|r?XUESRlY>QBGmw?F)VzxDNR@U8FsHqSoI ziL(r%P_R6mUGeDCU$eP*N}QCmbwRP5qmmrmg#e9eCR1sxWu`1Rm?Ljo4bCdwzG_+02%d=DN9vyPHvqf1q%r^m2crebn%o37$O$`*z zlh=lK)i!Tl+vVBpf(XU4*@8IHjJ9%SR~4z2q6j$RXx2-zToGpi(+b+wVNFL_cL4|) zb;jb>RDs#%gDMcGIfLy1qwQ^0^EuO#6QVd~_u4Vzy_3<09qN0v*F_6d^cbz+NSv`c+p`;R22MnTFY*o8R+lU zw6BLa6{mQC9_ZTLy2Y zBS2@J<#bW;<>{QVZZXzyypwP`oioZ5rl^rh62&o3F4wG#K&KgTn&9enxbQhcT5V|R zA#Mm>X+&LP7jsrfc9NXU^*c2EA;Vm*wKn8!LC#Zf0qXGN&2sNhA7^Mhiiz<*WIa#tVj#Lm`mBmNQgsjWG^cm$+nr@cqy7j772L`QlNhfK})B0`hNzXllH0_@<$( zFOgA#vkjxs0J~Xm*48K?@ltVPcbC8Qjqmdf6%##vOg7nKxV4QPZBdzu?nC4d6i5k|Crhl9+`4y-XHQ=8;^c~J*C!;gChI3e zkzi{tWpP@dOp3RPi{~}7Rl!zksTLb%)rwLnjFZHX!rB@gMX<3a0rN`&oJ0Et=KKQb{b0keo97VKEO;cCYZA04xb7LH5>>plZ zw6n+Udn=wleazX(IZe|9dDPUHx>jMtFqrq)IZ` z9r{kSGy`9pA0!=t$JBR#C5i zEfWsumoH9u{`fv;=U2$K<<^a3`g?~|bwRN#X&ZwxO9U;EgoEoDN<=IcEj#-;RU6^b zZFI8)4;yQ-b&cwzYU>Q+oh^26+(bnQ)oKxL5?M}+K}k>33TCsC!>w)pkALlZd~3f% zxCZZHeAVL13R=sH`zwq|iKCRQywAEQXk4hG*|ZHvg;}kLB8689=SqApl)1{XLV7{3 zsaP&9$ny+|XVFxoO0lY1NCzCheU~^%_~P+H{&rJyV{eQ5Ke>b^!nO{nGq&;`hkxyF z^6dO2Pe1z^ZaJsktcfQU>Fn$8q<0>zf|}`_;k|c`Is3+49)9#Cg$YWlj3RWBvAMbm zpF!Z^bFjC~`68@_lU|P(n}YjKpYg4C-VQt=r7+&JcznwK6=aQOC1LIiaoNzHdnQ@T zgSJISj_dbQHY-6@)C|%dLP+i$9dePbnQoSB9rf7EYwo@|K}k=w@(8b)uU5?F2J1bS zms2R_RHowSXbZFh?aBM$Hs7`lNu#x8HdCHCp0VKl?>U8r`{#@bLU_k7tiQ=i=ov`u&vIqQXaxz1=>>NSq6> zU~9uoTOeM0{v5B$!$Al~(}XaB)`F}T(KKN|7C7Qm1*4&Wy0M|$R|N!vGq0Y{fT{4N z34gCjfs(AF2z_u!sU@--%mslwPul_=0JyG6ZOvrdCyo^Jra>x=^PZ|Iky0?8Y;k^m z#V0RTEXtN~t~nTLrkhYKtzVvEO~8Uc#0M`*WIm*)Rali9N2J4$B&!ud9N={vjP4Ft zPFWUfCgTBmu!Gt;AX#40Ho^QIDtu`U&au5U=JvO~%k#ZMCQ*zQ zfRiL!VtgrYI_wt2HuCP^P4D7EB$IcE%veGWgAm!PiwtPZsO2aq#|i zkvM?V;qvS{Uf1h$nvhU+vP)3E;sL&bFFO}hCMQ8SybLvF%1fLx)V|7M4Dn#P&J0UpONMRgpRtPSHMZwrll+ktaI#cZ}Uc9@riFp zV#UHXc+&=)5%ANGKj&<=CfeKM58k-Ry`wjA{fG}24QEY@pI(t?5&bN~mnEhtIN05x zZw%*WCp@3eSf4zhEmq)cSa-|Nl7WyUyN8HsLp{61)n(|7Y`SEwR1A}dAN=;WFsqUz zN_p$>fRV5G+TAHBE9?!ZikO;CUrLERfn&=Gi zM~BSj3m!gy!Tn!<)^VL=xQYAF=jN?LN{=(Xi^>YT?j&R#Rt{2yH9(eXM$v$&^lUa2 zQKSNR)V*4l_}Blk9qa0KLZw@sxbBvP2&+FKRq K)cXREx+>g`i@Q2-A<&8IKRB& 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 z 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�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⪙Nw7lwV! znM<|s9l2pnX9EwtBbbbBV>ZsU@wct~V|E)Bbt*4tdnqQU93KpAf;VNBN>}LiUjKF;oXXeSx)MI&(M;%*uCEI6LdY3Jk={&4=F0iZt$bjk0>p_6qOZ-buTn^#Wn zq(SAiwFi;p8@*3Ao74CjV$ubgQXr&;l{2}d{dO~5RHaszD=Vo@IN$NIVmm$cr~ zZ;XWjPJUAVSUFnbq^eK}x&B2w1cUbPl?p>IBGk~tO9AY)`8Rz1;#st=(<2o4y$!=8 zp52wNuEg$7&Finy5P?k1=4Ncd^l?4Y{@X2Kk4(PW-OGuoL#8yWol&rHv&-931`-j` zN!as0M7|cqwVU+^r81q)zgX$tT^8^X|6yARWAiWY=TK4DoerzGQQ)iT%o0|W%l|ZF!Re`vObJ9kwSb_XZn$l zT%dN>^uiw{ThO(*y*UvG34ky~lDWwYYMcE0E=D}g0~bRX=kE8W1hj0%Bkg!ZdMQjm zF_@3++nV^x&zfJl-K-Yn(DYFSMYQnVS-;bD`Upz6rwg@xl@d~qKX7A;z>O3aTNv#l z?UF)`MT|&G@b$Pbl6pT8xjE+ILAMI~RIgdfSSNz%zSWkr@rYiTrV4Laf@e$Kov)i_ z=V43OEV=^+$=VQEY4B3~J}=eKJ3*z{eS6^VNh&R|Fve4Ps|@H8;q{V_mL-D9B1!ey zy!sY$JZAeuJceqbF~J{%u!?OV4k$TZTny5I^x$au7%G@oa}K1rx}~#(sZ)g46QGMF zSVM#P4Qhj?geqOqKocP?8lJSL05YHfJt|XJfN1$R8t_s2o*&x0PWAo(=0i*FcH$P#b$)%zEIEo*TMrKk7+3UA z%HR$+fr3R~d+0^$;4Yf#oKzA67Y4%*Ydq7G%O}3V;$&*-k{V`M{@OpEyx@wYyj^-e zhRT)B&RkcCTLPTNJ$=4mZGxyN%{0wqu_Vu+pdTUv+|t(WRz#Z`$rWR6ll$2jY+pZ? z*o8(>2cRoemWmip4-WF7kp{!zsm1)6=L~kE(}c&rS5#DtDH{{Rw2=sLiwxv3!CvC> zR#q6@7u@2!5#X{}-83&T>CqoT8=DFOCEdW(mIBvhO5>L@FcvM(~-$G5SystT`?T{hHKZB|p4f1Xhp0r9Pd{CZZX-0wKR z)kU*p5`@*&6{Tg3tF>^6h_iW9C~hXjI0#i$JE*7}qZxNR%J?J3Hdol*PGtBMR4jXI zlJdEdW!Xmx!bsVcgDl{(HWFK+qNy8`m?#ILbmNKPTWE8%Jvg{KJM59#oGq$0YYSg> z?P?0=U`kW|8rKM(BMn3XT4qX*#Q+ckShaL?cvbDH! z?L7=LemsUKf5CWPXlbCidsToFl#!QbmPjc&tRV3xofspHAsHCvyFkrk&6b{?zId)+)yc)>91@Y41gzCD-JP{l3R38la*Q z9>V1bJK0JW)l0`V4ewiNfSASyfs=R$H z_o6#)dzSn8^sVIf;IWQLvjzK58}neV&1$KNuwQu+;pt+d>lS7fmDRujJDZ~he3*Q5 zC=8MFa#SPmEA_^Wj?*@u<3yPxJG_|om_yxZg(h~cI<6gY-&)HJw!G#RN_Q!2et;jGvlS8;OjbWHs^KWLPN zTtZKyqzg*4GgVn4DjmOKeOgpcZ?650ObrCoIjzFH87SruUt0@HL8TX_ThgZUg}&8} zD&QfAg~y?TCEe(WOOx25X+>E-yF|1l6SF67A>=}S05w%XxJU&DaZ=SY`vbV2LY+HM zQ<7B5JyWf*RK8z|p{$`Qvg8TAV*@$ytHFGTBs9!OD)x-F6!y)xELh!3V;cv-nfFjR zYw}+oA4lzGA*51A58M3$gYhy6r?c4r4$aa zMglTyf@D9D7-%$YCC^ovE|Tf+f?Ocv{V@A>JJstM)9kI86y^~n}& zg70m+M~*hSs?EBxB8T5V*^B|F)_}M3@G#@b)6P}3*@BvvLjLMk=apNpInmS;8mUj3`WgD z@T#KBvYH+wLT=LAsRO))g7cKfa4J9t+gawdyPTxk_W<0`VTZ_PD=q26r9}SN=exZd zT_)bY6GjSEHzuqyv5!?FAJ-_Na40S9;h0M%9l4{kv>pqPJ-Y+c9K*^~XG|j^fC>)0 z^RM*a&kLe>SG5j~er||Pt90;z^CYS{duJC* zkHGj+D5Ile<-nQB>p)na_Zm9=)K!qvIQF^;z7Fm3d;?sJ7y?x+XMwD+RaWjO9FeF& zF2&PjRdJYiQbw-l37S@0fXFluLUQ~48-R+ZTqt!LEoX-11fR4NoctzfJvvC2sm8dMW$RKJc z)G*%Zbqa&Zv!cV*J?#{6Fcvnp ze4J}0ugur5x?1U*U%`lwn8<9bl@jUxApIvT?Y_#rn3mg9~%f z#bBB~Nir;N>U-e2|2@t*;m4991_#`n z9a2kNYK$}}7rgg48N|HkJaz62JR%6Vxq0tr!84lKhr7sW4oqBQ5=sG#TGP@B+9>MV z`8^MUxGNwV@^Zf}Fe0G)`J>3M)yt>Q?PO^qBO{@?kgwt$KJ~3#pYrJK+z|4e(_MBW zPit+q=9$&=#D?^{pIsl%(t8f3Hy_@b_^kN+gfPoTqM&`&R4`>BV*RoV*CXEYBWfsh z7}4g_Lk#Sfk)dzf$cQK>-1`{3Pxd4==q~ms zg&I>=`dWHq6#f)yz^1zUZ;k7G&^@<&1~1u$+O%hj24aX*C-RquW?a+skcXF9fk;*I zJ~8&~CXBFiECZ((9l=Yk-7k!ZxFvR73bi9>wy+UV(&_HrBC>e*W5LLhr!=-V2uhiV zIH8Tko;KJV2$p6n9hQEx+I)$CqW1I4XOItxtRM;U>rLN64hE#isNW>Fj=hv^ z&H{6b0NbLOEr)~;{hst{UP%3LM3*+@{>OkCXYIvPz;#2`*b^hsN=M)TS1#F)O})Zt z3vXs-8JOF(L$ltwmIcv;Vz3{vx!Vj0 z7Tqn{#mtd_0XF;`GAdh-O2yim*x_M$J4Ty^UhCs{99+Mee)&h*?6Mkm7`3xfsY;mcvJ%e{Cw6~ZWw>j|(R zh6>B6Ru^zdDzM-d1#98Gj`BX$o29=8Fh?G4eigd_LDO93gE1Z22~B)7Ck!uS({$X3 zSFn0%kYbs(#a9&_L~(_CW+L<)vDxXJlJN{GGTBrcKtR%cQUBnp@rh<;aaN8et!+dR z9|_3*ADEaBtNq!Dpn&4#tdiX({ zPGvJ=F;aTdG;rR`mk1!!RVd#-S#c1^9on-4K(Q!ck28{NL1$Y>k?ChZUYM0773WZG zQlG;rT34XUa+x_D6!vTdTWN8XBwwtN)52@bBbTqelK>L%f#Kck+7n-UZXE z1oMr&e4D{DyLtgin-k0OgWrTMROx&EUC_ULx(Laio#h5vqlnksVZXJ~nXKbu3VUyQ zUV>h6wC&+#Wo4O_83cCqv)m3)){M+RjJNVV8(+e%Ol82vHrMVy?>G?_R`sOcukR zdDlFf>E|cKl!@5=@gt-8k?yXjOirWL=HZTwtKjDYR|!Rkcevbmpl^=M#I87&y zJtnZBd>9N&!WS4}-yMkHVF0YdT%)87^!v_=+0#h~Uqv5!bdM71XUOuZH{ zn@W&?M9im)LG7jlr?-Ab@+N^gww*x^O}bFacc^3QFk*yi9Mu$MMKvW6`X=jxj-oBD zl@9{5R>ZzYE0j_Mt!T_V6;Nnnbnr_vy}5Y;ZIo47&^Rfw;wUnD|GX4x2Yi!6LqEmB zPI*?yVcZOkMC7P41_mGVqNuwVU%NeV@y)TNy8{CFuWmV9593^zpWC>a>8aw96~QcS zqD$Va(w+Utp|cnt^-&AFAkmBVL8;S^szUv8i85k{1%;kKBMP}TDu`}QBnHBp}#h{PN}r&zpGZPY-=<|G#u7N!#)A8*mYm{m!uXmQDg zDnV!QYz_K6Xex*bmm}1|4l8ef0C20VqvachR0b%jw`2Ix4l7aSys|oWqWirnHN-s( znnj?}E*N&<_HU2Rr%m-Hg~OVGvU^D}N5ng8JBIrgaN?zpYqJn*LUOHHV#sfcEouGc zy-jyg6uvEL$w)DYR2#O1m#(P;bJ*|hmRaI{R^#e8O#R z1uO(x$Y4D8lm+I&jQEo`w;rv-Uo83N7qvOkNJ*(nx*8KQ_AQsGQuYgvc?t`+GqKHP z2iemzIlg9OT($lg7Njlp?EGOz-4%uHv680$-F;5rm4NfFNUIKjBX_$$Qvw1*`jVbc zqu2}^K3CPX=1AcI0De(@eR8`Ae?;M^@~V}SWez>;K~pxStqs9$I+u|yq(2%5QYGjmkgajoNR&>59h=j8TlqLzr#9hz<@IN%F}UQ;Cl3 z&BKX*2O&N^VNzsU9H+RDos!2mh}aZOEDwTBe>)#lzFXQEijP-wE4GvLXB)1>GKLc- zjBeAKt^K9Iw;E7vm{<_(W;nU^Ps?77!AHcz!umPi%5-*%iV*eSe=O-vAq_J4xe!rn ze-x|ktMK-%dT!y(k=7&4`=LMkM_KZte57P#J8rJjDjn?x^Yhw$zX0?G(1{f4vi*7V zoc4V9-Esx5qgEgy6oa(VtVwv$^9bxZ5$(3^K1j$2pph9=RCM*Eff$IDcx2I=6t2CuH(t~}wuf!drr$(Ip@ zw##-(YtN>~apE536|Pw;k^E;^&tZ2+Pt?C!AHX}=zY)awXNxm{7J zs2Fv3am%`E14N`Hb$@`~2zcVE%l5sXNfcXLd}938LojkX2arwi_7*96q&@a@85e=} z%uI=HWbC6_8dL0Y7lMy9?fmyjI#|b_L-ogY-@Go&LzaAJ80^|aT&Uj0Fjz5oQ2zO=bU6H(Jxe9k8{n3=b z@Cdigqo_nuL+FU@&$RE_Fpuw)C`(O%-D9;4;ydpR zeuTD^J6|4Mpb1(N;`ZIZk(klr^pX+6XxvZ{Vn8|UTkW`!(wH1PK=aAcAvbz2@E=O}~;r2(K#NA!X0%evkPC7(O z>wTYaH!$0YyVEp%asob*jfo^hUu;-B+c0RI`Q*bclLtS;+&4SxNctj$3fJnAg3_?Z zUZA`iyGozk!Y4RV>g$m~TgpN0KA^Q_Y(t7*)@A7ZP8o|F71=INQ5H{;K_Ww8O5kpS zVoa2oMS1w?q75K>Zgu{hNdiukONRDz@0n(M?z+xDJb9;@EQBQDE?*SOxC1{;O$nTL z9a?>j^;B#KOPK-jQ!a6A-EEgjdb^-*`X+;s<-UOANabc}K+GgIuZ&iSw?(uHz@LxM z6jHe27i1>peO zFRZTJ2{$3|5NI=W@oYri>FA4pG)r+9E1`OaCea}rpico z2(%cCt%L<9LY?)h%@T-`bj*zvwp3c1M;~66^8YlVJZBU8Tnv66_u_P>*U#OSnhqvv zX}MgYzUKmLTjbqOUHS0Kq#%;@JbI={Iz0H#f?${DQ+&T>wG}SrUlQF+7Htxls_1fh zOz|4-*S)Xv9wr;-1^GQzMK@I(x)oh7q|N*u~wmV^yB{>boHVatE<_1ppL|(f$93W_%7n|eu&xF%=SS)nbTRD=GJHXz4gr#Ei%s}-J%bD&-dCkFOU%U*dwXt+@0dgA0H1p?iyUM z!x?;uQQPw@0@PZ6XxGLF|BJr;05Q!w%n!)}7%ne6##%ilgLvYOpMR*w6}j;M?7T{qOQY36k@A}Wzl{B0VG0o3LZV?{4T zciXxxENScED}5+&a`{G*2?Om;AKf|1kEZ!qy?ptBA?CDxSULo55@?QTyd_g^0XN~4 zeGafN%Q%6(Yl&c@iIy-jp{>OL>xi^hep4}|fEAt@4>q9TP4Xk9D1HR|muHx}W%3y# z8ijfD4hEF;v+sBBxBRFg9p01K;9bfpuKLK76@sIgE=jWLwgYgBMw{KcxH#)H*?{Sk zRf-!o#1fIX+iJm~PjE(>0At0duBG*|IcZNU1SH;ym3S85LX&3(;JDd}H_$ z&Nc1PC*2_L3cuFm>c%rsr_@fx zp;J;LZuOv8|9$QRt$=k7&AQnOyBmgmLvoz@3q&*i>ShK1pGt-Yi^ z7Fp8(gKCn3zSW({V_-ebb5-|ys(1dEdEwq3ecQ< za2Iv1ncNZ`!4ql@I~}~V4jX_aatV1QN(OD_&(6&aBnAJ$m$=Ro1radBCgQQ8H0!d& znDa4##G%t^H#vBhfA>`mEV3GY}ha@uU5 zV&@W`35c&BC3TvP5&2D)Ex>N$K2BHq7G~Jw#T-#6-OUxXyX@Ee=|i?j@Eyd9&3v+3 zoKq`f@*U{Qn6=^j?BqSW{+G%9LO)V+h|U22R)jlveccpQN+Mq&Mb=}%5bEPQ+trG} zjrv^`LloV^tjC`p;M@S%ZD4Q@TxwHPf&RCtsoJFb>n{^*!PpbDm2jS{S}P+Pu_XW| z&)aGiJ>Qe;esBhY5!}Q$136`rf!v5o157PAH`b@fdw(xAg%p*edZ863B1zyYqFSk9 z2TYeroW3552Yv?16Bfo;fTT;w;kLQ5l}2A``l2EW`i=B)B|p7wR(jRwmlsvZ0+50N)#l@+f61BB#sQ&VnDK?*+l%1ofbV#~rK`B9Df8E_ ztPv~)2H)$zsnel=U;TOdw4MsXB_JJqc%pCTC%^`^y5>184t$n4f008VB?Sd}SmXlm zbKgxlHMPkXMHIq*WnMP3wNaqZuUYOjmzz8V zYfpKExs`l<^}ovQTn$h^mj4kYj=d(%JMC8#gnvD@p~L-`d(TyLVwl2cH#1a%GZjQbzrd{{4u7rTTR%6lfM}vVHe{B12F+ zTsaI$`u3qUFN*GslX=V6^F^}S-~r=|r@uO9t2gLn8rO|MFnOww1O4I;mL}elHe#21 z^qmd9g5GWZz)L&o#a|yEdd7VA@e&l7R#sYo&AvG0AeVZo6PLwWNT8jg_#{AA-ZdNflMns z0Yd#(T3=9!V)M5w^~WQ~<4G)tIK;Li+!eL0iG8#nly5C(mXp;?X->${r4>_>;z5?L z!a<3jZ2Dn;)?W4Cx%`$V|3>1`W9IE_F-t>>Dv@|f7mc1p^~de8cZelj=Ei_0!Z73A zAGTk{vk;u~6>wNOE0*PU7eHYneFxV2QFcJ@OAa95zyzq#m6j%EK`Smmk zPVU9yLr_j;oDLQB9xwI%tCmeS(z`w#gMEasYLBh=ocdjNdjHNg2M~cKw@KHYHad{{ z!uxwXST*wwKy-y6R%HH~wqt+Q)3kV*lY ztkRUiz7*P*7utNuAElp~*iKacZn45cp(dqNg6fo{$}vTR4SS+RZ%IUEenQO);e9muO2==?gwB0@()~n@8$QobeKruvO?qPPr*R7X-nS5 zyQqpun@rC18G_tj?f#FwWfvn_M83_9!B75)|EMj#-7w)iJu1xWG9(9*&gGTVnpxd7 z2<*tjo8%7XAg^Rh&5SrapB@HvUjCY_K6WhDbo-9T)XGq5hm{w6z?HqkUdX7EU@?)Z zv;;!c6c{7XBOUlblAmF*-pp!me`@LEgAGZscX6W6ROdBk(&E7)FqF}<2K34&RaLi3 zoFS0~eRC7j`zs9}>5TJUe#Dvh$ zLs5fB$Y=;VIkGM{MHDfHoFy9j{5k0xoyNw%NBozOrS3;Lqby@L)1(!dOuPIb?4`@L zyVC6Fw}F*&qy!MTR6@qLrD#ZsHdX&=P5H*+F%xDykGMX4`jpnY$9ovrwvFXl%$N<% zi;i-VHSE6$?CfR0)u2C~6>6z9E z(+}b241H>My`V7VJiOot(EA@cTp6XJZr>IfPvl2|7B< zK@F4lUTujO!63(U@g!NE7`D-6`OM@#PYOR9&ZMUk+OaEMC!?M)Yq7{m<{a`Yw2$^W zsXu_Uju9{r)+M#Tr=BmW7Vr)+&(QKXg~6%&QTbjy!QEls=P|~#R5o8J@8`M+8uJMrDkS|S=|9dv(eCRK(Xb$WEw}IzkdaVKxFWWCfZTCh&H53 zl-FWUe;rbC&8q$S+o?(M`=LBd(wb@PAF9l73Yg9h!Gpm5M7D8^2INkOh!xZB6nnrz zxYTsX&U%tzkLxQV8{?1>Y1k(oeH(2VN~AMGND5id)E&_+QDoSO#yhN2z=pk-vBDg^ zaf`cB%@ylfQgQizEx=@(jvcqMKrMaB>Mdvh8ycKy9PqF zlV|atDhEd)Z<&b32d_!EO-pn+w!W%0tT>XLR0>H*$XR{B&%f=--!91ax%@|7SK9G` zdtpHdYSNHwm$#ZggK;LhJPu@rho1@$>Y!m3eXfLoa5d+T8W}MNTN0u_gU73ly71=s zE;~c5#fFEmPSL_&nd)Wa=E~qg5SHXn`?0eRBWi5LGilOm_HXy7{@jTb744RKXl}J{@L!l@tHO#**07)8<9kJ#9?T>B zRsQ7IG&bDDj@|u?6?_{N{Kb*!R==lhe8M_jmo|lEbyfUd^QLX-?A~Y`t+Z#JE~6r! zgGeuz6tVbEv9j-3OxKy^OUw;9GwBo4syvE_{=Z)%Q4JHlNBW5_Bl&*-Z$Xg0ARne6 zpePJ^GNj!(pxS5Dofh@D!eeJn5JZL|H&|UTOmj}3eGDTd*RNmao8S5lK{X;xQ=WbHQ~bUE z>_3H)w-fm>!(mFJQ9~O;5PF9VqmuNKAt0EUm_l1ar_*6~Z=1_+e~*og4I^2(BP9^!t5YfBkiyfBtzkH#d3r-K#wH)RP~`&2q(! zcmORLnSCmRfPQ~S6oq)6N2}FBN-#REMu+KN1h?gXmiKbI>e@q~Alq zm6Q@B{OO;5mD!nD&OUa|8B9y-#Awj}lkoMv{uMJjfj5S1_M{W&L2{P?W zvpvrvNt4n8OtO6XF`l@1k>{TKL0*071-|#jE9|VU5?3P{^%*wqZK9Oo#`S9~pE!xN zg5B+1OaWQH&Ccc)t-TguZGq>0?5DW7vB$st%D>_9)5m%D+jsf7pZO*J;46Q~pZvk^ z^Yk+x=lyrCvUq-u_by-IiI1J+-77a)STua*Gfy$>dR%|^9iF(f%t6nfynq;p=BMa) zclq8MFZ1c={u=EAH>H}Kn`3i(kN@F+{O5e--+l!zjso&L2Q*We8H2WmGFd6ygXIZlZ6K{PJ_D$DlId!|srgBS?hyDs1OcOD zm=W4yv_S|*z77xsQAiZ|G-@%GO6*2yu_NV6T|kx+c>*s8vBDKv+jnoWyS+necb8sw zNR}(`XBY~fG_$<)-WFHhd7rCSZqnM@0{YbAkayqM=j~V8EFO>e=`Wq)!pCbw;gE@D z)0qvH541#;3ZYSPW5(mCD2mX!AkT7=)NQ=&c8g9&QmIs^R;$#a8pas1Jac?4vB~a! zL9;nctyZJZDcTx}G$lkt2 za)hTSigK(ActT;Ni{~+A{DidPFu}1TS4w3t61BEHD2kLc zO(}{Y6OB3^F6ew@s9In-e*6SMwMH))vbM3toWDdWER*#*t%E&8=%Jz-&s;dpmG{@l zcW#l^=2$s(mTq^C{MvP9=a(4v`rH}Z=G2K(EY2@-_1&wSJayVBMvIiK%^kYEE_3rs zNPO@g?r9-)46CEW2B~2;syd zy<|vH6x_YL$}>-YlE)u^oNL#wBYcmmH*YZ&gkU{%nv!SESFbP`ODAS&)*IYgy9Mox zlgC!bGM_A$)Tf(7bw!XB_(tQ!5uMJEVcueDvdUz$LcZMwVbJ*yTev7I-}8wpA)Vd< z{r-Sq*kjw0xc5b!pObX z{pi!-HeeYH2CgWLB08Nm#u%E-X35*L4>Y}ppZ{p__E+^|OeZNnr!Fj+&iEJq;$QOO zi!bqOzxJyHL2!7@5GZBP+U4^ZV@fWYxv%&W;=!LCjbt!P+*#!Zz>q>f5c=e~fh@&Z z#qL3ePFFH9zsS%2(%<5*f9V(a-gjT;cYfJQ!OT>XEE{m+#&zDg{3aL9on&dI&fobvf1OTikKPk=L_wP$ z`TTjBQ;Y1b?=eyFY47YX**r$4)#2R5IxoNUGRID=&>8fZYs{d0k8ZESZ~gXf@*n?4 z{|R0Yvbw%Wtybe;f0x_0?xH^R>CZgx##aKZi*W&UbgU$gIKUqL`MyN)QT#(*l*Ds0 zJ|u-Wd`(l1oQ!k^py-ThsF1$)lLRd;g5Cl%} zAeAeK<;b?fII{>Lj!yea&YlHp%2*70#FI;av2Hg12#=Vt!cj`{EG_5uK7p_Bq(CZX zErFHPYY}NOfWqKK0U`|W;}|0Z&;G!31g6P9|DXP6mRA-Ds&%HOmI$L7TRZO)RC`Dp zVS%_>A&5eZEofA#1isH?bAm5?;fJ_!<2rx-;w$VG8eMOYDMcy;-+BF2a+|ZUwT6r& zKmF4`#=_zpmB@{y!)V02@|+=`C-Ho*j3jYmUtxSz zFLUqSJ)U~%Nqi+Sc}^e&yjFMpO$BN|6k6l(G0&wtFcj zp87P~8yj>EcFB_tr_Ve=wLZc2UYlyQLT}K)BAA+-q}%P`2La2+mbi8IE}rsPKDLZV z6#wqazr#$k!m!_FX1dPt6O&YH8X*$u@dSgyu)Vdz#@0U9Z@0Ph)CvnzJ}RgY2u&1+ z3=+dDuU+O-pE%7#qf&CF>SRTRDI_|#HwYge*^8?ae&dl^QQ5bP-VF6(!^=gIBeg1R2`_5$^JNE>aE?(f7 zXP%}xQKQjpB81|VmtWy`{-6Js*8V=f_N%{&5Q58>-{Q`#8)SLTty}MN^ZI+-zI%&X z*Ke@Dw?nJdrrT+g4u^CO+E`;;o~?vxwSuQ4bCV64jV6WBXybgClHQPZ$8jU$S_7oV zmG|D~=YR2+`0CgHoP+j0pZ$SnIdNhcY=NenvPhRWVNAJ)Nc#}L^9T*mLqER#?eB2s z&K=%)=N;oWf$A1hS} z7=&nJX>~jF1_Mrsd;H|eWv$eI!#GcB=S9uEi7>P%@+wO4c52z zId}d^{>{Jp3M(ti{O!N}x0s!tX6<&ui(h+@-}%b#@yuhF`1fD__f)+G6cU@m*48%j z^K~W~b$Tt!Cq8+a_Wls1s`#N|b!(f2#d%iOR>`uQh51FExNwP;6DN54?YFpj>n>+b zoMh$rGDa7OfAC-Y*Vf%k50hr#d-to)#@SXb+%b*XL-ddm5z zjA2=aR(R6Knu5Uh(AFtSu$qOsPtDUzc!tSIm(OOUBJks}0x=2%l}bPyh2tX6SjS%) zb#_);f}n!$Tn$JEJq~&;4tjkG;knlgmZ;R~R3|6Fi+J;$EBwKq{yA^H`8{-+5r-AD z(gae2u_zgX^~iMrp1|{bie$iW*yHr+(|qo8pQD!~c!6cIX_%aoRBAa<%@S8EL1atR z&Drzo0!$$=TB5Zg9s2YS;O=UVYwv9H#>>}8lOaJ=;n`1pn$LXZGt4b6&~9~D+gN93 zcZWu;&UCX$vpGQ&$K!kTpo_7)#-{9*-Rcm=qIEG=6^`sDKsb(>c99lZln9&^E=(F( zn%N)5mFUs$g+v_jn;CJ?^q6=m$DS~wf@>tL9u5!+_1);d{${WnY5h@C)H7D>a zG$-qXl?W|DYN3a~(oG8Xh9D}FXcgi4Az>h}QlLB!PfCo1EX`3t$Y79=Wd*+PfpENu z!hp^*gejPxpK^tq5>(=l7hn7~-~P_G>9h|xxpLg8kdYXZqY$Kp!61pFfY9cgKC#Tj z6AMUdP+^Sl0%Va=q$z*?#@oDmkm2VL_=dTeX}qYy*~gz`@4%rRNKIZC0^dg<38ILV z=;$QSsrAN@R07!XCFi^C~Zx9)vx=~3i_J)^bk zo+XP^E~9j0vmylJoKcI!_tcR+!Hx2awq$wE zF*P;Kn6q;s<8`**4C1x+4vj?Jm2~JDUURF>u40#qD%DL^Hj+lH7L(L zUqUIYVZ260NO!k4V4~S%dU~4geeW$!ojO6IQN#BX`8eygoVi(X1mQ8IF(%)4O!nZ9 z_uqe?zx#Lp9^G!|0~LVI-sDsK(|`I;n3$OOzW?q)Kdk$Gs18wWUHX1`-UtcezUZUx zZ;tw**wh36-$zHiZr{F*)|xYC&OC%j9rD+BsK^`x35*KeBc%NC$FU#qIgi}O@`Xld zohi>2VVxni)`mg9kJj2{UXC2;OJRpCy(l$6h6_I#+dVD5NCKb(u>j#|Dw3YT9*G*|_ME z9z<4=Q)XsqjS;S!#zH|k4%F5K8T$s1q`8Y=aa`b>L`%H1OQ!oo@gkWv2&vJ9O9GcT z6<>E~XqOvFp&jZ|WI0kw0_B{9b$Yi<5Vz!6!B7i^1gLrwKdQ0YTp{fZc=JzQ;-&Ar zz~+tX1j^&spgKF-F*JvP?X*xTJ>a;nMP%q&3|Is{1PV@{Z~B>^3Wp4ifP z{V1YlMg>+$G9L%g7FpRT7x+PVsLrv6u}PFlK(B<3NBN5``_UrGBzL#znZu5ZQK4ZD zyQ1=%cru_c#dyB2Jb&yfq6>}mLu{ciMpG$sx|EbGFU(`IoE{Qv#_6X{vo{>_8~^H$ z2o+pBcY9U^pNQYvbakF)=|=XtFe8*z2=0UFF>3MX=``zVG=I+WEQV zI;U2vare$`-oJ5`$>uamD=Y3h8+%CLFU16+ME9^sOZx^c-Lc7!F&W)?wx_JX@ za-zUT=z`GqP*PweWTq4iSc?$?=?CP6CQB0z4%(RI8S=cK5=JPY(1qJtt8q-z#%$lY z%ha(2l&?qzeR}-?d7ir%EMuHP4vF#Fd846$_r4QD!X?{mgacA@8YG({ZaYeST$@d7%X{`xJ|pvi_?WEkzY|t z+Z`%uv6zzgDuhJ&uCTC1mup*7Xr~6Y*2UErt+C~(_3r9jE?#<)H{X1dgLa!v+aXR< z)03P&d6L0kNTX4w-98{04w;&oB+oLEWKbqR2*M~F7Yx2~95d&krxDg2OrGZvhC#W< zl*tpul4Uu5pj?4pj_X})i)+fJrlz@Z<0iFQl_-i_2y$7FFuoomTjs+tm;F$pw|J=d zIdkR=zx~_4#qa*^@A3NUud}taMZ0}~@B7Tn&2!5!W~j0~ecxUjO1K<7S+v-}4ae2@Nd^5n@!e)glq)zPu&=);K7`&cnHb3Ekh+xtGj z2a)g@6)A2oTYmzi241zI3&5N0pnEWYQ@JMu(+e=7bqz2!jBOCesC4J05@%3L!kVv1o%Ah7600IEu;g z1d%7yLrrKB0wq{%HbEG&G{yG>ULZ?(xZA{vvdfp&jTSs>F{OWuP!d0CFf;+ZLUOND zkg1r7g(W=S=kDqnZ@utM)>hZ(_V@7on8`+yFbwcLLFD=Pag}$kU8P!YV01yMH9^Ut z(pf=aAP7A=?G``y+;jZoPyIC4%f$CA%kh=tQz_3cljijpCO$ep7l`pg;i7X~&D76N(1TT2KhVgUE= z%@{scDCIdGmoCcu%8?PCDUD|h(h71Yq35IKs3TKaP2>k<*I>t?)^1!`{$4;Bgd_tO zACqPoaivPD-KX8{@~Mk+eEO4@n47M!wYG||0bWp{kOjtMNb3;<4#v*293^ClD(La% z;SLT(7)D5?xOeX!2M29Voji@_yYzLT#Npvrn)HqioIKBwLNOQ&u+}=eh0=dc2*ujk zD$~btAs%L0lg%r z-On*9B%L`;MOXr*F-UAsWw$$|+v&10KgXNbzso_Y@kGFUrOHfm5taf$|y?&dS`9*?oibPBN*yZjSV+efD9qyjuu``cx`|e$`Jfr6IxqA66 z=9+b~EJgVdnKpE~0|v=}FsNd*EjdgXNs=%(JB38ksMqnl0dcLt_D-McH&%J>GZ$&? zZBwt*sZ;}eE9e)Rq)?dLAhqL~s`?yZa~WIo$g{i*b;dP5h;bB*@+3uJIkvFCmABrY z$U02cs-;NAqcAB-N`zO6P>jZu)B>p_CNB+ztw9RMZBfcYdOk&=@q|0MwAP1X{j#%n z_BhT+a=*2Y6p~MT{NsG>>wm`5(h@V%(`1RueBEyyaQd+`blYtPgT6xzN1@FnDxi&V zo{8leRKhiQTDzo-qusqVt|_-dU`hneWkD*GP9@SdDz4jvi_wuF}|0d2IYC#{PmIjOf@8L8ZPQm+5}q*CF>vmLsskB4C{1S~ghs z6WTv4hUfbgSl>@!C?4@0#b2mUypOvzMh|&_D`v-y#CU8foWkCe=HeV3Q+`1GCmxaM zIJ#b=8l))l(#$(23PZpl^jJuXmE{@oGi1Ftc<>(&$1V_aNp6899 zze4Nrgl|+dk113Z)IWayzkS{q%f{+D>uc*InOop-wL%yNSX?$;t_@Z=hhyLO34EVC zFA=UXPRANUru8AQ4@FTpIL;Vnu9pr6cv7MBoOC#3x>?7Xg0kwDsOk) z+WUL#?QYZQwCVTzB*QLgk&@&oNv25w{FvMi8Cb=7OY`Qn+uXj_CRY)ie$MN!zs^_x zTY1=%9R`AdDPJASfYj!5~cuyoi;hW!Bd25=1eLY7JAya>NzqebpayIDg?1 z7C3(5B*}2-r24fQ^?D72EU%42@QiU6JPbmNao5lDOIY3W2}9>|C50e28f`Sb=d&=s z$j6_$#HEXusMTr=1_R!`{5@WIud4@Wsz zjtHrpW7b$eG3r1|uAdMfz55oaErYJ5QqCsizJ1{cBwKd2zUR9QKJbX*kcq|wUf@%$ z)o4yku(7er3opIOpgZ8wQ%_<hslxHQ9WyWJutqZa|A4}?$6ev$QG{QKfpR0}Y1* z27?}1p_!eX=j8DdynFQ;NuD8uB+GLu%3z9u%nDK~K?YQ!8i6k%9iltC6jG6g6Qh7C zKL7wA07*naRHRnm=Y8n4*gxp9pGg**RhAczbN0ju7AL2Of{-App!1A+HK0%Icb)$v$uyupzUa^Y9zt7}E zjrKvOj6SiXc}^JD$qGwhveGH~Fzk4QjMQsRY1`SqN75fq6o%d17M<>Z?jSGK%7$T* zl9#CpSZ5M!ji%S>kPP}%qJUh(K|7`2>$9{p$3(40z1AS`1A-tTh$5mWBKAY7Q9uv{ zSQU^I5cpo%#HjL_4aT`|LI}LbFYRVxI-M57ej87@48g)^6cTA&^7532*2Q=vW6H6282SW3P$CJ5iwE;P za$}jDo#WQ6+f*tQrl+R}0=EX?P;hm0bad1_^k|te*7A%STzg+(@FXNSkjkD8qCPr$VCrH=gVoFQM%O{r6j)ZyCQvD5ZZArFAt`zjPB_XZn{#= z5AKko=B7n%9e_%6og z`B;@UdT5kV_<`>-4GTNA9yo*&1-`Ec0?!2~YfZCJW!Uete(N?xmJun-pg&+R7`TEe z%~2{K2qVI{iu5NiqDqnn>~szHb~A3=ZSlsnEna;4Hme5%_7ll=H{rW)UE%UuZ?dzw zfigK!;GqKdU4(=@%Sh9q>xhGhiAIy8+a<15nVX+y_3kZ-!gY{S%^CLgc8TMd(`QbX z&bJKa7X~2$i8ju# zRF|pU%gakV^UTxCEgmC`Dy*&DIU!?%z!4E&y7A&D&q+z)ioYX;nmp1i$&wh)I5b>R=%QRC%5msC_j0qn{(}rrL!ph1D zySsa=Z>)1{VS#F;%AnuJ8fVj?lyL6afGG-hO|3JX^vc548iVh7WLe^hE@K!D2aY_d zHHFdC>vi%hVRm7OV<*n=-FM!@Aix^Zv_ScRllto%WemnTw5n2{pb;sGbcjw9Je)gp znrVdRb9`lm$uL3+pM$*?%9^qW^k{cl>}}m+cYlvtw{FpDAK-g|%bb*wG|LgvBS{MK z!q6XPWQ8Wl3X-%SGltG!NKL`XrFp_AAPORk24izakS;7*XGmPodmKBWYY^4Qv?0qf zs@0l<{L5#_^PHF5y;Du$xlkno8_rwMOTK+c<^e1{$jgl_XFOSV=VGX2f|igi(m^d1zzk_Il)5Miho_ zFGXUt!6XU!a6qUOz6uCcmn+;m67P~t;99vpsVQ#KOn|*5aItvR6v|9UR zKyf(EXFU|9l->GxJYq@n)HPd5m5q@sRpwq9tLD6stTAY9@I#*{ik)d?r%Q8k8j!56 zuXAj1k%>nAJ{uQPq6_2ZO)y5X9||l!V#)BZ`c8ZZ67~Ix5&NM<(1S`v$1yV{vM3*l zv3V5F>CuJFqyGD7;m?1W;_;)Y5f6*s`&Ex}j3$m6&hZ%V!EYlz;9He{D956KqxaU1 zr$Rvxl$4S~&qrgNsqaW&P~`b|tT$@*M^va0pfExfMd?R5I%8CM9yw!hG4`uG>XMH= zy!YzHjhoMZ{p(-jt?$0U`r2KVmY2A6@dBUxyg=784jRsy2V1>eD z&coX&;S9q;f>Mr1X-kLl(a$3lRi5RJG={+Qon=6g6-WU}d31+Ex=BtUJPHCbqv$1? z&Al#nH+FdYy$vqkSmny~HSXTq=ic6c-A+zB7Z?$0Vd4=0|?h=I&2i*gvX6LBZYSgPPF?^yi z!O4@S*za};{19sm?Ou05dx?deSf^_YXj=__v3%sy25poI#vDG+^IXAa%Xz#K1SN4;DMg-Ut^hZNUcU!OjvZSfuEbow zehncci^q=9@AZkIh&;=vR%+u!d&k3bWK1EU&_(I7HXM^PM;o%Id@7X+6BA7Y?mjf8 z=9ySL&fL-|R`1+pZ)XRKq$qNR{SG=!2t3ElDYQm-0bUR=RS9W$1x4DUJ;>PZx#x9x zZi-4M8i{~MvA|DPpv$Q~54UsZ*PrfvC+CBDjH~I^M09~Yx+%FYKA7dfanrgM`kdU&# zk|QM@E+D$Keuu%JM-W8#QB0Z}gmkazv>L)$hm_(1XZ-7mAs3k{5XvJ-HCDsKM2x4L zbGeX~zz-2hIR8E0C-5bH;G=>Haixw7BC@Goj>&cUE9Zj+-hGGS7FDh9u26I5=Q`f1h^yfIQ6{3N$m# z!rUA)Gt&f~Pp{i03<9T0t~hny&h|FeLcLyRVzS9_IAnEo)uHWWQzVWc*drHkJ<7-{ z_lhtGO4YF=Z;u)u-*+P$V=Q495=Jq87&08DY;J9FZ+i!e;KZpj+`M^{O0~-D%#1^? zba5!WDho}o6xv9s#w2CL!})=Shw+0&*uw?PhoeH`Ly8jdkfp*{QVwGjZnO`6cyag9 z)qx*z3xpQa9l8n1|R@vI!WiS{zS#TVqgk+dx zfFO)wdcEHGOO9+CMk+8Ngo}I;kR~qEu6592cW;+UrH1E+7#T6N9=#0i?RB}mzQgL) z0qgr6b~*`tQ_#&bQYDVHu?BCgfxbh-p#NLFYD zy##HolbR~WPxGY03tYFUJl{PWCErF$R|$K{nes)kvkb7K&eZdWBS)Sc4ikEVJ_5n45}@WlBGEH59YHyChlYnyJbhp~<;7lz^3 zZC;kV3a4U{_gQmDrSMAPx*WTd+p*eX#FZL7-110F53b*JM8(o{a5O)6{4MU|TG^3* z&)6mCP))-4Gl?U-%ER&FxXTp|{d2xs3V|FiDCH?S2YbBz_M1pg5JeUIu)#OJ@g1Ii z<{4BV>2|sf+ATRWMd4n{E6sKDqF{4#3(pU!)#?-_`Wb{F!;-&JuRC9%+qZ7Bzqik+ zQ>PgY2k!bPk9Ma6)}fV@hQ8K1gmEMw2*ar4zZs$^9v6^#`ERn!H67Nkv2bjK$Im@Z zIv8MjdsrbU@aScRR<}xO{0s#aoys$R7mRbJC$;6;YOg+b9=?9*(S5IlUU*7 zDTPvk;V|Q^o2$&v&SFX<$-oPo1GE&RMS-ybFN|F1eEAG%3t1JYEJ<|#AN4Fcb%5?9gI#d1mIHe+Gk zm`;_7BWoqinMIN`Aqpd^)e1toA~DOHZ{2V>#8=L|bl7dt-rb>AjVOv7DV>j+#Es;o zPy}8;7(~QTg*c3zA*_%L215=y9rpM4+1uZz+wC$OB*dZ5)YK%iGqcRi%@D^S!rGFj zlDhErT9qV894|}Cl9J$&rYXC-JG_7625FMAFh7s-6rSfekwr1`K{VwdN-5NlC7SF; zc-C6{@_@@S$I0?NpCAlqG^Z%EVRLJndz)KmV_DzW!dSsGpZpYAnz6RF#?rAx8jU)P z^7O1dG`r3V3SBT93>gjvT)eEsXZ`No^*Jm<-a7x?_=ewfLr zDRfb=v9`{^-aek^QLR?+gOFs9QmsX9@fDQHDdW0OA;`0WBF_oJfXor_P+j^L={50k6ODUEX-}dmt=l&z)m= zWre`=+1cK4n{hI9h+Q0&@9hr3qplRrx}u8`!KZg1#Rd|`y|`xH7S84jpa>vX$a`n?h*Q9gN5l%)3%UF2kWMifRI zKXH=P)pf33d6$)y6)M#jV=VnLU(=~GDPe3oMzr!gr+v^yYfY_MbI*{q_?}M~MHB^0 z%`f9e4UF~Zcl$Ia>def~@zN`=BEy(!y-vMWBMy9Yo?&$1xCdB_kYvW-Sxci5P>TXo zSi!G0@P#I|F6P7&F7zs@)j8PT|z`hQow73h_OOSB^rIbQFM* zCyud((DUdHQl40x;^c{AXe}{9A}PQszW&BLB{>>=CGfqFFbJsDDzrOYw1%kW^2tWw z!LG3j3H*RGO9`ds-o^&XQw#=u;@FL!(sbaS-x3j3N;pNdF-$aScmkr(#~4t7qSNV+ zCJCobEhBL`twH2t4bD0N;p|7syo#c5V?^y$iNb&&aK%Np+oOHZqTA_|%4W-ctA)^( zZo9+A+IoqI`bg!2QfTWeP;z4!rWps_9y|LjHn(?KTfI%M-@{l-wOV0nYKpnpIp*i* znVp+uqFyHm0(6m&iQeP!bE)hb86%H9)(SmVXjS5fh51D$n@#dOEAO2X!zksUJUJeX zcqLNcvJV+|@Sg9LQwoSHG0mxI4AALzxq9se8yg!mnoU+$*EoOS5*IIAq|@y(H#g7i zTeqpzD$LAImyM=zsJ5rjMw4ZkYY0n9Ll^|55>M0Z_1x$56sq*^gAu2uBu5L5ARgi) zj0MGq5aAz9A|LIer9@}EzDE`}j~-ioFme7-DAxUj)?cPL`m5k&9U_QF^K=i3t3&nq zq0H+LO*}%G|LEo51L)+TMZ#f|Ke7VyN{UXQ^|+v-Yye05>sSqVWC}7WltwlYp6@%$ zk@Ea8#^C$GcvR`EBm6PVL_PJfk3TOEOifL5{)zK^>a#z<$&)KgOw=g~&CQ!PdF{1V zc=grScgkX3#UJ?`&B+GqYir!w+@=y&n3|r#8pl=eOK%io?0A8@Tq<3< zJ-fx34u@=RZPD6o(`t2SwcE7Y9a^me`u)DkEh?PaCmjsH_%1kC1^B`z^g^U{rlo0C zjwKN&-!CI%v~x07J_d&riP9L(^Mew))nz9ekYp)oo)L!;##;9Fb{*MuWr;8S!k5UB zl&!5ze(vY~1BQcyt(_fel{&lo`{l?@j!|J*R?kY7@S~FB2W>Qmm*oaX37JaeyYH-> zL>yyD^V09jNU}U7@FT*~%{d(=WLe^jGAE}v_ry7tjvb>>uhVU}c>Arlx%~EJvLs`6 zc9y4~`8d`ZTCEnnZjZdkv6SQAI4YG8c3ix;vPq0PNh!-tb+iCVUdrgQb%Q%M*Qic3nVeo=acKo1Jhu0C>GcOhVML==W0(wCSXd;kR49rZKk)FBa^7Or zmPn+eFz%k6K68d%zt80>Z_%8Zq+Y91uT`A_QBsaC^RY=^5V{<>WH@xbTv6T$sFg?Zv?h2AiulVn)yuCFiM zfn$UujH^U}f?*pgJ*4s-$=^CZ86!OQ_V>7YcWi5pGg zFm%Ycb=*2nderJQx}7eIiwnGe>kdi3k6&6us1bx;#&mg}B#L4uPSH7Yb2Io#633x? z&V|bztyf|e7iYm}gcW#!LgETYW5F6nz{-mhqYI?9>~uAaM%|IdH#gbe+hfq{lV%BN zI&><}dWET}1}n>p96x@HGmo9-*oouRCmYO8Hkg~6=GfvK%g5%KpPON7vO%L(rO~Ky z`s4`~7Z#|*AsB); zsMqUM;+WxZNRb!iefAs=uH@plrq(`U)K~tQa4O*sCYyh>!o)sMWQ}-ZO3M2i*ar%z z`=fjwU1)tjv(dkrV(Twgq>X4MN0Fp4>0KNd_KBlnfTQQb_x}|35rx>Jqk_Z{1oQBI z1VK1~#*9nw;js#fx0LaGqm}$EZ}Q3umaC_3c(T<4g%jP7GUh~J&qGn9=X=z&d?eGLOCUu7b1it2qKC?ljo%op>TdY zX_nIO_ecjrCh85&o;kzW#~){YZk}wIa`oM-y!gV4wA%Z`m6*rQoMCcuihjS({(g%j zair=>rCKTpf z9w?`NK}cAsDlhfTcq?!(=zHw zo3hDrFnwYQe)Q=J__9DqM`DMJPN&bC@82T|W1@=h)Opfz5Ys%NHZj5eeh1-))a!M0 zUX&ayi8YpqMuR-huvQWJ0UK-UaXVyxxl$>YR9K&=up z*_~7NA*`&L(Nqg%a{kvr z)I34ls8g-POifO*Fh5JJR>A6mI0~4ZnIY5V-cX`W2uYS@WTm~0P@?qsaa4&Qa7s?4 zB-$84-*@uzk}BZTh7!MY+}73!Bm7b=?@Z?mrvmNAzNG9 z_<_%3XV3DruYHYEr%o|9H^=L*zs6&y&v5JJO)8Zt)k=kWy;@qMxP3BBGYpoE^>yxT zY?OS&33Ohty}iTs_BPT}%uG+?DMgxPB*O#?<9*yA^zuWvRN_8#L|`1i%0N#gRXM5G@lQ7~K3I+IGLnQ2eC|#qVz!As*?Jf7FubVYT1`-|vB6 z-aj@Jk7R{?IBUy3glZjq&(WIjJcZ}`80*9q;)oyPh_f|1^C+KrAsyXiv{t+kl3p6z zj?gnXN^da+^@Bh1h38FaZ@K&vs}^0$^1f@=zuNJi>F#Bhy=beeRV7OnX-S+35&$t|CXn+O zbHtr*FV4A3KCrPSK&>DJ{VF-ci`w}3b8%$14;(0!nZBZ)u7=}T&A0mW_Yde^hMHGcZVZ`qC z4wLmsUVHufgkj9lQHxfm&-T_XtyYV2wIWk7!oV;MY+J@ZbCJn7vL+uzmSth+28K#y z8btsAgFFM>&`u>Sihqx$2;h0D46lk3u8i0@GHM#c5yK!L3PU`{<;>C|mo6B46CQp307HihXU;G)Jx!9P93CCfZZ%1>1jmuXq9N27Qs`)gNlvOfHB#Bq zly@v2d9ZRU1*(;CS-F1DH4y4+*NPi+6lXm-rOM=`rsU%yy^$-UtT~GN7RLP1AeNt) z@Y6I?EUZAToRGgRNm6WKk|iM;37L?}oe(BrCfM5B=SSba$}8V}30<>!@4ZiX|AWsk z-5O^vUZp;JhW^nGS)7u_A(pA5=@5sJ^37NZEGiY8I8M-YjUXBz$e5g&j zg{3o;s}l@@K<@uZ$#Y42kK+_wm*wS!pJ^C`JbAJ~b;4(Rd!KTt&P1cZFn`!eW?)1g42**gSgwpQ+R;HlA$pZ>~>q{>oK?B*!o%Z%oexongX9pFhF%EF8FnyhbZ;MxYa zZ(QVD!=t%&hyKxxGUer5L(Nz0u(E$|WXh6*e~3+1uM^Zf=%q*RJu4U;I}tUAoMfrE|nd z%I@wSlanGc}eJ;{x`>b-6f1`xt^9C*PM-Pb4 zDttwao5UoUrm60gVlWnCZ82&^Ua%{LCQ&r$i63zs#Ga<7F&z4LzWeH1aTJp&zmz64 zDWPeCpf}*)Kzeq3&t+z2mX~hc;`;S#%+1Z9X$E_HyZrXIzvI`x{zvY7^bymuGu*oM zA}`*2fjmulvcAT~(+RNEwEX?aATDvP@h@bs3r_jwK%;wk3akmgRJNJ^6r9LyFL0vR0$t z@3XVJg=v_4@70%i^_ADSbajP6e@Lsxbi2~Wc`G{x#L zNYcWSGFEat#!b;o`FnMtjaF|XKyH3gOH}N;^Gt$PQ^gl?6yIhTMs!;pI-M?Qo-)~( zWO;d+Gw04RGdn}O+vdX$-sj%Edn9?z?Cd;e&z>dEQwIG3`v?2+;~i16r*nuIHg^29;`+EKhK~DyChATw`^8 zl}8Wm({6Y1y^=Ia>pG@kVA)dna$QIH-x7Mw1Fl}aOdRwvbb)SI^algdIL0(JJlDez z8trDAN@JQzqd~pok@VU;e(+G%t*C2F)8t96Sa&CBA`Sn#j)qLl8J118QlWKtK*_QA zpMLTN!zjTkm89P+NpMYzW~ax!jeRs7N}f&Gchr4j5=GL)3=j)@)L$mCFtU_9i(_i# z5{_3!%VKt(JfT)96Acp#%|u7T)N~q+DrMKDRQ6eax{VNm*{K@gFd!wTJMv`Yl;Sk4hCG1jU2*aS)k9oRtz|)N_Zr`}d3m4|u{rn^DeDGU>RHJ_G zDp#+ru&{KN`s6gWSHiR%sjh1pmhIrUzFJ{hAT<0^38CpYu8ZfFs83EYH9bqURwv8h z>BdtuomEtneHX>Wpd1ka>6GqH3F!uD2I(HUJ4Cv>VaTCFxSM}%g3or-i7xL*DJV3UQm1*y7SB?S2MAC4-TG6eSk^u# zUMgu@U~tT}t1?X!r&-|2)-h41P0_Dj>|LV%IXY@+z6=R2G6VH*P4KUqTUdlrqU!?o ziM_oHn#Tp-sal+IrJy`^QUukZQcp)EQ}baZr)ZPn+8*TXq{&qZs{iWam*#RCqt%r=ou>7a?-r zD_`mc&E#m?s=U=P169`*RBt4|H{QJ$#OQLw2jLk-yIi}lFQ%94ZQ3%qciU%u&LNiQ zvCED0au7Ye=~22bc(IZ0yVso$xDudrp_b$O7CFzUdq zC9DKeO64GZ9jVx_A{L0F^~a=6LqGy#I3pku!TKe5JO;TwnCwYFr&#LgCrZz*Q(p=1Vq?M(^n%|@ zY-vbJH%JZ^^(ZQnR^=RhMUGe?^fkdGuGG7{pKbj`!tla{Gk>zYr+XZCv2n08Rki9#aF6;d5xb z80MUlWd>@$afI2IA}!g#X^MAq%KaOm+5F$_q8&%qX>s8_Up_`R+z5__Xv6!(JIwZS!``462Nna&4dP`Wy- zw?8~S!)^|stNu!p!?3uY524hEDj`*R*-lk^p;F2!BI_L%M>(Y~ya>~0n>ln_Ui&r7 z$3r*f0z313x?;cH$X)+;{YzFVZ1nqL>NV)%d73q-JOz82UiED@=P4~OsmgJad$tFd(m2b|&!bAfa6QqJAs zl!`dpktyH%7-BTzcjY(XJTM6oTF!RMgdGg$tRyoNtIaQIb-`IB+$rXQOg={9lB11Oh4Jx8 zPT+_sB)W5U=LsCqqv7zSxPAmZiC4GS=4~;gWcG)Pmr}A@T@?B7Yi7l-TV-+{81i<`I|pr9&+?dS8tQ5)=rl3Y;qw1-BJgFYZprw zk2!n3e@ff_^Y<^#OG%q14AND)sKP{kThh-ySwBTCaq#KAEuF`7+R z&NpcObBx!-ULSe(v>lvw-3R<4w2O^YJD*H(xO52PYMGKA`L5AVV0nWSd11mgcOnlR zpis>*S$DzHU0&rv8B9tDA76&NP#{xnb>X8zvVZ%n?fTQqmn=5CGueD1rZo1Kf<$RP zkuoMyH6cU*y-Pn<9A_H z%*;B#t$eXeb#ecyy{fvpuBAmbsW?TL!UO{9LxH4lbE+k~Q!z{7WgC>bu^VE38~-pCph17GgxH!r^?p3H3LIU( zj}Pqco5lI^5uhx1#s@#Va9i8DZXGq|_>@7{?y%X$)_ka-lKO0lJ=RYwi;hUJ*`gw& zOVcYx?MwS9C*y%Xl?Fk+sIqitbngIRkYbZsQlwf(n`Q(^BWqnlnkJp@Kr{Dq!I~}g zDd}omnRZ2vh;LYEsB|I2)~~L+`^UAbJr7?^TV5c~!gz;szIb>AY3ayF^TS?E6I^K^ zu@sMvlQlQ+HdRceS`aeX$6I&oWmj!9s1CVbV6*FL~ar;y3=w| zO>wZe7B{!mM1T4Y=#G!SfU!B*Tz+h2g}B%FYt3s?#odMKFI(U!3zCeIn4Oj8iNR<% z3$*J~2Lc#31F|ogN?D+SQ)6RVJc!NKwNw^bn8IxhVA`B?CLQ(n`M2Q;4A-2zgTQ6t z&ezxaL#cK(x8OWsd0w+UZipmmKT6iLb;~L`7YI>+AmjSj^QmL?H^I|!poxidB9Ag2 zOkZTHK}o%_!F)hCI%M+u`tJSghqRL4dicW=bilnt(^gAcEKtdDy40|~4ir1_KzG-1 zXMXb4GUxM!-Lhk;=$(M_R`6&7sD%Umo%hY^^k|4oj;&jktbUf`zm*HP3-%01Dovrt z2NjHmGl1c_Q0!QtemXre_cirh`51KpL|~o~^&zNJ4Af@H&l>H-(18W#&09NqgSuu$ zXJetzJvbnhxe^i%fd|JzcL_^|)_mg`Q-#y;OCp2OClbOFKj>0*u*W7Qy_0^|48aMj z)1S%4S4&8+C4wHGS-U4M8g|E$d)%CNwUqM`>-*lg|Eri?x5iXApr0Be@zM^BZkl96x+1Q`R>0)(`B$ncUi1U|91?*e{TOEU8Q6?XW ziz`NbhpEuX_*mL=dV96b6p&ED$Z#>p;of)x;X#Afz4Hz`R;2bMm40dRV`hdbn8Db` zGK|2mb=0%x&VbEGua}3_!^7j|;dp)fsAc{4WUi_+oQ;{_KS@p}BR6N$`Y8eJExFT@ zFtBgfY!fHP5<{8MSXRHwC?AxR{MH%EXpbx>q2_L^!C{pS;!>C&JFIC=gwD=N#U45N9Xp@>2o8_G+3Syocua&n>xM}ZTc z@DAyK!%R$xn?rb3;_kC#PAr1VGAj!1q+CcJbYc5SikTyWk@dmEd{G?+F|_Y^FYeq8 zG-kF)UsVoqC^g|@TOXWpxs7aQ7!xK)bEcVS!?QdV;a<>Xz>|F0>dz7eh%K)M@mX% zn$Mi@*_rRSFJBb$h-;M77wogJvemn6V?K!I$;MNt*Q#y^kc$(>Wc`Y^rBvpwfZ^C4 zk`%X1!r(gBVvko($uy(?gdNX`Z1sZXr>EV_8VywtAyWMN%`2Mz6vG^XORIzKBA1t* zfZIx6*caLnzj#6#d{Zt|Z5RRuX!ZEH4kViO>;(1S7df^i>6J}SS^B@*DFqFrZGF?n z9q7Umd;Fznek92hE6=h@i$x9EM!2%%QjT6ftZDhJ4+5~-ZR~{YX$l#0!4yRcQ|0Ge z?ivNsBQtL_u$ly4%8#d}!lg{)1paW&XHe1?Xr7EnMU`VHP5k%{`YYYtR0 z?)t;M^-4+l#LdxmHRn^7Tus@#dkPj~XLfF4rDLDIe4hoaQASbk55IJ4s+mgoI@h+$ z$btZL_4=;5USQMRcl&sLr@o(vSr%lnXOkN*bPbVGi>Qt{kl?p&=@2$FK>oWXTs@IP z#H1VgMC#dpix+#$0KTHkwVe{Bh@xdX_41T=65o3Qe}ryZkjgSC=BfY-k0c*dlvb;J z09btvVo{lynT7qXv8P%f(z-ZMWGV0`+Pk?%` zde!r&+oZyWFAX3NulY;yA15cA;S+T2Cg0sISy~*{Hy4gp$Jr+lB$wW;w-GIY4_L?D zPi*179pjUer@<8W7`0uO#L%Pq$8!wZcx}O->-}d2B?q`(HHvgDC8Z@9nYk#+pB;Iu z=MFW;EXGnXQ|ryA2-{U)z^g^$zvMwmM^}*p*4>(gMeDb_WfC}cjZ}YW@w(Yg_ugM% z)n+p)m?=XXoiDBY+HCOk^zLk*%oj_TvxXPUA58CGmMP7Rz5;06CLah7{Yqp91a6*fFH$%-1ko*C#cbIxB^Gwbd~_4n@acq;UFdo}r7WM`6Hro=WZ&7rvk*3osXNp7G!KQYh0^KUY zZTPD9U{erV1|1PNOX00znq23w(}+}8oc))*EX`SuD^CGoVH!+HEqT1nc0O-@@oQzSu>%JAY<2r%O#)BS%19{aPv3)IV*sy+$d+5r{Bi#(Ta<6FP`Wg2xoVD;P` zb!b-dj&PHH!Kd1V@Kr1`&Zc{_ZU0r`j|l#{)B6s{b%vOHN*e-}hK8Pwskw1brseFu z2cQo6#xDwz4jE^$DDe|wZEw)0Oev=Q$a`kWOC?tn!JxoF7S0njjid^E>_2vGY!LT% z$uhx~gmUa%VW3K~AdGwFx6i806l#JkZ1yBliK%VPo)HO+^12~@cpA*I$nt}PRnt^d zRg^z`rwnG+sZkK}P6O@^a!G_CVKtD9cJn6x%MA<-jbtVMit`l_^+Xer`aXpkM95}5 zkxi;);x18%g<>deB9kH^$jLvCz_EM$++HnppF_LPtvBf3x}qgVEyS;{$-0_5VO$fMYz* z?-V0eCirl%wyviK-rFnMd;}1cz`AA=2*btY=BK=mJj;NRbsl&VBvku->9%0K1kmsm}ws z8Xgc+jEs#MaV-~Im%zv;7Ga%8CVql4C*al~ zq|;*^^>my5boQgj=+y_2iLl!e$%Xyjo7rzXko|b2KcImu_K(>y`GGWYn{Dg61mU92 zW6`CDx9ATV`bD>17J3DK<$28a!MAS%u8UgSBq@kel?zM`hFs|z^%t=rDp6Y(TizKJ%^MGC$=ds{J2qwkAVLzO;k(;D$i0ZSqSp~d56Ie^e-CdXlS00< z7;YIo{b6tOF$<-MyS||}mD%GITEeiB1Qa1i9X z?;CffF5u+yw?U4Qz6zKhjw+E&#T@)v`}cVf$5{MA^rmK;joM2aLSlJRSCgCPj-4l{ zcfNP#5QVIr#;jqUFUo!5c@DL)X;#)@#rHDKKNQ*5(jvk;KjUpp6AYiAk616Aq7c(_ zrn*GBTBYk|WpDEzcTaU32TFuZ#*(meiijw7tQLtSgl_0TlVO6+gvmpkYJ>_5Hc6k^ zQUO>2sju&y+X@-L&?#YTvzE4xtLc?8ivnhhp2xFwjapUQFe;c!Bqd?O(7xQs+z=BJ z=F~H=1WezO14IbA(+7hKy!{5GU=nF1!d|y2mFa0W=ZGoDu%HS|qH>)c;TC~Qd?D0c z^G-IB#k5)Z=7%!3+nXc?32s)qMM-q=TKAFA8^&(WGt`Nq7Yfkfp#?rQ?1Z6r!6u&$ zuQLoY{%y6!Qx4C4!WEP!O)eIMWC8z};7!|kMh1U!sbqP*n#g>ZwEJ6^FV%%;mCKp* zwer*+Pdq~#Ry)&QnJw!I=)u`Ai4reyihp)MZM?}B8{_^>i%2<6X}SKJRm05R9Qo>(kztavz`{?xgO6|R*^W*%@$(gh zA~(xQE_tOYbk@eRHSh@~ovn5{BWJTWSe7tMzGKzI)irt1$oDE0ZzexCsd$Oq$T%X?KhawOY`+vLiLv5lA)z&yv{0cWK{WaD# z-Mts?E|ed@>y~?hq1*3_c=cmR?$2flo}P}MFiy717R9vj@irUz6(bN70d<^-#%pyB zqiQ}}9|r~Cr=$;ej`#mqZwkg9%5A}1_}O%X5{&F67-2s@3RRpplwJJud#j8+`l;^j&<^^8Jz8R`^NT6x zgBTP>8Z_x(AmzFk(76(&8_^TqSd|0VM#O55x^=yw39TrOeg4?Wy28r-58a}>fluw6 zlRDvcC~%!7{pIqqy}%@UCvvV_X(NoPXa$m%P_b21bHFu6lG$tgDN7C)0#mp2@W}G% z3ahc+=>cYhu>NNV4kwYf>+HV;*h~mr+?+yoKc4-39U&Lq0+PY^FHSAp}%e}F8?f6xKqK?J8c>gD4r73Y!jB<>0U7zhrIH?I-X z5lJyhdv^B6h+{Zv5KrWZ;v=^z1*e4Oh^?&bRPll768^*vXV{3flIDK1-ni1#_vW5> zqVtoAH_dlMxdGYw#rfcjEa{w(Yr{wv*awlAekyR5DWki#$3A30j?#$K&D-QgSOZt< z(!-;z)i3B{UclJ+91SFg3q4FXOuQ!Z=eon@GF_a-hA&nLY_3@U8!M}pHZ)Jz5-5qw47Hw3 zWVjalTH*ZBD3x76qK=_=sOP$W{Gy?6F5nOP{rep_ z$?5LWGXrpx&Xk>Pjp8=9w0J*UEY`6^$BW*gPo3$4!M%+v9-f|rAXLEMP=m9Fb=J_3 zBjOr%wmS(BlOr}mIAVYq-_p`jkK87;rJ}5#(KxQHo(xazh9~uae#BmalpYi&$pYWm z+e2UVu;w9dTxA7j97Y|cxkKCm=W#$`K<_m-Q-8Q>83`IPe}Uk^2zzm=2`19yC`V@< z4U&&3AQ@m}kqk-_JaOm5@qaLlTu6L< zEOmUmW))|HITJ=3ymr%|W)fIesh~^p6kfGzz6c4$N7)u7@fBVmG>QicgL;%cYM_z z;qju~l6a*8HIn#n-3KRzyC$s5c(3yfPT)3Tcl2i(0>{4rUKaq1yk;0riC(7+v|=-A zjio5ik`B|U;MnyKOuve=SLE)Y(opIT$fHz{Q-{P&xo$(oxC^c2vbHnAVvn`Hlj?-s zg`i425;a2VV1R&ew?7DoKc6u5wYGWi1icH|s(fHow9 zVJ3Qau8}pY?6=tKYEQbFuYP8I;d8!1rgf&SCf(v9okm)hv-mRE0HW(#Y%KeW;8`qAl^y>34$nRTPfO2?|lbMOyz|C!EZ+~{V+EcoW z(dKp`y<s40VCJzQ^!aUG3?~!BRlv&uTacp4{E1+DdVa@z*pWO3U zw0M<_3uLOdCTfy}6k_xK@VlA*0&g}RR;eYOMkf_nIAr@A?!0X80bcuP>51)vO=H~* zM@I7XeW7CN?E*R!}9f*zc8zpCc9kZi7hzhg$8% zGM6q_$M+xfc|U(&?|of3p~_a(5nNgmH)os(rgq!7pMCxz`rQZW7;3wo5IO2uVd(~8 za$!9yh)y7>3~?Ud7{_k!Kl;M1#z;i^=O_Uq z8upsmuC|)mEqD1waF*toa)NEWLEmRBmHKG6DkIg!5k!o3Wh69wj(+SqZZPyrQ6Npz zUE<;omy(u@p&a#~IGs-S5KG@Q==saO`iORe!ul+gbVfsM8?B;!GiPtw zRC=JznN67z@4JS|2@x2I_qlY&Jg@Q~R6>T?n=RKNJa>((kHZug5rEy>^su|x93 zOV>o2nv=0!mH!fm(_D9hlftYvY1H0}3d-jwc$whEE$KGfYq%&1bcb8$>9)s}!{?yb zLTUEMR`M`fG$jV>c{|-2FYu9L$wHkbww(n__M^L6*0aZ-D z%Lh=*9ie*m_(KoQ)PK&HKAZ{<*Rw)mG#4iU3x?Gs)MWbjq}TU&*2W_v&uFV}@O>f2 z&R(l>lKC}^D-4pjgOTc!nwbgwTz3Jj*viT(eQiy|u5tk0;>gJ_7Jq!V2Kl`R6njn zzERS^x#(=P`Z&xv_RJ250T$iO4H($6E+UD2zhqYh1muwb_liez+#dZB^-9G46^3i> z!{ygC?f4za0awz4Hx#SWjOlRQW|b!WepiDebGeo^3x@zGT0D6&9is+*ZUOe=m_B?Hb%Evth( zz=uJaUwKnkB7F2!w^84f-HI%lZklv8XRPH4HY)JVbNRbz;a;6xzX>aYc*``g{Zeda zlleTs@}g%;qkK+|8@&*{fP_n`sESCMDbot)OGynUP5-A2YvQ$lY-53Z{R*z}SMe0J zr#snVmK>x`-vi}3)_(6yWCIq8=H_NXtni=R{@I@E4`eX)%+~~L-`m;-fxFF-3fek5 zvR!oECRX+>#mU|b^}>R7qy!kt|M$IjjZx2JCU>+;{)vBBFclcJ8Cj5x8fKDMEU3!g zl2Nt@j55@(9E1xj_GY(K+1@5|`7nm(38)lTK$h#t>EQs;#j+t1=@ViyvT(`QUc)WM zkM?6|hb{{{=VKtbGD1MqA!(Or-sb(Hv!Jjr5S07OlzyT}3!WvD8E|GnTnu)oa5UqO z&%f3t@m?7!Dc5jK+Q`P^3CvtPd`nT^ee~6xYiVohla5yKuq)%yeY7&)l=jaYVy|vS zj6bU=P)G3ZCUK#6{vV z+R`~oWSI}B2@9nXhLsDg^Vq@r>RIfDdz;NY3u_#pEa}Foa1KtglpW#6Ca|cA(Gl0K@`uiAUz6_`ly0pxjzNM z;L?e>)2Y-31vc?-5)_I@5H4*)smC)sjTVMf1(pd`j*lV}&XuDpD?)%wu%F4;N14y3 zX5nC|H?R8#s4EV#U&UOcb=Z<%_0#OgOpr@^8X5tgTM<3TrnA8Ud$47hfp1A)z#fH-!joV>zRE5%}Y zQ8cHXB~=b}b?8cD--74mg-`7E_Sv7~mYcogj;iV!*Wa6%aJbGV!ao$`Nn3(xW{EqD zYB-5QDCUg2Q>-uOR2TRuU;h&hcValsv}x*lxXUwJ#fK?`(x}K@8D^E3FcKh_;@ zpH+d;n?|R>n#e2MpJ3Uc3{$67b#;@p5+M9{a9Hbncd0mc_@<6`kVVJIq| zNtvK$$tU&AW^(&oE{m|NTG}|oF(i<8DEU;iYQ z{u70idz_l%_)a(aXTSQWKgj&sR>HORjw|Rrd1O-~1ol~Zx8$`oe}`Y4qp}nM5_r<) z;GyUihgk>S<)1%kCR}O^+xmKYsa984|En7Uvb=ul$iH2_Rbqx#z$HLctV@!Z-fK+f zVz&BA&PUI*@=*++I;cmL5ET?IITnz~p|W?`{)hReGG6JQzemo%=Qazz8FH>pxD@f~<-Fee(;T2)3UE3!BR{jjA7?1hWskdXKXHvkb32?)s%oa*N7JCm$ov&c z5eEVfVVlftV54R>;c_aZW~p(aW$M70*sr-qc9VkJ!u_Wkg++d{R_LAWU~Bd6q2x1U zmCVDi*dw9^GjQr!Gpmv*Xj59F)A&@Fgi-sHf)Si$LQL!pO)AQLE)BJr*A`2+VzBux znstBoKn{#bz4`rfnz_Jp>vVLhrCnnvvj|#PSnzsUySl!6y7#OS2^cun3=TZg2^>@n zIMw*p`}L!z7~H2Z!; z%g0VL5r(Z>qFkR^ew=ql*I*A{pz+HEw{Y=;_5g+L$x38uNZW$&UxNqMpi?Q@(( zmOAf<%PaO++SrFojWm4|X!lp;ZAPX!X5evi%Vkgj4*8Vm*8K5Hzt$|54hs?$kZvDh zBgL`hq?`0VD60b!J`x@7N~2dJvo8&} zXHVxQV-@thq4*v2zZM7nHt(Mnm~1Z@3d5tb!-YkoBwt zGX2+0X5AHU^nKFts84obn+ahU4Me6~ANXH!RQa3HxnqwkqvMZ7jPF{N#gt~qC|1u# zU8YCNv`PJRJlKiDN^1U)4)cAtM_YAXS{EB&Pi06~eA%0D+;xjtgFHRo7ZMb_I$lSP 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 hE!?b002ovPDHLkV1ld9NizTd From f867b65cd20419d75350031320ea2e5109f211e3 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Sat, 19 Aug 2017 23:36:16 +0200 Subject: [PATCH 057/364] creatensis.pl: create signed installer --- ms-windows/Installer-Files/QGIS.ico | Bin 17542 -> 17542 bytes ms-windows/QGIS-Installer.nsi | 34 ++-- ms-windows/osgeo4w/creatensis.pl | 234 +++++++++++++++++----------- 3 files changed, 170 insertions(+), 98 deletions(-) diff --git a/ms-windows/Installer-Files/QGIS.ico b/ms-windows/Installer-Files/QGIS.ico index 0e75a9d6a2238d5ed7b93920f38fdeadbdc189c7..da327712e77d64c48f83e108bbaf5d45e778ee94 100644 GIT binary patch literal 17542 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 literal 17542 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 \$verbose, "keep" => \$keep, + "signwith=s" => \$signwith, + "signpass=s" => \$signpass, "releasename=s" => \$releasename, "version=s" => \$version, "binary=i" => \$binary, @@ -45,6 +49,8 @@ my $result = GetOptions( "help" => \$help ); +die "certificate not found" if defined $signwith && ! -f $signwith; + pod2usage(1) if $help; my $wgetopt = $verbose ? "" : "-nv"; @@ -265,78 +271,6 @@ unless(-d $unpacked ) { chdir ".."; } -# -# Create postinstall.bat -# - -open F, ">../Installer-Files/postinstall.bat"; - -my $r = ">>postinstall.log 2>&1\r\n"; - -print F "\@echo off\r\n"; -print F "if exist postinstall.log del postinstall.log\r\n"; -print F "set OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT:\\=/%$r"; -print F "if \"%OSGEO4W_ROOT_MSYS:~1,1%\"==\":\" set OSGEO4W_ROOT_MSYS=/%OSGEO4W_ROOT_MSYS:~0,1%/%OSGEO4W_ROOT_MSYS:~3%$r"; - -print F "del preremove-conf.bat$r"; -my $c = ">>preremove-conf.bat\r\n"; -print F "echo set OSGEO4W_ROOT=%OSGEO4W_ROOT%$c"; -print F "echo set OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT_MSYS%$c"; -print F "echo set OSGEO4W_STARTMENU=%OSGEO4W_STARTMENU%$c"; -print F "echo set OSGEO4W_DESKTOP=%OSGEO4W_DESKTOP%$c"; - -print F "echo OSGEO4W_ROOT=%OSGEO4W_ROOT%$r"; -print F "echo OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT_MSYS%$r"; -print F "echo OSGEO4W_STARTMENU=%OSGEO4W_STARTMENU%$r"; -print F "echo OSGEO4W_DESKTOP=%OSGEO4W_DESKTOP%$r"; -print F "PATH %OSGEO4W_ROOT%\\bin;%PATH%$r"; -print F "cd %OSGEO4W_ROOT%$r"; - -chdir $unpacked; -for my $p () { - $p =~ s/\//\\/g; - my($dir,$file) = $p =~ /^(.+)\\([^\\]+)$/; - - print F "echo Running postinstall $file...$r"; - print F "%COMSPEC% /c $p$r"; - print F "ren $p $file.done$r"; -} -chdir ".."; - -print F "ren postinstall.bat postinstall.bat.done$r"; - -close F; - -open F, ">../Installer-Files/preremove.bat"; - -$r = ">>%TEMP%\\$packagename-OSGeo4W-$version-$binary-preremove.log 2>&1\r\n"; - -print F "\@echo off\r\n"; -print F "call \"%~dp0\\preremove-conf.bat\"$r"; -print F "echo OSGEO4W_ROOT=%OSGEO4W_ROOT%$r"; -print F "echo OSGEO4W_STARTMENU=%OSGEO4W_STARTMENU%$r"; -print F "echo OSGEO4W_DESKTOP=%OSGEO4W_DESKTOP%$r"; -print F "set OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT:\\=/%$r"; -print F "if \"%OSGEO4W_ROOT_MSYS:~1,1%\"==\":\" set OSGEO4W_ROOT_MSYS=/%OSGEO4W_ROOT_MSYS:~0,1%/%OSGEO4W_ROOT_MSYS:~3%$r"; -print F "echo OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT_MSYS%$r"; -print F "PATH %OSGEO4W_ROOT%\\bin;%PATH%$r"; -print F "cd %OSGEO4W_ROOT%$r"; - -chdir $unpacked; -for my $p () { - $p =~ s/\//\\/g; - my($dir,$file) = $p =~ /^(.+)\\([^\\]+)$/; - - print F "echo Running preremove $file...$r"; - print F "%COMSPEC% /c $p$r"; - print F "ren $p $file.done$r"; -} -chdir ".."; - -print F "ren preremove.bat preremove.bat.done$r"; - -close F; - my($major, $minor, $patch); open F, "../../CMakeLists.txt"; @@ -369,6 +303,78 @@ unless( defined $binary ) { } } +# +# Create postinstall.bat +# + +open F, ">../Installer-Files/postinstall.bat"; + +my $r = ">>postinstall.log 2>&1\r\n"; + +print F "\@echo off\r\n"; +print F "if exist postinstall.log del postinstall.log\r\n"; +print F "set OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT:\\=/%$r"; +print F "if \"%OSGEO4W_ROOT_MSYS:~1,1%\"==\":\" set OSGEO4W_ROOT_MSYS=/%OSGEO4W_ROOT_MSYS:~0,1%/%OSGEO4W_ROOT_MSYS:~3%$r"; + +print F "del preremove-conf.bat$r"; +my $c = ">>preremove-conf.bat\r\n"; +print F "echo set OSGEO4W_ROOT=%OSGEO4W_ROOT%$c"; +print F "echo set OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT_MSYS%$c"; +print F "echo set OSGEO4W_STARTMENU=%OSGEO4W_STARTMENU%$c"; +print F "echo set OSGEO4W_DESKTOP=%OSGEO4W_DESKTOP%$c"; + +print F "echo OSGEO4W_ROOT=%OSGEO4W_ROOT%$r"; +print F "echo OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT_MSYS%$r"; +print F "echo OSGEO4W_STARTMENU=%OSGEO4W_STARTMENU%$r"; +print F "echo OSGEO4W_DESKTOP=%OSGEO4W_DESKTOP%$r"; +print F "PATH %OSGEO4W_ROOT%\\bin;%PATH%$r"; +print F "cd /d %OSGEO4W_ROOT%$r"; + +chdir $unpacked; +for my $p () { + $p =~ s/\//\\/g; + my($dir,$file) = $p =~ /^(.+)\\([^\\]+)$/; + + print F "echo Running postinstall $file...$r"; + print F "%COMSPEC% /c $p$r"; + print F "ren $p $file.done$r"; +} +chdir ".."; + +print F "ren postinstall.bat postinstall.bat.done$r"; + +close F; + +open F, ">../Installer-Files/preremove.bat"; + +$r = ">>%TEMP%\\$packagename-OSGeo4W-$version-$binary-preremove.log 2>&1\r\n"; + +print F "\@echo off\r\n"; +print F "call \"%~dp0\\preremove-conf.bat\"$r"; +print F "echo OSGEO4W_ROOT=%OSGEO4W_ROOT%$r"; +print F "echo OSGEO4W_STARTMENU=%OSGEO4W_STARTMENU%$r"; +print F "echo OSGEO4W_DESKTOP=%OSGEO4W_DESKTOP%$r"; +print F "set OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT:\\=/%$r"; +print F "if \"%OSGEO4W_ROOT_MSYS:~1,1%\"==\":\" set OSGEO4W_ROOT_MSYS=/%OSGEO4W_ROOT_MSYS:~0,1%/%OSGEO4W_ROOT_MSYS:~3%$r"; +print F "echo OSGEO4W_ROOT_MSYS=%OSGEO4W_ROOT_MSYS%$r"; +print F "PATH %OSGEO4W_ROOT%\\bin;%PATH%$r"; +print F "cd /d \"%OSGEO4W_ROOT%\"$r"; + +chdir $unpacked; +for my $p () { + $p =~ s/\//\\/g; + my($dir,$file) = $p =~ /^(.+)\\([^\\]+)$/; + + print F "echo Running preremove $file...$r"; + print F "%COMSPEC% /c $p$r"; + print F "ren $p $file.done$r"; +} +chdir ".."; + +print F "ren preremove.bat preremove.bat.done$r"; + +close F; + unless(-d "untgz") { system "unzip $packages/Untgz.zip"; die "unpacking Untgz.zip failed" if $?; @@ -438,29 +444,77 @@ if( -f "osgeo4w/$unpacked/apps/$shortname/doc/LICENSE" ) { print "Running NSIS\n" if $verbose; -my $cmd = "makensis"; -$cmd .= " -V$verbose"; -$cmd .= " -DVERSION_NAME='$releasename'"; -$cmd .= " -DVERSION_NUMBER='$version'"; -$cmd .= " -DBINARY_REVISION=$binary"; -$cmd .= sprintf( " -DVERSION_INT='%d%02d%02d%02d'", $pmajor, $pminor, $ppatch, $binary ); -$cmd .= sprintf( " -DQGIS_BASE='$packagename %d.%d'", $pmajor, $pminor ); -$cmd .= " -DINSTALLER_NAME='$packagename-OSGeo4W-$version-$binary-Setup$archpostfix.exe'"; -$cmd .= " -DDISPLAYED_NAME=\"$packagename '$releasename' ($version)\""; -$cmd .= " -DSHORTNAME='$shortname'"; -$cmd .= " -DINSTALLER_TYPE=OSGeo4W"; -$cmd .= " -DPACKAGE_FOLDER=osgeo4w/$unpacked"; -$cmd .= " -DLICENSE_FILE='$license'"; -$cmd .= " -DARCH='$arch'"; -$cmd .= " QGIS-Installer.nsi"; +my $installerbase = "$packagename-OSGeo4W-$version-$binary-Setup$archpostfix"; +my $run; +my $instdest; +unless($^O =~ /win/i) { + $run = "wine "; + $instdest = `winepath -w \$PWD`; + $instdest =~ s/\s+$//; + $instdest =~ s/\\/\\\\/g; +} else { + $run = ""; + $instdest = "."; +} + + +my $args = ""; +$args .= " -V$verbose"; +$args .= " -DVERSION_NAME='$releasename'"; +$args .= " -DVERSION_NUMBER='$version'"; +$args .= " -DBINARY_REVISION=$binary"; +$args .= sprintf( " -DVERSION_INT='%d%02d%02d%02d'", $pmajor, $pminor, $ppatch, $binary ); +$args .= sprintf( " -DQGIS_BASE='$packagename %d.%d'", $pmajor, $pminor ); +$args .= " -DDISPLAYED_NAME=\"$packagename $version '$releasename'\""; +$args .= " -DPACKAGE_FOLDER=osgeo4w/$unpacked"; +$args .= " -DLICENSE_FILE='$license'"; +$args .= " -DARCH='$arch'"; +$args .= " QGIS-Installer.nsi"; + +sub sign { + my $base = shift; + + my $cmd = "osslsigncode sign"; + $cmd .= " -pkcs12 \"$signwith\""; + $cmd .= " -pass \"$signpass\"" if defined $signpass; + $cmd .= " -n \"$packagename $version '$releasename'\""; + $cmd .= " -h sha256"; + $cmd .= " -i \"https://qgis.org\""; + $cmd .= " -t \"http://timestamp.digicert.com\""; + $cmd .= " -in \"$base.exe\""; + $cmd .= " $base-signed.exe"; + system $cmd; + die "signing failed [$cmd]" if $?; + + rename("$base-signed.exe", "$base.exe") or die "rename failed: $!"; +} + +my $cmd; +unlink "makeuinst.exe"; +$cmd = "makensis -DINNER=1 -DUNINSTALLERDEST='$instdest' -DINSTALLER_NAME='makeuinst.exe' $args"; system $cmd; -die "running nsis failed [$cmd]" if $?; +die "running makensis failed [$cmd]" if $?; +die "makeuinst.exe not created" unless -f "makeuinst.exe"; + +unlink "uninstall.exe"; +system "${run}makeuinst.exe"; +die "uninstall.exe not created" unless -f "uninstall.exe"; +unlink "makeuinst.exe"; + +sign "uninstall" if $signwith; + +$cmd = "makensis -DINSTALLER_NAME='$installerbase.exe' $args"; +system $cmd; +die "running makensis failed [$cmd]" if $?; + +sign "$installerbase" if $signwith; open P, ">osgeo4w/binary$archpostfix-$version"; print P $binary; close P; +system "md5sum $installerbase.exe >$installerbase.exe.md5sum"; __END__ @@ -476,6 +530,8 @@ creatensis.pl [options] [packages...] -verbose increase verbosity -releasename=name name of release (defaults to CMakeLists.txt setting) -keep don't start with a fresh unpacked directory + -signwith=cert.p12 optionall sign package with certificate (requires osslsigncode) + -signpass=password password of certificate -version=m.m.p package version (defaults to CMakeLists.txt setting) -binary=b binary version of package -ininame=filename name of the setup.ini (defaults to setup.ini) From d2a90f40e34e2b93aac0fbaec64c6353d22dad73 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 23:17:24 +1000 Subject: [PATCH 058/364] Fix conversion of direct vector layer parameters to python strings --- src/core/processing/qgsprocessingparameters.cpp | 4 ++++ tests/src/core/testqgsprocessing.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index d9bb6361e0c..15eac0250bd 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -2565,6 +2565,10 @@ QString QgsProcessingParameterFeatureSource::valueAsPythonString( const QVariant } } } + else if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( qvariant_cast( value ) ) ) + { + return layer->source().prepend( '\'' ).append( '\'' ); + } return value.toString().prepend( '\'' ).append( '\'' ); } diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index 7ea91298df0..5772677c27e 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -3442,6 +3442,7 @@ void TestQgsProcessing::parameterFeatureSource() QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), true ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', True)" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) ); QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) ); + QCOMPARE( def->valueAsPythonString( QVariant::fromValue( v2 ), context ), QStringLiteral( "'%1'" ).arg( vector2 ) ); QVariantMap map = def->toVariantMap(); QgsProcessingParameterFeatureSource fromMap( "x" ); From 6144b1c5d96088d7b98a54baca9c5ca4285c0776 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 23:19:38 +1000 Subject: [PATCH 059/364] Resurrect Field Calculator algorithm, add test --- .../processing/algs/qgis/FieldsCalculator.py | 152 ++++++++---------- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- .../algs/qgis/ui/FieldsCalculatorDialog.py | 34 ++-- python/plugins/processing/gui/TestTools.py | 5 +- .../expected/field_calculator_points.gfs | 31 ++++ .../expected/field_calculator_points.gml | 86 ++++++++++ .../tests/testdata/qgis_algorithm_tests.yaml | 17 ++ 7 files changed, 234 insertions(+), 96 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/field_calculator_points.gml diff --git a/python/plugins/processing/algs/qgis/FieldsCalculator.py b/python/plugins/processing/algs/qgis/FieldsCalculator.py index 87e88abff1b..6d05e60dc9a 100644 --- a/python/plugins/processing/algs/qgis/FieldsCalculator.py +++ b/python/plugins/processing/algs/qgis/FieldsCalculator.py @@ -29,35 +29,31 @@ from qgis.PyQt.QtCore import QVariant from qgis.core import (QgsExpression, QgsExpressionContext, QgsExpressionContextUtils, - QgsFeature, QgsFeatureSink, QgsField, QgsDistanceArea, - QgsProject, - QgsApplication, - QgsProcessingUtils) + QgsProcessingParameterFeatureSource, + QgsProcessingParameterEnum, + QgsProcessingParameterNumber, + QgsProcessingParameterBoolean, + QgsProcessingParameterExpression, + QgsProcessingParameterString, + QgsProcessingParameterFeatureSink, + QgsProcessingException) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterString -from processing.core.parameters import ParameterNumber -from processing.core.parameters import ParameterBoolean -from processing.core.parameters import ParameterSelection -from processing.core.outputs import OutputVector from .ui.FieldsCalculatorDialog import FieldsCalculatorDialog class FieldsCalculator(QgisAlgorithm): - - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' NEW_FIELD = 'NEW_FIELD' FIELD_NAME = 'FIELD_NAME' FIELD_TYPE = 'FIELD_TYPE' FIELD_LENGTH = 'FIELD_LENGTH' FIELD_PRECISION = 'FIELD_PRECISION' FORMULA = 'FORMULA' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' TYPES = [QVariant.Double, QVariant.Int, QVariant.String, QVariant.Date] @@ -66,27 +62,26 @@ class FieldsCalculator(QgisAlgorithm): def __init__(self): super().__init__() - - def initAlgorithm(self, config=None): self.type_names = [self.tr('Float'), self.tr('Integer'), self.tr('String'), self.tr('Date')] - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'))) - self.addParameter(ParameterString(self.FIELD_NAME, - self.tr('Result field name'))) - self.addParameter(ParameterSelection(self.FIELD_TYPE, - self.tr('Field type'), self.type_names)) - self.addParameter(ParameterNumber(self.FIELD_LENGTH, - self.tr('Field length'), 1, 255, 10)) - self.addParameter(ParameterNumber(self.FIELD_PRECISION, - self.tr('Field precision'), 0, 15, 3)) - self.addParameter(ParameterBoolean(self.NEW_FIELD, - self.tr('Create new field'), True)) - self.addParameter(ParameterString(self.FORMULA, self.tr('Formula'))) - self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Calculated'))) + def initAlgorithm(self, config=None): + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterString(self.FIELD_NAME, + self.tr('Result field name'))) + self.addParameter(QgsProcessingParameterEnum(self.FIELD_TYPE, + self.tr('Field type'), options=self.type_names)) + self.addParameter(QgsProcessingParameterNumber(self.FIELD_LENGTH, + self.tr('Field length'), minValue=1, maxValue=255, defaultValue=10)) + self.addParameter(QgsProcessingParameterNumber(self.FIELD_PRECISION, + self.tr('Field precision'), minValue=0, maxValue=15, defaultValue=3)) + self.addParameter(QgsProcessingParameterBoolean(self.NEW_FIELD, + self.tr('Create new field'), defaultValue=True)) + self.addParameter(QgsProcessingParameterExpression(self.FORMULA, self.tr('Formula'))) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('Calculated'))) def name(self): return 'fieldcalculator' @@ -95,77 +90,70 @@ class FieldsCalculator(QgisAlgorithm): return self.tr('Field calculator') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) - fieldName = self.getParameterValue(self.FIELD_NAME) - fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)] - width = self.getParameterValue(self.FIELD_LENGTH) - precision = self.getParameterValue(self.FIELD_PRECISION) - newField = self.getParameterValue(self.NEW_FIELD) - formula = self.getParameterValue(self.FORMULA) - - output = self.getOutputFromName(self.OUTPUT_LAYER) - - fields = layer.fields() - if newField: - fields.append(QgsField(fieldName, fieldType, '', width, precision)) - - writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context) - - exp = QgsExpression(formula) + source = self.parameterAsSource(parameters, self.INPUT, context) + layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) + field_name = self.parameterAsString(parameters, self.FIELD_NAME, context) + field_type = self.TYPES[self.parameterAsEnum(parameters, self.FIELD_TYPE, context)] + width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context) + precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context) + new_field = self.parameterAsBool(parameters, self.NEW_FIELD, context) + formula = self.parameterAsString(parameters, self.FORMULA, context) + expression = QgsExpression(formula) da = QgsDistanceArea() - da.setSourceCrs(layer.crs()) + da.setSourceCrs(source.sourceCrs()) da.setEllipsoid(context.project().ellipsoid()) - exp.setGeomCalculator(da) - exp.setDistanceUnits(context.project().distanceUnits()) - exp.setAreaUnits(context.project().areaUnits()) + expression.setGeomCalculator(da) - exp_context = QgsExpressionContext(QgsExpressionContextUtils.globalProjectLayerScopes(layer)) + expression.setDistanceUnits(context.project().distanceUnits()) + expression.setAreaUnits(context.project().areaUnits()) - if not exp.prepare(exp_context): - raise GeoAlgorithmExecutionException( - self.tr('Evaluation error: {0}').format(exp.evalErrorString())) + fields = source.fields() + field_index = fields.lookupField(field_name) + if new_field or field_index < 0: + fields.append(QgsField(field_name, field_type, '', width, precision)) - outFeature = QgsFeature() - outFeature.initAttributes(len(fields)) - outFeature.setFields(fields) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, source.wkbType(), source.sourceCrs()) - error = '' - calculationSuccess = True + exp_context = self.createExpressionContext(parameters, context) + if layer is not None: + exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / layer.featureCount() if layer.featureCount() else 0 + if not expression.prepare(exp_context): + raise QgsProcessingException( + self.tr('Evaluation error: {0}').format(expression.parserErrorString())) + + features = source.getFeatures() + total = 100.0 / source.featureCount() if source.featureCount() else 0 - rownum = 1 for current, f in enumerate(features): + if feedback.isCanceled(): + break + rownum = current + 1 exp_context.setFeature(f) exp_context.lastScope().setVariable("row_number", rownum) - value = exp.evaluate(exp_context) - if exp.hasEvalError(): - calculationSuccess = False - error = exp.evalErrorString() - break + value = expression.evaluate(exp_context) + if expression.hasEvalError(): + feedback.reportError(expression.evalErrorString()) else: - outFeature.setGeometry(f.geometry()) - for fld in f.fields(): - outFeature[fld.name()] = f[fld.name()] - outFeature[fieldName] = value - writer.addFeature(outFeature, QgsFeatureSink.FastInsert) - + attrs = f.attributes() + if new_field or field_index < 0: + attrs.append(value) + else: + attrs[field_index] = value + f.setAttributes(attrs) + sink.addFeature(f, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) - del writer - if not calculationSuccess: - raise GeoAlgorithmExecutionException( - self.tr('An error occurred while evaluating the calculation ' - 'string:\n{0}').format(error)) + return {self.OUTPUT: dest_id} def checkParameterValues(self, parameters, context): - newField = self.getParameterValue(self.NEW_FIELD) - fieldName = self.getParameterValue(self.FIELD_NAME).strip() + newField = self.parameterAsBool(parameters, self.NEW_FIELD, context) + fieldName = self.parameterAsString(parameters, self.FIELD_NAME, context).strip() if newField and len(fieldName) == 0: - return self.tr('Field name is not set. Please enter a field name') + return False, self.tr('Field name is not set. Please enter a field name') return super(FieldsCalculator, self).checkParameterValues(parameters, context) def createCustomParametersWidget(self, parent): diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 3f9e953b177..96f8b98d456 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -70,6 +70,7 @@ from .ExtendLines import ExtendLines from .ExtentFromLayer import ExtentFromLayer from .ExtractNodes import ExtractNodes from .ExtractSpecificNodes import ExtractSpecificNodes +from .FieldsCalculator import FieldsCalculator from .FieldsMapper import FieldsMapper from .FixedDistanceBuffer import FixedDistanceBuffer from .FixGeometry import FixGeometry @@ -166,7 +167,6 @@ from .ZonalStatistics import ZonalStatistics # from .SelectByLocation import SelectByLocation # from .SpatialJoin import SpatialJoin # from .GeometryConvert import GeometryConvert -# from .FieldsCalculator import FieldsCalculator # from .FieldPyculator import FieldsPyculator # from .SelectByAttributeSum import SelectByAttributeSum # from .DefineProjection import DefineProjection @@ -190,7 +190,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SelectByLocation(), # ExtractByLocation(), # SpatialJoin(), - # GeometryConvert(), FieldsCalculator(), + # GeometryConvert(), # FieldsPyculator(), # FieldsMapper(), SelectByAttributeSum() # DefineProjection(), @@ -227,6 +227,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ExtentFromLayer(), ExtractNodes(), ExtractSpecificNodes(), + FieldsCalculator(), FieldsMapper(), FixedDistanceBuffer(), FixGeometry(), diff --git a/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py b/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py index a43b3a4cb49..a17f2bfa5f4 100644 --- a/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py +++ b/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py @@ -36,9 +36,11 @@ from qgis.PyQt.QtGui import QCursor from qgis.core import (QgsExpressionContextUtils, QgsProcessingFeedback, QgsSettings, - QgsProcessingUtils, QgsMapLayerProxyModel, - QgsMessageLog) + QgsProperty, + QgsProject, + QgsMessageLog, + QgsProcessingOutputLayerDefinition) from qgis.gui import QgsEncodingFileDialog from qgis.utils import OverrideCursor @@ -47,6 +49,8 @@ from processing.core.ProcessingLog import ProcessingLog from processing.gui.AlgorithmExecutor import execute from processing.tools import dataobjects from processing.gui.Postprocessing import handleAlgorithmResults +from processing.gui.PostgisTableSelector import PostgisTableSelector +from processing.gui.ParameterGuiUtils import getFileFilter pluginPath = os.path.dirname(__file__) WIDGET, BASE = uic.loadUiType( @@ -73,9 +77,6 @@ class FieldsCalculatorDialog(BASE, WIDGET): super(FieldsCalculatorDialog, self).__init__(None) self.setupUi(self) - self.feedback = FieldCalculatorFeedback(self) - self.feedback.progressChanged.connect(self.setPercentage) - self.executed = False self.alg = alg self.layer = None @@ -144,8 +145,8 @@ class FieldsCalculatorDialog(BASE, WIDGET): self.mOutputFieldPrecisionSpinBox.setEnabled(False) def selectFile(self): - output = self.alg.getOutputFromName('OUTPUT_LAYER') - fileFilter = output.getFileFilter(self.alg) + output = self.alg.parameterDefinition('OUTPUT') + fileFilter = getFileFilter(output) settings = QgsSettings() if settings.contains('/Processing/LastOutputPath'): @@ -200,17 +201,23 @@ class FieldsCalculatorDialog(BASE, WIDGET): layer = self.cmbInputLayer.currentLayer() + context = dataobjects.createContext() + parameters = {} - parameters['INPUT_LAYER'] = layer + parameters['INPUT'] = layer parameters['FIELD_NAME'] = fieldName parameters['FIELD_TYPE'] = self.mOutputFieldTypeComboBox.currentIndex() parameters['FIELD_LENGTH'] = self.mOutputFieldWidthSpinBox.value() parameters['FIELD_PRECISION'] = self.mOutputFieldPrecisionSpinBox.value() parameters['NEW_FIELD'] = self.mNewFieldGroupBox.isChecked() parameters['FORMULA'] = self.builder.expressionText() - parameters['OUTPUT_LAYER'] = self.leOutputFile.text().strip() or None - - context = dataobjects.createContext() + output = QgsProcessingOutputLayerDefinition() + if self.leOutputFile.text().strip(): + output.sink = QgsProperty.fromValue(self.leOutputFile.text().strip()) + else: + output.sink = QgsProperty.fromValue('memory:') + output.destinationProject = context.project() + parameters['OUTPUT'] = output ok, msg = self.alg.checkParameterValues(parameters, context) if not ok: @@ -224,10 +231,15 @@ class FieldsCalculatorDialog(BASE, WIDGET): parameters = self.getParamValues() if parameters: with OverrideCursor(Qt.WaitCursor): + self.feedback = FieldCalculatorFeedback(self) + self.feedback.progressChanged.connect(self.setPercentage) + context = dataobjects.createContext() ProcessingLog.addToLog(self.alg.asPythonCommand(parameters, context)) self.executed, results = execute(self.alg, parameters, context, self.feedback) + self.setPercentage(0) + if self.executed: handleAlgorithmResults(self.alg, context, diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index 4d986d7ea3d..5489414e54b 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -51,7 +51,8 @@ from qgis.core import (QgsApplication, QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination, - QgsProcessingParameterFileDestination) + QgsProcessingParameterFileDestination, + QgsProcessingParameterEnum) from qgis.PyQt.QtCore import QCoreApplication, QMetaObject from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout, QTextEdit, QMessageBox @@ -223,6 +224,8 @@ def createTest(text): params[param.name()] = int(token) else: params[param.name()] = float(token) + elif isinstance(param, QgsProcessingParameterEnum): + params[param.name()] = int(token) else: if token[0] == '"': token = token[1:] diff --git a/python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs b/python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs new file mode 100644 index 00000000000..fc35e4b96c0 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs @@ -0,0 +1,31 @@ + + + field_calculator_points + field_calculator_points + + 1 + EPSG:4326 + + 9 + 0.00000 + 8.00000 + -5.00000 + 3.00000 + + + id + id + Integer + + + id2 + id2 + Integer + + + calc + calc + Integer + + + diff --git a/python/plugins/processing/tests/testdata/expected/field_calculator_points.gml b/python/plugins/processing/tests/testdata/expected/field_calculator_points.gml new file mode 100644 index 00000000000..1f008e02c31 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/field_calculator_points.gml @@ -0,0 +1,86 @@ + + + + + 0-5 + 83 + + + + + + 1,1 + 1 + 2 + 4 + + + + + 3,3 + 2 + 1 + 2 + + + + + 2,2 + 3 + 0 + 0 + + + + + 5,2 + 4 + 2 + 4 + + + + + 4,1 + 5 + 1 + 2 + + + + + 0,-5 + 6 + 0 + 0 + + + + + 8,-1 + 7 + 0 + 0 + + + + + 7,-1 + 8 + 0 + 0 + + + + + 0,-1 + 9 + 0 + 0 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 8fa3bf5dd9e..075e896c422 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3176,3 +3176,20 @@ tests: OUTPUT: name: expected/pixel_centroids_lines.gml type: vector + + - algorithm: qgis:fieldcalculator + name: Test field calculator points + params: + FIELD_LENGTH: 10 + FIELD_NAME: test + FIELD_PRECISION: 3 + FIELD_TYPE: 1 + FORMULA: ' "id2" *2' + INPUT: + name: points.gml + type: vector + NEW_FIELD: true + results: + OUTPUT: + name: expected/field_calculator_points.gml + type: vector From a56725f76e1029b919a247ef0100604b3b7c5c05 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 19 Aug 2017 23:31:31 +1000 Subject: [PATCH 060/364] Resurrect Python Field Calculator algorithm, add test --- .../processing/algs/qgis/FieldPyculator.py | 98 +++++++++---------- .../algs/qgis/QGISAlgorithmProvider.py | 6 +- .../testdata/expected/pycalculator_points.gfs | 31 ++++++ .../testdata/expected/pycalculator_points.gml | 86 ++++++++++++++++ .../tests/testdata/qgis_algorithm_tests.yaml | 17 ++++ 5 files changed, 186 insertions(+), 52 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/expected/pycalculator_points.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/pycalculator_points.gml diff --git a/python/plugins/processing/algs/qgis/FieldPyculator.py b/python/plugins/processing/algs/qgis/FieldPyculator.py index 0203897bb57..eb00cfcad04 100644 --- a/python/plugins/processing/algs/qgis/FieldPyculator.py +++ b/python/plugins/processing/algs/qgis/FieldPyculator.py @@ -29,30 +29,27 @@ __revision__ = '$Format:%H$' import sys from qgis.PyQt.QtCore import QVariant -from qgis.core import (QgsFeature, +from qgis.core import (QgsProcessingException, QgsField, QgsFeatureSink, - QgsApplication, - QgsProcessingUtils) + QgsProcessingParameterFeatureSource, + QgsProcessingParameterString, + QgsProcessingParameterEnum, + QgsProcessingParameterNumber, + QgsProcessingParameterFeatureSink) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterString -from processing.core.parameters import ParameterNumber -from processing.core.parameters import ParameterSelection -from processing.core.outputs import OutputVector class FieldsPyculator(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' FIELD_NAME = 'FIELD_NAME' FIELD_TYPE = 'FIELD_TYPE' FIELD_LENGTH = 'FIELD_LENGTH' FIELD_PRECISION = 'FIELD_PRECISION' GLOBAL = 'GLOBAL' FORMULA = 'FORMULA' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' RESULT_VAR_NAME = 'value' TYPES = [QVariant.Int, QVariant.Double, QVariant.String] @@ -68,21 +65,21 @@ class FieldsPyculator(QgisAlgorithm): self.tr('Float'), self.tr('String')] - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'))) - self.addParameter(ParameterString(self.FIELD_NAME, - self.tr('Result field name'), 'NewField')) - self.addParameter(ParameterSelection(self.FIELD_TYPE, - self.tr('Field type'), self.type_names)) - self.addParameter(ParameterNumber(self.FIELD_LENGTH, - self.tr('Field length'), 1, 255, 10)) - self.addParameter(ParameterNumber(self.FIELD_PRECISION, - self.tr('Field precision'), 0, 10, 0)) - self.addParameter(ParameterString(self.GLOBAL, - self.tr('Global expression'), multiline=True, optional=True)) - self.addParameter(ParameterString(self.FORMULA, - self.tr('Formula'), 'value = ', multiline=True)) - self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Calculated'))) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterString(self.FIELD_NAME, + self.tr('Result field name'), defaultValue='NewField')) + self.addParameter(QgsProcessingParameterEnum(self.FIELD_TYPE, + self.tr('Field type'), options=self.type_names)) + self.addParameter(QgsProcessingParameterNumber(self.FIELD_LENGTH, + self.tr('Field length'), minValue=1, maxValue=255, defaultValue=10)) + self.addParameter(QgsProcessingParameterNumber(self.FIELD_PRECISION, + self.tr('Field precision'), minValue=0, maxValue=15, defaultValue=3)) + self.addParameter(QgsProcessingParameterString(self.GLOBAL, + self.tr('Global expression'), multiLine=True, optional=True)) + self.addParameter(QgsProcessingParameterString(self.FORMULA, + self.tr('Formula'), defaultValue='value = ', multiLine=True)) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('Calculated'))) def name(self): return 'advancedpythonfieldcalculator' @@ -91,33 +88,33 @@ class FieldsPyculator(QgisAlgorithm): return self.tr('Advanced Python field calculator') def processAlgorithm(self, parameters, context, feedback): - fieldName = self.getParameterValue(self.FIELD_NAME) - fieldType = self.getParameterValue(self.FIELD_TYPE) - fieldLength = self.getParameterValue(self.FIELD_LENGTH) - fieldPrecision = self.getParameterValue(self.FIELD_PRECISION) - code = self.getParameterValue(self.FORMULA) - globalExpression = self.getParameterValue(self.GLOBAL) - output = self.getOutputFromName(self.OUTPUT_LAYER) + source = self.parameterAsSource(parameters, self.INPUT, context) + field_name = self.parameterAsString(parameters, self.FIELD_NAME, context) + field_type = self.TYPES[self.parameterAsEnum(parameters, self.FIELD_TYPE, context)] + width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context) + precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context) + code = self.parameterAsString(parameters, self.FORMULA, context) + globalExpression = self.parameterAsString(parameters, self.GLOBAL, context) - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) - fields = layer.fields() - fields.append(QgsField(fieldName, self.TYPES[fieldType], '', - fieldLength, fieldPrecision)) - writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context) - outFeat = QgsFeature() + fields = source.fields() + fields.append(QgsField(field_name, self.TYPES[field_type], '', + width, precision)) new_ns = {} + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, source.wkbType(), source.sourceCrs()) + # Run global code if globalExpression.strip() != '': try: bytecode = compile(globalExpression, '', 'exec') exec(bytecode, new_ns) except: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr("FieldPyculator code execute error.Global code block can't be executed!\n{0}\n{1}").format(str(sys.exc_info()[0].__name__), str(sys.exc_info()[1]))) # Replace all fields tags - fields = layer.fields() + fields = source.fields() num = 0 for field in fields: field_name = str(field.name()) @@ -136,13 +133,17 @@ class FieldsPyculator(QgisAlgorithm): try: bytecode = compile(code, '', 'exec') except: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr("FieldPyculator code execute error. Field code block can't be executed!\n{0}\n{1}").format(str(sys.exc_info()[0].__name__), str(sys.exc_info()[1]))) # Run - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / layer.featureCount() if layer.featureCount() else 0 + features = source.getFeatures() + total = 100.0 / source.featureCount() if source.featureCount() else 0 + for current, feat in enumerate(features): + if feedback.isCanceled(): + break + feedback.setProgress(int(current * total)) attrs = feat.attributes() feat_id = feat.id() @@ -168,18 +169,17 @@ class FieldsPyculator(QgisAlgorithm): # Check result if self.RESULT_VAR_NAME not in new_ns: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr("FieldPyculator code execute error\n" "Field code block does not return '{0}' variable! " "Please declare this variable in your code!").format(self.RESULT_VAR_NAME)) # Write feature - outFeat.setGeometry(feat.geometry()) attrs.append(new_ns[self.RESULT_VAR_NAME]) - outFeat.setAttributes(attrs) - writer.addFeature(outFeat, QgsFeatureSink.FastInsert) + feat.setAttributes(attrs) + sink.addFeature(feat, QgsFeatureSink.FastInsert) - del writer + return {self.OUTPUT: dest_id} def checkParameterValues(self, parameters, context): # TODO check that formula is correct and fields exist diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 96f8b98d456..12d3442a87c 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -70,6 +70,7 @@ from .ExtendLines import ExtendLines from .ExtentFromLayer import ExtentFromLayer from .ExtractNodes import ExtractNodes from .ExtractSpecificNodes import ExtractSpecificNodes +from .FieldPyculator import FieldsPyculator from .FieldsCalculator import FieldsCalculator from .FieldsMapper import FieldsMapper from .FixedDistanceBuffer import FixedDistanceBuffer @@ -167,7 +168,6 @@ from .ZonalStatistics import ZonalStatistics # from .SelectByLocation import SelectByLocation # from .SpatialJoin import SpatialJoin # from .GeometryConvert import GeometryConvert -# from .FieldPyculator import FieldsPyculator # from .SelectByAttributeSum import SelectByAttributeSum # from .DefineProjection import DefineProjection # from .RasterCalculator import RasterCalculator @@ -191,8 +191,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # ExtractByLocation(), # SpatialJoin(), # GeometryConvert(), - # FieldsPyculator(), - # FieldsMapper(), SelectByAttributeSum() + # SelectByAttributeSum() # DefineProjection(), # RasterCalculator(), # ExecuteSQL(), FindProjection(), @@ -229,6 +228,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ExtractSpecificNodes(), FieldsCalculator(), FieldsMapper(), + FieldsPyculator(), FixedDistanceBuffer(), FixGeometry(), GeometryByExpression(), diff --git a/python/plugins/processing/tests/testdata/expected/pycalculator_points.gfs b/python/plugins/processing/tests/testdata/expected/pycalculator_points.gfs new file mode 100644 index 00000000000..3b46eed3ef5 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/pycalculator_points.gfs @@ -0,0 +1,31 @@ + + + pycalculator_points + pycalculator_points + + 1 + EPSG:4326 + + 9 + 0.00000 + 8.00000 + -5.00000 + 3.00000 + + + id + id + Integer + + + id2 + id2 + Integer + + + new_field + new_field + Integer + + + diff --git a/python/plugins/processing/tests/testdata/expected/pycalculator_points.gml b/python/plugins/processing/tests/testdata/expected/pycalculator_points.gml new file mode 100644 index 00000000000..d1a7caadaa7 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/pycalculator_points.gml @@ -0,0 +1,86 @@ + + + + + 0-5 + 83 + + + + + + 1,1 + 1 + 2 + 4 + + + + + 3,3 + 2 + 1 + 2 + + + + + 2,2 + 3 + 0 + 0 + + + + + 5,2 + 4 + 2 + 4 + + + + + 4,1 + 5 + 1 + 2 + + + + + 0,-5 + 6 + 0 + 0 + + + + + 8,-1 + 7 + 0 + 0 + + + + + 7,-1 + 8 + 0 + 0 + + + + + 0,-1 + 9 + 0 + 0 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 075e896c422..6ba27247c3b 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3193,3 +3193,20 @@ tests: OUTPUT: name: expected/field_calculator_points.gml type: vector + + - algorithm: qgis:advancedpythonfieldcalculator + name: Test advanced python calculator + params: + FIELD_LENGTH: 10 + FIELD_NAME: new_field + FIELD_PRECISION: 3 + FIELD_TYPE: 0 + FORMULA: value = __attr[2]*2 + GLOBAL: '' + INPUT: + name: points.gml + type: vector + results: + OUTPUT: + name: expected/pycalculator_points.gml + type: vector From cfb926a70de8dcf8a13531c7094427876feef276 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 00:48:26 +1000 Subject: [PATCH 061/364] Port Find Projection alg to new API Also modify alg to export a vector table of candidates instead of a HTML list, since a vector table is more useful inside of models and can be used for further analysis steps. --- .../processing/algs/qgis/FindProjection.py | 89 ++++++++++--------- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- .../expected/projection_candidates.gfs | 16 ++++ .../expected/projection_candidates.gml | 49 ++++++++++ .../tests/testdata/qgis_algorithm_tests.yaml | 26 +++--- 5 files changed, 129 insertions(+), 56 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/expected/projection_candidates.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/projection_candidates.gml diff --git a/python/plugins/processing/algs/qgis/FindProjection.py b/python/plugins/processing/algs/qgis/FindProjection.py index 94bfa8e3460..62f61f6772b 100644 --- a/python/plugins/processing/algs/qgis/FindProjection.py +++ b/python/plugins/processing/algs/qgis/FindProjection.py @@ -26,31 +26,32 @@ __copyright__ = '(C) 2017, Nyall Dawson' __revision__ = '$Format:%H$' import os -import codecs -from qgis.core import (QgsApplication, - QgsGeometry, +from qgis.core import (QgsGeometry, + QgsFeature, QgsFeatureSink, - QgsRectangle, + QgsField, + QgsFields, QgsCoordinateReferenceSystem, QgsCoordinateTransform, - QgsProcessingUtils) + QgsWkbTypes, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterExtent, + QgsProcessingParameterCrs, + QgsProcessingParameterFeatureSink) +from qgis.PyQt.QtCore import QVariant from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterCrs -from processing.core.parameters import ParameterExtent -from processing.core.outputs import OutputHTML pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] class FindProjection(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' TARGET_AREA = 'TARGET_AREA' TARGET_AREA_CRS = 'TARGET_AREA_CRS' - OUTPUT_HTML_FILE = 'OUTPUT_HTML_FILE' + OUTPUT = 'OUTPUT' def tags(self): return self.tr('crs,srs,coordinate,reference,system,guess,estimate,finder,determine').split(',') @@ -62,17 +63,16 @@ class FindProjection(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'))) - extent_parameter = ParameterExtent(self.TARGET_AREA, - self.tr('Target area for layer'), - self.INPUT_LAYER) - extent_parameter.skip_crs_check = True + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + extent_parameter = QgsProcessingParameterExtent(self.TARGET_AREA, + self.tr('Target area for layer')) + #extent_parameter.skip_crs_check = True self.addParameter(extent_parameter) - self.addParameter(ParameterCrs(self.TARGET_AREA_CRS, 'Target area CRS')) + self.addParameter(QgsProcessingParameterCrs(self.TARGET_AREA_CRS, 'Target area CRS')) - self.addOutput(OutputHTML(self.OUTPUT_HTML_FILE, - self.tr('Candidates'))) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('CRS candidates'))) def name(self): return 'findprojection' @@ -81,27 +81,34 @@ class FindProjection(QgisAlgorithm): return self.tr('Find projection') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) + source = self.parameterAsSource(parameters, self.INPUT, context) - extent = self.getParameterValue(self.TARGET_AREA).split(',') - if not extent: - extent = QgsProcessingUtils.combineLayerExtents([layer]) - target_crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.TARGET_AREA_CRS)) + extent = self.parameterAsExtent(parameters, self.TARGET_AREA, context) + target_crs = self.parameterAsCrs(parameters, self.TARGET_AREA_CRS, context) - target_geom = QgsGeometry.fromRect(QgsRectangle(float(extent[0]), float(extent[2]), - float(extent[1]), float(extent[3]))) + target_geom = QgsGeometry.fromRect(extent) - output_file = self.getOutputValue(self.OUTPUT_HTML_FILE) + fields = QgsFields() + fields.append(QgsField('auth_id', QVariant.String, '', 20)) + + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) # make intersection tests nice and fast engine = QgsGeometry.createGeometryEngine(target_geom.geometry()) engine.prepareGeometry() - layer_bounds = QgsGeometry.fromRect(layer.extent()) + layer_bounds = QgsGeometry.fromRect(source.sourceExtent()) - results = [] + crses_to_check = QgsCoordinateReferenceSystem.validSrsIds() + total = 100.0 / len(crses_to_check) + + found_results = 0 + + for current, srs_id in enumerate(crses_to_check): + if feedback.isCanceled(): + break - for srs_id in QgsCoordinateReferenceSystem.validSrsIds(): candidate_crs = QgsCoordinateReferenceSystem.fromSrsId(srs_id) if not candidate_crs.isValid(): continue @@ -115,15 +122,15 @@ class FindProjection(QgisAlgorithm): continue if engine.intersects(transformed_bounds.geometry()): - results.append(candidate_crs.authid()) + feedback.pushInfo(self.tr('Found candidate CRS: {}').format(candidate_crs.authid())) + f = QgsFeature(fields) + f.setAttributes([candidate_crs.authid()]) + sink.addFeature(f, QgsFeatureSink.FastInsert) + found_results += 1 - self.createHTML(output_file, results) + feedback.setProgress(int(current * total)) - def createHTML(self, outputFile, candidates): - with codecs.open(outputFile, 'w', encoding='utf-8') as f: - f.write('\n') - f.write('\n') - for c in candidates: - f.write('

' + c + '

\n') - f.write('\n') + if found_results == 0: + feedback.reportError(self.tr('No matching projections found')) + + return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 12d3442a87c..f8291c73e1a 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -73,6 +73,7 @@ from .ExtractSpecificNodes import ExtractSpecificNodes from .FieldPyculator import FieldsPyculator from .FieldsCalculator import FieldsCalculator from .FieldsMapper import FieldsMapper +from .FindProjection import FindProjection from .FixedDistanceBuffer import FixedDistanceBuffer from .FixGeometry import FixGeometry from .GeometryByExpression import GeometryByExpression @@ -172,7 +173,6 @@ from .ZonalStatistics import ZonalStatistics # from .DefineProjection import DefineProjection # from .RasterCalculator import RasterCalculator # from .ExecuteSQL import ExecuteSQL -# from .FindProjection import FindProjection pluginPath = os.path.normpath(os.path.join( os.path.split(os.path.dirname(__file__))[0], os.pardir)) @@ -194,7 +194,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SelectByAttributeSum() # DefineProjection(), # RasterCalculator(), - # ExecuteSQL(), FindProjection(), + # ExecuteSQL(), # ] algs = [AddTableField(), Aggregate(), @@ -229,6 +229,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): FieldsCalculator(), FieldsMapper(), FieldsPyculator(), + FindProjection(), FixedDistanceBuffer(), FixGeometry(), GeometryByExpression(), diff --git a/python/plugins/processing/tests/testdata/expected/projection_candidates.gfs b/python/plugins/processing/tests/testdata/expected/projection_candidates.gfs new file mode 100644 index 00000000000..5dd5db8b754 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/projection_candidates.gfs @@ -0,0 +1,16 @@ + + + projection_candidates + projection_candidates + 100 + + 8 + + + auth_id + auth_id + String + 14 + + + diff --git a/python/plugins/processing/tests/testdata/expected/projection_candidates.gml b/python/plugins/processing/tests/testdata/expected/projection_candidates.gml new file mode 100644 index 00000000000..f46f2f4b864 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/projection_candidates.gml @@ -0,0 +1,49 @@ + + + missing + + + + EPSG:20256 + + + + + EPSG:20356 + + + + + EPSG:28356 + + + + + EPSG:32356 + + + + + EPSG:32556 + + + + + EPSG:32756 + + + + + IGNF:UTM56SW84 + + + + + EPSG:5552 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 6ba27247c3b..df4888d4de3 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2827,19 +2827,19 @@ tests: fields: fid: skip -# - algorithm: qgis:findprojection -# name: Find projection -# params: -# INPUT_LAYER: -# name: custom/find_projection.gml -# type: vector -# TARGET_AREA: 151.1198,151.1368,-33.9118,-33.9003 -# TARGET_AREA_CRS: EPSG:4326 -# results: -# OUTPUT_HTML_FILE: -# name: expected/find_projection.html -# type: file -# + - algorithm: qgis:findprojection + name: Find projection + params: + INPUT: + name: custom/find_projection.gml + type: vector + TARGET_AREA: 151.1198,151.1368,-33.9118,-33.9003 + TARGET_AREA_CRS: EPSG:4326 + results: + OUTPUT: + name: expected/projection_candidates.gml + type: vector + - algorithm: qgis:polygonfromlayerextent name: Standard polygon from layer extent params: From 2a6847e03080e7f11fd2180b34a064baeb2db184 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 01:00:59 +1000 Subject: [PATCH 062/364] Port define projection to new API --- .../processing/algs/qgis/DefineProjection.py | 31 ++++++++----------- .../algs/qgis/QGISAlgorithmProvider.py | 4 +-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/python/plugins/processing/algs/qgis/DefineProjection.py b/python/plugins/processing/algs/qgis/DefineProjection.py index 492e6a38395..520ef50fd49 100644 --- a/python/plugins/processing/algs/qgis/DefineProjection.py +++ b/python/plugins/processing/algs/qgis/DefineProjection.py @@ -28,15 +28,12 @@ __revision__ = '$Format:%H$' import os import re -from qgis.core import (QgsCoordinateReferenceSystem, - QgsApplication, - QgsProcessingUtils) -from qgis.utils import iface +from qgis.core import (QgsProcessing, + QgsProcessingParameterVectorLayer, + QgsProcessingParameterCrs, + QgsProcessingOutputVectorLayer) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterCrs -from processing.core.outputs import OutputVector pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] @@ -45,7 +42,6 @@ class DefineProjection(QgisAlgorithm): INPUT = 'INPUT' CRS = 'CRS' - OUTPUT = 'OUTPUT' def group(self): return self.tr('Vector general tools') @@ -54,11 +50,11 @@ class DefineProjection(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input Layer'))) - self.addParameter(ParameterCrs(self.CRS, 'Output CRS')) - self.addOutput(OutputVector(self.OUTPUT, - self.tr('Layer with projection'), True)) + self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT, + self.tr('Input Layer'), types=[QgsProcessing.TypeVectorAnyGeometry])) + self.addParameter(QgsProcessingParameterCrs(self.CRS, 'Output CRS')) + self.addOutput(QgsProcessingOutputVectorLayer(self.INPUT, + self.tr('Layer with projection'))) def name(self): return 'definecurrentprojection' @@ -67,9 +63,8 @@ class DefineProjection(QgisAlgorithm): return self.tr('Define current projection') def processAlgorithm(self, parameters, context, feedback): - fileName = self.getParameterValue(self.INPUT) - layer = QgsProcessingUtils.mapLayerFromString(fileName, context) - crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) + layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) + crs = self.parameterAsCrs(parameters, self.CRS, context) provider = layer.dataProvider() ds = provider.dataSourceUri() @@ -89,6 +84,6 @@ class DefineProjection(QgisAlgorithm): f.write(wkt) layer.setCrs(crs) - iface.mapCanvas().refresh() + layer.triggerRepaint() - self.setOutputValue(self.OUTPUT, fileName) + return {self.INPUT: layer} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index f8291c73e1a..ce6817fbd35 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -53,6 +53,7 @@ from .ConvexHull import ConvexHull from .CreateAttributeIndex import CreateAttributeIndex from .CreateConstantRaster import CreateConstantRaster from .Datasources2Vrt import Datasources2Vrt +from .DefineProjection import DefineProjection from .Delaunay import Delaunay from .DeleteColumn import DeleteColumn from .DeleteDuplicateGeometries import DeleteDuplicateGeometries @@ -170,7 +171,6 @@ from .ZonalStatistics import ZonalStatistics # from .SpatialJoin import SpatialJoin # from .GeometryConvert import GeometryConvert # from .SelectByAttributeSum import SelectByAttributeSum -# from .DefineProjection import DefineProjection # from .RasterCalculator import RasterCalculator # from .ExecuteSQL import ExecuteSQL @@ -192,7 +192,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SpatialJoin(), # GeometryConvert(), # SelectByAttributeSum() - # DefineProjection(), # RasterCalculator(), # ExecuteSQL(), # ] @@ -209,6 +208,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): CreateAttributeIndex(), CreateConstantRaster(), Datasources2Vrt(), + DefineProjection(), Delaunay(), DeleteColumn(), DeleteDuplicateGeometries(), From 99fd727baba8963ac7616ce5ce95a6fb305b278d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 01:37:13 +1000 Subject: [PATCH 063/364] Fix some issues when creating processing tests --- python/plugins/processing/gui/TestTools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index 5489414e54b..1d8bf7cdd10 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -153,6 +153,9 @@ def createTest(text): if param.flags() & QgsProcessingParameterDefinition.FlagHidden or param.isDestination(): continue + if not param.name() in parameters: + continue + i += 1 token = parameters[param.name()] # Handle empty parameters that are optionals @@ -180,7 +183,7 @@ def createTest(text): params[param.name()] = p elif isinstance(param, QgsProcessingParameterMultipleLayers): - multiparams = token.split(';') + multiparams = token newparam = [] # Handle datatype detection From 338ee36b1dfbc18948c01c66d7c1875669ea448e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 01:37:27 +1000 Subject: [PATCH 064/364] Port Execute SQL to new API, add test --- .../processing/algs/qgis/ExecuteSQL.py | 100 ++++++++---------- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- .../tests/testdata/expected/execute_sql.gfs | 26 +++++ .../tests/testdata/expected/execute_sql.gml | 28 +++++ .../tests/testdata/qgis_algorithm_tests.yaml | 17 +++ 5 files changed, 118 insertions(+), 58 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/expected/execute_sql.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/execute_sql.gml diff --git a/python/plugins/processing/algs/qgis/ExecuteSQL.py b/python/plugins/processing/algs/qgis/ExecuteSQL.py index 92f8d5260a2..6054d7860c9 100644 --- a/python/plugins/processing/algs/qgis/ExecuteSQL.py +++ b/python/plugins/processing/algs/qgis/ExecuteSQL.py @@ -25,21 +25,18 @@ __copyright__ = '(C) 2016, Hugo Mercier' __revision__ = '$Format:%H$' -from qgis.core import (QgsFeature, - QgsVirtualLayerDefinition, +from qgis.core import (QgsVirtualLayerDefinition, QgsVectorLayer, - QgsCoordinateReferenceSystem, QgsWkbTypes, - QgsApplication, - QgsProcessingUtils) + QgsProcessingParameterMultipleLayers, + QgsProcessingParameterString, + QgsProcessingParameterEnum, + QgsProcessingParameterCrs, + QgsProcessingParameterFeatureSink, + QgsFeatureSink, + QgsProcessingException) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import ParameterString -from processing.core.parameters import ParameterMultipleInput -from processing.core.parameters import ParameterCrs -from processing.core.parameters import ParameterSelection -from processing.core.outputs import OutputVector class ExecuteSQL(QgisAlgorithm): @@ -54,7 +51,7 @@ class ExecuteSQL(QgisAlgorithm): INPUT_GEOMETRY_FIELD = 'INPUT_GEOMETRY_FIELD' INPUT_GEOMETRY_TYPE = 'INPUT_GEOMETRY_TYPE' INPUT_GEOMETRY_CRS = 'INPUT_GEOMETRY_CRS' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' def group(self): return self.tr('Vector general tools') @@ -63,19 +60,19 @@ class ExecuteSQL(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterMultipleInput(name=self.INPUT_DATASOURCES, - description=self.tr('Additional input datasources (called input1, .., inputN in the query)'), - optional=True)) + self.addParameter(QgsProcessingParameterMultipleLayers(name=self.INPUT_DATASOURCES, + description=self.tr('Additional input datasources (called input1, .., inputN in the query)'), + optional=True)) - self.addParameter(ParameterString(name=self.INPUT_QUERY, - description=self.tr('SQL query'), - multiline=True)) + self.addParameter(QgsProcessingParameterString(name=self.INPUT_QUERY, + description=self.tr('SQL query'), + multiLine=True)) - self.addParameter(ParameterString(name=self.INPUT_UID_FIELD, - description=self.tr('Unique identifier field'), optional=True)) + self.addParameter(QgsProcessingParameterString(name=self.INPUT_UID_FIELD, + description=self.tr('Unique identifier field'), optional=True)) - self.addParameter(ParameterString(name=self.INPUT_GEOMETRY_FIELD, - description=self.tr('Geometry field'), optional=True)) + self.addParameter(QgsProcessingParameterString(name=self.INPUT_GEOMETRY_FIELD, + description=self.tr('Geometry field'), optional=True)) self.geometryTypes = [ self.tr('Autodetect'), @@ -86,13 +83,13 @@ class ExecuteSQL(QgisAlgorithm): 'MultiPoint', 'MultiLineString', 'MultiPolygon'] - self.addParameter(ParameterSelection(self.INPUT_GEOMETRY_TYPE, - self.tr('Geometry type'), self.geometryTypes, optional=True)) + self.addParameter(QgsProcessingParameterEnum(self.INPUT_GEOMETRY_TYPE, + self.tr('Geometry type'), options=self.geometryTypes, optional=True)) - self.addParameter(ParameterCrs(self.INPUT_GEOMETRY_CRS, - self.tr('CRS'), optional=True)) + self.addParameter(QgsProcessingParameterCrs(self.INPUT_GEOMETRY_CRS, + self.tr('CRS'), optional=True)) - self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('SQL Output'))) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('SQL Output'))) def name(self): return 'executesql' @@ -101,24 +98,19 @@ class ExecuteSQL(QgisAlgorithm): return self.tr('Execute SQL') def processAlgorithm(self, parameters, context, feedback): - layers = self.getParameterValue(self.INPUT_DATASOURCES) - query = self.getParameterValue(self.INPUT_QUERY) - uid_field = self.getParameterValue(self.INPUT_UID_FIELD) - geometry_field = self.getParameterValue(self.INPUT_GEOMETRY_FIELD) - geometry_type = self.getParameterValue(self.INPUT_GEOMETRY_TYPE) - geometry_crs = self.getParameterValue(self.INPUT_GEOMETRY_CRS) + layers = self.parameterAsLayerList(parameters, self.INPUT_DATASOURCES, context) + query = self.parameterAsString(parameters, self.INPUT_QUERY, context) + uid_field = self.parameterAsString(parameters, self.INPUT_UID_FIELD, context) + geometry_field = self.parameterAsString(parameters, self.INPUT_GEOMETRY_FIELD, context) + geometry_type = self.parameterAsEnum(parameters, self.INPUT_GEOMETRY_TYPE, context) + geometry_crs = self.parameterAsCrs(parameters, self.INPUT_GEOMETRY_CRS, context) df = QgsVirtualLayerDefinition() - layerIdx = 1 - if layers: - for layerSource in layers.split(';'): - layer = QgsProcessingUtils.mapLayerFromString(layerSource, context) - if layer: - df.addSource('input{}'.format(layerIdx), layer.id()) - layerIdx += 1 + for layerIdx, layer in enumerate(layers): + df.addSource('input{}'.format(layerIdx + 1), layer.id()) if query == '': - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Empty SQL. Please enter valid SQL expression and try again.')) else: df.setQuery(query) @@ -133,26 +125,22 @@ class ExecuteSQL(QgisAlgorithm): df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) - if geometry_crs: - crs = QgsCoordinateReferenceSystem(geometry_crs) - if crs.isValid(): - df.setGeometrySrid(crs.postgisSrid()) + if geometry_crs.isValid(): + df.setGeometrySrid(geometry_crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): - raise GeoAlgorithmExecutionException(vLayer.dataProvider().error().message()) + raise QgsProcessingException(vLayer.dataProvider().error().message()) - writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter(vLayer.fields(), - vLayer.wkbType() if geometry_type != 1 else 1, - vLayer.crs(), context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs()) - features = QgsProcessingUtils.getFeatures(vLayer, context) + features = vLayer.getFeatures() total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 - outFeat = QgsFeature() for current, inFeat in enumerate(features): - outFeat.setAttributes(inFeat.attributes()) - if geometry_type != 1: - outFeat.setGeometry(inFeat.geometry()) - writer.addFeature(outFeat, QgsFeatureSink.FastInsert) + if feedback.isCanceled(): + break + + sink.addFeature(inFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) - del writer + return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index ce6817fbd35..a93ae2cf7f0 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -65,6 +65,7 @@ from .DropGeometry import DropGeometry from .DropMZValues import DropMZValues from .EliminateSelection import EliminateSelection from .EquivalentNumField import EquivalentNumField +from .ExecuteSQL import ExecuteSQL from .Explode import Explode from .ExportGeometryInfo import ExportGeometryInfo from .ExtendLines import ExtendLines @@ -172,7 +173,6 @@ from .ZonalStatistics import ZonalStatistics # from .GeometryConvert import GeometryConvert # from .SelectByAttributeSum import SelectByAttributeSum # from .RasterCalculator import RasterCalculator -# from .ExecuteSQL import ExecuteSQL pluginPath = os.path.normpath(os.path.join( os.path.split(os.path.dirname(__file__))[0], os.pardir)) @@ -193,7 +193,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # GeometryConvert(), # SelectByAttributeSum() # RasterCalculator(), - # ExecuteSQL(), + # # ] algs = [AddTableField(), Aggregate(), @@ -220,6 +220,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): DropMZValues(), EliminateSelection(), EquivalentNumField(), + ExecuteSQL(), Explode(), ExportGeometryInfo(), ExtendLines(), diff --git a/python/plugins/processing/tests/testdata/expected/execute_sql.gfs b/python/plugins/processing/tests/testdata/expected/execute_sql.gfs new file mode 100644 index 00000000000..30e19534a91 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/execute_sql.gfs @@ -0,0 +1,26 @@ + + + execute_sql + execute_sql + + 1 + EPSG:4326 + + 2 + 1.00000 + 5.00000 + 1.00000 + 2.00000 + + + id + id + Integer + + + id2 + id2 + Integer + + + diff --git a/python/plugins/processing/tests/testdata/expected/execute_sql.gml b/python/plugins/processing/tests/testdata/expected/execute_sql.gml new file mode 100644 index 00000000000..053470e6a70 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/execute_sql.gml @@ -0,0 +1,28 @@ + + + + + 11 + 52 + + + + + + 1,1 + 1 + 2 + + + + + 5,2 + 4 + 2 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index df4888d4de3..4d4fba9b437 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3210,3 +3210,20 @@ tests: OUTPUT: name: expected/pycalculator_points.gml type: vector + + - algorithm: qgis:executesql + name: Test execute SQL + params: + INPUT_DATASOURCES: + params: + - name: points.gml + type: vector + type: multi + INPUT_GEOMETRY_FIELD: '' + INPUT_GEOMETRY_TYPE: 0 + INPUT_QUERY: select * from input1 where id2=2 + INPUT_UID_FIELD: '' + results: + OUTPUT: + name: expected/execute_sql.gml + type: vector From 4d242c5673ebadc304476e7e5dc5c9599e722a00 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 02:07:32 +1000 Subject: [PATCH 065/364] Partial port of raster calculator to new API TODO: modeler handling --- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- .../processing/algs/qgis/RasterCalculator.py | 99 ++++++++++--------- .../tests/testdata/qgis_algorithm_tests.yaml | 58 +++++------ 3 files changed, 86 insertions(+), 76 deletions(-) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index a93ae2cf7f0..7ec40a0afb7 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -124,6 +124,7 @@ from .RandomPointsPolygons import RandomPointsPolygons from .RandomSelection import RandomSelection from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets from .Rasterize import RasterizeAlgorithm +from .RasterCalculator import RasterCalculator from .RasterLayerStatistics import RasterLayerStatistics from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable @@ -172,7 +173,6 @@ from .ZonalStatistics import ZonalStatistics # from .SpatialJoin import SpatialJoin # from .GeometryConvert import GeometryConvert # from .SelectByAttributeSum import SelectByAttributeSum -# from .RasterCalculator import RasterCalculator pluginPath = os.path.normpath(os.path.join( os.path.split(os.path.dirname(__file__))[0], os.pardir)) @@ -192,8 +192,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SpatialJoin(), # GeometryConvert(), # SelectByAttributeSum() - # RasterCalculator(), - # # ] algs = [AddTableField(), Aggregate(), @@ -278,6 +276,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): RandomPointsPolygons(), RandomSelection(), RandomSelectionWithinSubsets(), + RasterCalculator(), RasterizeAlgorithm(), RasterLayerStatistics(), RectanglesOvalsDiamondsFixed(), diff --git a/python/plugins/processing/algs/qgis/RasterCalculator.py b/python/plugins/processing/algs/qgis/RasterCalculator.py index 87a0b31e3f3..6944729e500 100644 --- a/python/plugins/processing/algs/qgis/RasterCalculator.py +++ b/python/plugins/processing/algs/qgis/RasterCalculator.py @@ -16,7 +16,7 @@ * * *************************************************************************** """ -from processing.modeler.ModelerAlgorithm import ValueFromInput, ValueFromOutput + import os __author__ = 'Victor Olaya' @@ -29,17 +29,18 @@ __revision__ = '$Format:%H$' import math from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterMultipleInput, ParameterExtent, ParameterString, ParameterRaster, ParameterNumber -from processing.core.outputs import OutputRaster -from processing.tools import dataobjects from processing.algs.gdal.GdalUtils import GdalUtils -from qgis.core import (QgsApplication, - QgsRectangle, +from qgis.core import (QgsProcessing, + QgsProcessingException, QgsProcessingUtils, - QgsProject) + QgsProcessingParameterMultipleLayers, + QgsProcessingParameterNumber, + QgsProcessingParameterExtent, + QgsProcessingParameterRasterDestination, + QgsProcessingParameterRasterLayer, + QgsProcessingOutputRasterLayer, + QgsProcessingParameterString) from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.algs.qgis.ui.RasterCalculatorWidgets import LayersListWidgetWrapper, ExpressionWidgetWrapper class RasterCalculator(QgisAlgorithm): @@ -57,25 +58,38 @@ class RasterCalculator(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterMultipleInput(self.LAYERS, - self.tr('Input layers'), - datatype=dataobjects.TYPE_RASTER, - optional=True, - metadata={'widget_wrapper': LayersListWidgetWrapper})) + layer_param = QgsProcessingParameterMultipleLayers(self.LAYERS, + self.tr('Input layers'), + layerType=QgsProcessing.TypeRaster, + optional=True) + layer_param.setMetadata({'widget_wrapper': 'processing.algs.qgis.ui.RasterCalculatorWidgets.LayersListWidgetWrapper'}) + self.addParameter(layer_param) - class ParameterRasterCalculatorExpression(ParameterString): + class ParameterRasterCalculatorExpression(QgsProcessingParameterString): + + def __init__(self, name='', description='', multiLine=False): + super().__init__(name, description, multiLine=multiLine) + self.setMetadata({ + 'widget_wrapper': 'processing.algs.qgis.ui.RasterCalculatorWidgets.ExpressionWidgetWrapper' + }) + + def type(self): + return 'raster_calc_expression' + + def clone(self): + return ParameterRasterCalculatorExpression(self.name(), self.description(), self.multiLine()) def evaluateForModeler(self, value, model): for i in list(model.inputs.values()): param = i.param - if isinstance(param, ParameterRaster): + if isinstance(param, QgsProcessingParameterRasterLayer): new = "{}@".format(os.path.basename(param.value)) old = "{}@".format(param.name()) value = value.replace(old, new) for alg in list(model.algs.values()): for out in alg.algorithm.outputs: - if isinstance(out, OutputRaster): + if isinstance(out, QgsProcessingOutputRasterLayer): if out.value: new = "{}@".format(os.path.basename(out.value)) old = "{}:{}@".format(alg.modeler_name, out.name) @@ -83,15 +97,15 @@ class RasterCalculator(QgisAlgorithm): return value self.addParameter(ParameterRasterCalculatorExpression(self.EXPRESSION, self.tr('Expression'), - multiline=True, - metadata={'widget_wrapper': ExpressionWidgetWrapper})) - self.addParameter(ParameterNumber(self.CELLSIZE, - self.tr('Cell size (use 0 or empty to set it automatically)'), - minValue=0.0, default=0.0, optional=True)) - self.addParameter(ParameterExtent(self.EXTENT, - self.tr('Output extent'), - optional=True)) - self.addOutput(OutputRaster(self.OUTPUT, self.tr('Output'))) + multiLine=True)) + self.addParameter(QgsProcessingParameterNumber(self.CELLSIZE, + self.tr('Cell size (use 0 or empty to set it automatically)'), + type=QgsProcessingParameterNumber.Double, + minValue=0.0, defaultValue=0.0, optional=True)) + self.addParameter(QgsProcessingParameterExtent(self.EXTENT, + self.tr('Output extent'), + optional=True)) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Output'))) def name(self): return 'rastercalculator' @@ -100,11 +114,10 @@ class RasterCalculator(QgisAlgorithm): return self.tr('Raster calculator') def processAlgorithm(self, parameters, context, feedback): - expression = self.getParameterValue(self.EXPRESSION) - layersValue = self.getParameterValue(self.LAYERS) + expression = self.parameterAsString(parameters, self.EXPRESSION, context) + layers = self.parameterAsLayerList(parameters, self.LAYERS, context) layersDict = {} - if layersValue: - layers = [QgsProcessingUtils.mapLayerFromString(f, context) for f in layersValue.split(";")] + if layers: layersDict = {os.path.basename(lyr.source().split(".")[0]): lyr for lyr in layers} for lyr in QgsProcessingUtils.compatibleRasterLayers(context.project()): @@ -121,26 +134,22 @@ class RasterCalculator(QgisAlgorithm): entry.bandNumber = n + 1 entries.append(entry) - output = self.getOutputValue(self.OUTPUT) - extentValue = self.getParameterValue(self.EXTENT) - if not extentValue: - extentValue = QgsProcessingUtils.combineLayerExtents(layersValue) + output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) + bbox = self.parameterAsExtent(parameters, self.EXTENT, context) + if bbox.isNull(): + bbox = QgsProcessingUtils.combineLayerExtents(layers) - if extentValue: - extent = extentValue.split(',') - bbox = QgsRectangle(float(extent[0]), float(extent[2]), - float(extent[1]), float(extent[3])) - else: + if bbox.isNull(): if layersDict: bbox = list(layersDict.values())[0].extent() for lyr in layersDict.values(): bbox.combineExtentWith(lyr.extent()) else: - raise GeoAlgorithmExecutionException(self.tr("No layers selected")) + raise QgsProcessingException(self.tr("No layers selected")) def _cellsize(layer): return (layer.extent().xMaximum() - layer.extent().xMinimum()) / layer.width() - cellsize = self.getParameterValue(self.CELLSIZE) or min([_cellsize(lyr) for lyr in layersDict.values()]) + cellsize = self.parameterAsDouble(parameters, self.CELLSIZE, context) or min([_cellsize(lyr) for lyr in layersDict.values()]) width = math.floor((bbox.xMaximum() - bbox.xMinimum()) / cellsize) height = math.floor((bbox.yMaximum() - bbox.yMinimum()) / cellsize) driverName = GdalUtils.getFormatShortNameFromFilename(output) @@ -154,14 +163,16 @@ class RasterCalculator(QgisAlgorithm): res = calc.processCalculation() if res == QgsRasterCalculator.ParserError: - raise GeoAlgorithmExecutionException(self.tr("Error parsing formula")) + raise QgsProcessingException(self.tr("Error parsing formula")) + + return {self.OUTPUT: output} def processBeforeAddingToModeler(self, algorithm, model): values = [] expression = algorithm.params[self.EXPRESSION] for i in list(model.inputs.values()): param = i.param - if isinstance(param, ParameterRaster) and "{}@".format(param.name) in expression: + if isinstance(param, QgsProcessingParameterRasterLayer) and "{}@".format(param.name) in expression: values.append(ValueFromInput(param.name())) if algorithm.name: @@ -171,7 +182,7 @@ class RasterCalculator(QgisAlgorithm): for alg in list(model.algs.values()): if alg.modeler_name not in dependent: for out in alg.algorithm.outputs: - if (isinstance(out, OutputRaster) and + if (isinstance(out, QgsProcessingOutputRasterLayer) and "{}:{}@".format(alg.modeler_name, out.name) in expression): values.append(ValueFromOutput(alg.modeler_name, out.name)) diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 4d4fba9b437..aae17f4b2c0 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2210,35 +2210,35 @@ tests: - 'Maximum value: 15:29:22' - 'NULL \(missing\) values: 1' -# - algorithm: qgis:rastercalculator -# name: Raster Calculator with cellsize -# params: -# LAYERS: -# params: -# - name: dem.tif -# type: raster -# type: multi -# CELLSIZE: 0.001 -# EXPRESSION: dem@1 -# results: -# OUTPUT: -# hash: ef97a22ee16e0e28bbdc0341449777b1527e37febc3c4339b2c057c9 -# type: rasterhash -# -# - algorithm: qgis:rastercalculator -# name: Raster Calculator -# params: -# LAYERS: -# params: -# - name: dem.tif -# type: raster -# type: multi -# CELLSIZE: 0.0 -# EXPRESSION: dem@1 * 2 -# results: -# OUTPUT: -# hash: fe6e018be13c5a3c17f3f4d0f0dc7686c628cb440b74c4642aa0c939 -# type: rasterhash + - algorithm: qgis:rastercalculator + name: Raster Calculator with cellsize + params: + LAYERS: + params: + - name: dem.tif + type: raster + type: multi + CELLSIZE: 0.001 + EXPRESSION: dem@1 + results: + OUTPUT: + hash: ef97a22ee16e0e28bbdc0341449777b1527e37febc3c4339b2c057c9 + type: rasterhash + + - algorithm: qgis:rastercalculator + name: Raster Calculator + params: + LAYERS: + params: + - name: dem.tif + type: raster + type: multi + CELLSIZE: 0.0 + EXPRESSION: dem@1 * 2 + results: + OUTPUT: + hash: fe6e018be13c5a3c17f3f4d0f0dc7686c628cb440b74c4642aa0c939 + type: rasterhash - algorithm: qgis:orientedminimumboundingbox name: Oriented minimum bounding box polys From 51f8b1a2bbf96acd32c038dcc81781df7fb7d1e5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 02:21:45 +1000 Subject: [PATCH 066/364] Port Convert Geometry Type to new API Includes partial support for Z/M types (values are lost during conversion, but at least 2d geometries are exported) TODO: full support for Z/M/curves --- .../processing/algs/qgis/GeometryConvert.py | 108 ++++++++++-------- .../algs/qgis/QGISAlgorithmProvider.py | 4 +- .../expected/convert_poly_centroid.gfs | 2 +- .../expected/convert_poly_centroid.gml | 6 + .../expected/convert_poly_multiline.gfs | 2 +- .../expected/convert_poly_multiline.gml | 6 + .../testdata/expected/convert_poly_nodes.gfs | 2 +- .../testdata/expected/convert_poly_nodes.gml | 6 + .../tests/testdata/qgis_algorithm_tests.yaml | 72 ++++++------ 9 files changed, 118 insertions(+), 90 deletions(-) diff --git a/python/plugins/processing/algs/qgis/GeometryConvert.py b/python/plugins/processing/algs/qgis/GeometryConvert.py index bbf7258a7e7..ff54bf49f17 100644 --- a/python/plugins/processing/algs/qgis/GeometryConvert.py +++ b/python/plugins/processing/algs/qgis/GeometryConvert.py @@ -29,13 +29,11 @@ from qgis.core import (QgsFeature, QgsGeometry, QgsFeatureSink, QgsWkbTypes, - QgsApplication, - QgsProcessingUtils) + QgsProcessingException, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterEnum, + QgsProcessingParameterFeatureSink) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterSelection -from processing.core.outputs import OutputVector class GeometryConvert(QgisAlgorithm): @@ -56,12 +54,13 @@ class GeometryConvert(QgisAlgorithm): self.tr('Multilinestrings'), self.tr('Polygons')] - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'))) - self.addParameter(ParameterSelection(self.TYPE, - self.tr('New geometry type'), self.types)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterEnum(self.TYPE, + self.tr('New geometry type'), options=self.types)) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Converted'))) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, + self.tr('Converted'))) def name(self): return 'convertgeometrytype' @@ -70,8 +69,8 @@ class GeometryConvert(QgisAlgorithm): return self.tr('Convert geometry type') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - index = self.getParameterValue(self.TYPE) + source = self.parameterAsSource(parameters, self.INPUT, context) + index = self.parameterAsEnum(parameters, self.TYPE, context) splitNodes = False if index == 0: @@ -88,56 +87,64 @@ class GeometryConvert(QgisAlgorithm): else: newType = QgsWkbTypes.Point - writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), newType, layer.crs(), context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + source.fields(), newType, source.sourceCrs()) - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / layer.featureCount() if layer.featureCount() else 0 + features = source.getFeatures() + total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): + if feedback.isCanceled(): + break + + if not f.hasGeometry(): + sink.addFeature(f, QgsFeatureSink.FastInsert) + geom = f.geometry() geomType = geom.wkbType() - if geomType in [QgsWkbTypes.Point, QgsWkbTypes.Point25D]: + if QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PointGeometry and not QgsWkbTypes.isMultiType(geomType): if newType == QgsWkbTypes.Point: - writer.addFeature(f, QgsFeatureSink.FastInsert) + sink.addFeature(f, QgsFeatureSink.FastInsert) else: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif geomType in [QgsWkbTypes.MultiPoint, QgsWkbTypes.MultiPoint25D]: + elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PointGeometry and QgsWkbTypes.isMultiType(geomType): if newType == QgsWkbTypes.Point and splitNodes: points = geom.asMultiPoint() for p in points: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) else: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif geomType in [QgsWkbTypes.LineString, QgsWkbTypes.LineString25D]: + elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.LineGeometry and not QgsWkbTypes.isMultiType(geomType): if newType == QgsWkbTypes.Point and splitNodes: points = geom.asPolyline() for p in points: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.LineString: - writer.addFeature(f, QgsFeatureSink.FastInsert) + sink.addFeature(f, QgsFeatureSink.FastInsert) else: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif geomType in [QgsWkbTypes.MultiLineString, QgsWkbTypes.MultiLineString25D]: + elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.LineGeometry and QgsWkbTypes.isMultiType( + geomType): if newType == QgsWkbTypes.Point and splitNodes: lines = geom.asMultiPolyline() for line in lines: @@ -145,25 +152,26 @@ class GeometryConvert(QgisAlgorithm): feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.LineString: lines = geom.asMultiPolyline() for line in lines: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPolyline(line)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.MultiLineString: - writer.addFeature(f, QgsFeatureSink.FastInsert) + sink.addFeature(f, QgsFeatureSink.FastInsert) else: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif geomType in [QgsWkbTypes.Polygon, QgsWkbTypes.Polygon25D]: + elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PolygonGeometry and not QgsWkbTypes.isMultiType( + geomType): if newType == QgsWkbTypes.Point and splitNodes: rings = geom.asPolygon() for ring in rings: @@ -171,24 +179,26 @@ class GeometryConvert(QgisAlgorithm): feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.MultiLineString: rings = geom.asPolygon() feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromMultiPolyline(rings)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Polygon: - writer.addFeature(f, QgsFeatureSink.FastInsert) + sink.addFeature(f, QgsFeatureSink.FastInsert) else: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif geomType in [QgsWkbTypes.MultiPolygon, QgsWkbTypes.MultiPolygon25D]: + elif QgsWkbTypes.geometryType( + geomType) == QgsWkbTypes.PolygonGeometry and QgsWkbTypes.isMultiType( + geomType): if newType == QgsWkbTypes.Point and splitNodes: polygons = geom.asMultiPolygon() for polygon in polygons: @@ -197,32 +207,32 @@ class GeometryConvert(QgisAlgorithm): feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.LineString: polygons = geom.asMultiPolygon() - for polygons in polygons: + for polygon in polygons: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPolyline(polygon)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Polygon: polygons = geom.asMultiPolygon() for polygon in polygons: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPolygon(polygon)) - writer.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) elif newType in [QgsWkbTypes.MultiLineString, QgsWkbTypes.MultiPolygon]: - writer.addFeature(f, QgsFeatureSink.FastInsert) + sink.addFeature(f, QgsFeatureSink.FastInsert) else: - raise GeoAlgorithmExecutionException( + raise QgsProcessingException( self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) feedback.setProgress(int(current * total)) - del writer + return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 7ec40a0afb7..0f2621d936b 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -78,6 +78,7 @@ from .FieldsMapper import FieldsMapper from .FindProjection import FindProjection from .FixedDistanceBuffer import FixedDistanceBuffer from .FixGeometry import FixGeometry +from .GeometryConvert import GeometryConvert from .GeometryByExpression import GeometryByExpression from .Gridify import Gridify from .GridLine import GridLine @@ -171,7 +172,6 @@ from .ZonalStatistics import ZonalStatistics # from .ExtractByLocation import ExtractByLocation # from .SelectByLocation import SelectByLocation # from .SpatialJoin import SpatialJoin -# from .GeometryConvert import GeometryConvert # from .SelectByAttributeSum import SelectByAttributeSum pluginPath = os.path.normpath(os.path.join( @@ -190,7 +190,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SelectByLocation(), # ExtractByLocation(), # SpatialJoin(), - # GeometryConvert(), # SelectByAttributeSum() # ] algs = [AddTableField(), @@ -232,6 +231,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): FixedDistanceBuffer(), FixGeometry(), GeometryByExpression(), + GeometryConvert(), Gridify(), GridLine(), GridPolygon(), diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gfs b/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gfs index 0e86d87d8dc..b8342ff5b41 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gfs +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gfs @@ -6,7 +6,7 @@ 1 EPSG:4326 - 5 + 6 0.65385 8.00000 -1.00000 diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gml b/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gml index e6e316c9881..8bbbe087f94 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gml +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gml @@ -41,6 +41,12 @@ 0 + + + 120 + -100291.43213 + + 4.08045977011494,-0.218390804597701 diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gfs b/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gfs index 5f351afd396..ead068d2911 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gfs +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gfs @@ -6,7 +6,7 @@ 5 EPSG:4326 - 5 + 6 -1.00000 10.00000 -3.00000 diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gml b/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gml index 644fda625a2..91dcf63b231 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gml +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gml @@ -41,6 +41,12 @@ 0 + + + 120 + -100291.43213 + + 3,2 6,1 6,-3 2,-1 2,2 3,2 diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs index 0afc6f5fb20..f0bb271f722 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs @@ -6,7 +6,7 @@ 1 EPSG:4326 - 32 + 33 -1.00000 10.00000 -3.00000 diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml index 98c0a47817b..5612c0f0c94 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml @@ -204,6 +204,12 @@ 0 + + + 120 + -100291.43213 + + 3,2 diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index aae17f4b2c0..aed4606b561 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2957,42 +2957,42 @@ tests: name: expected/create_grid_lines.gml type: vector -# - algorithm: qgis:convertgeometrytype -# name: polygon to centroid -# params: -# INPUT: -# name: polys.gml -# type: vector -# TYPE: '0' -# results: -# OUTPUT: -# name: expected/convert_poly_centroid.gml -# type: vector -# -# - algorithm: qgis:convertgeometrytype -# name: polygon to multilinestring -# params: -# INPUT: -# name: polys.gml -# type: vector -# TYPE: '3' -# results: -# OUTPUT: -# name: expected/convert_poly_multiline.gml -# type: vector -# -# - algorithm: qgis:convertgeometrytype -# name: polygon to nodes -# params: -# INPUT: -# name: polys.gml -# type: vector -# TYPE: '1' -# results: -# OUTPUT: -# name: expected/convert_poly_nodes.gml -# type: vector -# + - algorithm: qgis:convertgeometrytype + name: polygon to centroid + params: + INPUT: + name: polys.gml + type: vector + TYPE: '0' + results: + OUTPUT: + name: expected/convert_poly_centroid.gml + type: vector + + - algorithm: qgis:convertgeometrytype + name: polygon to multilinestring + params: + INPUT: + name: polys.gml + type: vector + TYPE: '3' + results: + OUTPUT: + name: expected/convert_poly_multiline.gml + type: vector + + - algorithm: qgis:convertgeometrytype + name: polygon to nodes + params: + INPUT: + name: polys.gml + type: vector + TYPE: '1' + results: + OUTPUT: + name: expected/convert_poly_nodes.gml + type: vector + # # - algorithm: qgis:extractbylocation # # name: polygon intersecting points # # params: From 8da29c06b4859f1b2e464c9c81794363abc18356 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 17:35:37 +1000 Subject: [PATCH 067/364] Fix test reference file --- .../tests/testdata/expected/field_calculator_points.gfs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs b/python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs index fc35e4b96c0..a1baefadabe 100644 --- a/python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs +++ b/python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs @@ -23,8 +23,8 @@ Integer - calc - calc + test + test Integer
From bcc662722b233476b2907f168608b86dbcadc2da Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 20 Aug 2017 18:20:27 +1000 Subject: [PATCH 068/364] Upgrade Convert Geometries algorithm to maintain Z/M, curves were possible --- .../processing/algs/qgis/GeometryConvert.py | 266 +++++++++--------- .../testdata/expected/convert_poly_nodes.gfs | 6 +- .../testdata/expected/convert_poly_nodes.gml | 213 +------------- 3 files changed, 139 insertions(+), 346 deletions(-) diff --git a/python/plugins/processing/algs/qgis/GeometryConvert.py b/python/plugins/processing/algs/qgis/GeometryConvert.py index ff54bf49f17..5bf3e9d4204 100644 --- a/python/plugins/processing/algs/qgis/GeometryConvert.py +++ b/python/plugins/processing/algs/qgis/GeometryConvert.py @@ -27,6 +27,10 @@ __revision__ = '$Format:%H$' from qgis.core import (QgsFeature, QgsGeometry, + QgsMultiPointV2, + QgsMultiLineString, + QgsLineString, + QgsPolygonV2, QgsFeatureSink, QgsWkbTypes, QgsProcessingException, @@ -72,20 +76,32 @@ class GeometryConvert(QgisAlgorithm): source = self.parameterAsSource(parameters, self.INPUT, context) index = self.parameterAsEnum(parameters, self.TYPE, context) - splitNodes = False if index == 0: newType = QgsWkbTypes.Point elif index == 1: newType = QgsWkbTypes.Point - splitNodes = True + if QgsWkbTypes.hasM(source.wkbType()): + newType = QgsWkbTypes.addM(newType) + if QgsWkbTypes.hasZ(source.wkbType()): + newType = QgsWkbTypes.addZ(newType) elif index == 2: newType = QgsWkbTypes.LineString + if QgsWkbTypes.hasM(source.wkbType()): + newType = QgsWkbTypes.addM(newType) + if QgsWkbTypes.hasZ(source.wkbType()): + newType = QgsWkbTypes.addZ(newType) elif index == 3: newType = QgsWkbTypes.MultiLineString - elif index == 4: - newType = QgsWkbTypes.Polygon + if QgsWkbTypes.hasM(source.wkbType()): + newType = QgsWkbTypes.addM(newType) + if QgsWkbTypes.hasZ(source.wkbType()): + newType = QgsWkbTypes.addZ(newType) else: - newType = QgsWkbTypes.Point + newType = QgsWkbTypes.Polygon + if QgsWkbTypes.hasM(source.wkbType()): + newType = QgsWkbTypes.addM(newType) + if QgsWkbTypes.hasZ(source.wkbType()): + newType = QgsWkbTypes.addZ(newType) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), newType, source.sourceCrs()) @@ -99,140 +115,120 @@ class GeometryConvert(QgisAlgorithm): if not f.hasGeometry(): sink.addFeature(f, QgsFeatureSink.FastInsert) - - geom = f.geometry() - geomType = geom.wkbType() - - if QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PointGeometry and not QgsWkbTypes.isMultiType(geomType): - if newType == QgsWkbTypes.Point: - sink.addFeature(f, QgsFeatureSink.FastInsert) - else: - raise QgsProcessingException( - self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PointGeometry and QgsWkbTypes.isMultiType(geomType): - if newType == QgsWkbTypes.Point and splitNodes: - points = geom.asMultiPoint() - for p in points: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPoint(p)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.Point: + else: + for p in self.convertGeometry(f.geometry(), index): feat = QgsFeature() feat.setAttributes(f.attributes()) - feat.setGeometry(geom.centroid()) + feat.setGeometry(p) sink.addFeature(feat, QgsFeatureSink.FastInsert) - else: - raise QgsProcessingException( - self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.LineGeometry and not QgsWkbTypes.isMultiType(geomType): - if newType == QgsWkbTypes.Point and splitNodes: - points = geom.asPolyline() - for p in points: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPoint(p)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.Point: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(geom.centroid()) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.LineString: - sink.addFeature(f, QgsFeatureSink.FastInsert) - else: - raise QgsProcessingException( - self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.LineGeometry and QgsWkbTypes.isMultiType( - geomType): - if newType == QgsWkbTypes.Point and splitNodes: - lines = geom.asMultiPolyline() - for line in lines: - for p in line: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPoint(p)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.Point: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(geom.centroid()) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.LineString: - lines = geom.asMultiPolyline() - for line in lines: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPolyline(line)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.MultiLineString: - sink.addFeature(f, QgsFeatureSink.FastInsert) - else: - raise QgsProcessingException( - self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PolygonGeometry and not QgsWkbTypes.isMultiType( - geomType): - if newType == QgsWkbTypes.Point and splitNodes: - rings = geom.asPolygon() - for ring in rings: - for p in ring: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPoint(p)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.Point: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(geom.centroid()) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.MultiLineString: - rings = geom.asPolygon() - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromMultiPolyline(rings)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.Polygon: - sink.addFeature(f, QgsFeatureSink.FastInsert) - else: - raise QgsProcessingException( - self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) - elif QgsWkbTypes.geometryType( - geomType) == QgsWkbTypes.PolygonGeometry and QgsWkbTypes.isMultiType( - geomType): - if newType == QgsWkbTypes.Point and splitNodes: - polygons = geom.asMultiPolygon() - for polygon in polygons: - for line in polygon: - for p in line: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPoint(p)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.Point: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(geom.centroid()) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.LineString: - polygons = geom.asMultiPolygon() - for polygon in polygons: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPolyline(polygon)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType == QgsWkbTypes.Polygon: - polygons = geom.asMultiPolygon() - for polygon in polygons: - feat = QgsFeature() - feat.setAttributes(f.attributes()) - feat.setGeometry(QgsGeometry.fromPolygon(polygon)) - sink.addFeature(feat, QgsFeatureSink.FastInsert) - elif newType in [QgsWkbTypes.MultiLineString, QgsWkbTypes.MultiPolygon]: - sink.addFeature(f, QgsFeatureSink.FastInsert) - else: - raise QgsProcessingException( - self.tr('Cannot convert from {0} to {1}').format(geomType, newType)) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id} + + def convertGeometry(self, geom, target_type): + # returns an array of output geometries for the input geometry + if target_type == 0: + #centroid + return self.convertToCentroid(geom) + elif target_type == 1: + #nodes + return self.convertToNodes(geom) + elif target_type == 2: + #linestrings + return self.convertToLineStrings(geom) + elif target_type == 3: + #multilinestrings + return self.convertToMultiLineStrings(geom) + elif target_type == 4: + #polygon + return self.convertToPolygon(geom) + + def convertToCentroid(self, geom): + return [geom.centroid()] + + def convertToNodes(self, geom): + mp = QgsMultiPointV2() + # TODO: mega inefficient - needs rework when geometry iterators land + # (but at least it doesn't lose Z/M values) + for g in geom.geometry().coordinateSequence(): + for r in g: + for p in r: + mp.addGeometry(p) + return [QgsGeometry(mp)] + + def convertToLineStrings(self, geom): + if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry: + raise QgsProcessingException( + self.tr('Cannot convert from {0} to LineStrings').format(QgsWkbTypes.displayString(geom.wkbType()))) + elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry: + if QgsWkbTypes.isMultiType(geom.wkbType()): + return geom.asGeometryCollection() + else: + #line to line + return [geom] + else: + # polygons to lines + # we just use the boundary here - that consists of all rings in the (multi)polygon + boundary = QgsGeometry(geom.geometry().boundary()) + # boundary will be multipart + return boundary.asGeometryCollection() + + def convertToMultiLineStrings(self, geom): + if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry: + raise QgsProcessingException( + self.tr('Cannot convert from {0} to MultiLineStrings').format(QgsWkbTypes.displayString(geom.wkbType()))) + elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry: + if QgsWkbTypes.isMultiType(geom.wkbType()): + return [geom] + else: + # line to multiLine + ml = QgsMultiLineString() + ml.addGeometry(geom.geometry().clone()) + return [QgsGeometry(ml)] + else: + # polygons to multilinestring + # we just use the boundary here - that consists of all rings in the (multi)polygon + return [QgsGeometry(geom.geometry().boundary())] + + def convertToPolygon(self, geom): + if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry and geom.geometry().nCoordinates() < 3: + raise QgsProcessingException( + self.tr('Cannot convert from Point to Polygon').format(QgsWkbTypes.displayString(geom.wkbType()))) + elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry: + # multipoint with at least 3 points + # TODO: mega inefficient - needs rework when geometry iterators land + # (but at least it doesn't lose Z/M values) + points = [] + for g in geom.geometry().coordinateSequence(): + for r in g: + for p in r: + points.append(p) + linestring = QgsLineString(points) + linestring.close() + p = QgsPolygonV2() + p.setExteriorRing(linestring) + return [QgsGeometry(p)] + elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry: + if QgsWkbTypes.isMultiType(geom): + parts = [] + for i in range(geom.geometry().numGeometries()): + p = QgsPolygonV2() + linestring = geom.geometry().geometryN(i).clone() + linestring.close() + p.setExteriorRing(linestring) + parts.append(QgsGeometry(p)) + return QgsGeometry.collectGeometry(parts) + else: + # linestring to polygon + p = QgsPolygonV2() + linestring = geom.geometry().clone() + linestring.close() + p.setExteriorRing(linestring) + return [QgsGeometry(p)] + else: + #polygon + if QgsWkbTypes.isMultiType(geom): + return geom.asGeometryCollection() + else: + return [geom] diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs index f0bb271f722..089512fb3c2 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs @@ -2,11 +2,11 @@ convert_poly_nodes convert_poly_nodes - - 1 + + 4 EPSG:4326 - 33 + 6 -1.00000 10.00000 -3.00000 diff --git a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml index 5612c0f0c94..c092423a400 100644 --- a/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml +++ b/python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml @@ -13,55 +13,7 @@ - -1,-1 - aaaaa - 33 - 44.123456 - - - - - -1,3 - aaaaa - 33 - 44.123456 - - - - - 3,3 - aaaaa - 33 - 44.123456 - - - - - 3,2 - aaaaa - 33 - 44.123456 - - - - - 2,2 - aaaaa - 33 - 44.123456 - - - - - 2,-1 - aaaaa - 33 - 44.123456 - - - - - -1,-1 + -1,-1-1,33,33,22,22,-1-1,-1 aaaaa 33 44.123456 @@ -69,31 +21,7 @@ - 5,5 - Aaaaa - -33 - 0 - - - - - 6,4 - Aaaaa - -33 - 0 - - - - - 4,4 - Aaaaa - -33 - 0 - - - - - 5,5 + 5,56,44,45,5 Aaaaa -33 0 @@ -101,105 +29,14 @@ - 2,5 - bbaaa - 0.123 - - - - - 2,6 - bbaaa - 0.123 - - - - - 3,6 - bbaaa - 0.123 - - - - - 3,5 - bbaaa - 0.123 - - - - - 2,5 + 2,52,63,63,52,5 bbaaa 0.123 - 6,1 - ASDF - 0 - - - - - 10,1 - ASDF - 0 - - - - - 10,-3 - ASDF - 0 - - - - - 6,-3 - ASDF - 0 - - - - - 6,1 - ASDF - 0 - - - - - 7,0 - ASDF - 0 - - - - - 7,-2 - ASDF - 0 - - - - - 9,-2 - ASDF - 0 - - - - - 9,0 - ASDF - 0 - - - - - 7,0 + 6,110,110,-36,-36,17,07,-29,-29,07,0 ASDF 0 @@ -212,47 +49,7 @@ - 3,2 - elim - 2 - 3.33 - - - - - 6,1 - elim - 2 - 3.33 - - - - - 6,-3 - elim - 2 - 3.33 - - - - - 2,-1 - elim - 2 - 3.33 - - - - - 2,2 - elim - 2 - 3.33 - - - - - 3,2 + 3,26,16,-32,-12,23,2 elim 2 3.33 From aa6973780708236760e4ddef72b86d57a12fd142 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sun, 20 Aug 2017 16:33:36 +0200 Subject: [PATCH 069/364] Prevent import layer over itself --- src/providers/ogr/qgsgeopackagedataitems.cpp | 129 ++++++++++--------- 1 file changed, 69 insertions(+), 60 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 067fed5ae83..5d43719cbc0 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -321,7 +321,6 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct if ( !QgsMimeDataUtils::isUriList( data ) ) return false; - // TODO: probably should show a GUI with settings etc QString uri; QStringList importResults; @@ -332,71 +331,81 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct { if ( u.layerType == QStringLiteral( "vector" ) ) { - // open the source layer - bool owner; - QString error; - QgsVectorLayer *srcLayer = u.vectorLayer( owner, error ); - if ( !srcLayer ) + // Check that we are not copying over self + if ( u.uri.startsWith( mPath ) ) { - importResults.append( tr( "%1: %2" ).arg( u.name ).arg( error ) ); + importResults.append( tr( "You cannot import layer %1 over itself!" ).arg( u.name ) ); 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 ) - { - - QVariantMap options; - 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, 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; + // 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 ) + { + + QVariantMap options; + 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, 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 From a6b0c44ec440f42be842daae523e8a3b505f3cba Mon Sep 17 00:00:00 2001 From: Sandro Mani Date: Wed, 9 Aug 2017 14:37:04 +0200 Subject: [PATCH 070/364] Ensure canvas has focus if nothing else has Forward port from Sourcepole's fork --- src/app/qgisapp.cpp | 10 ++++++++++ src/app/qgisapp.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 08ad5c74ded..a7c94ad2464 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -1234,6 +1234,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh } #endif + connect( qApp, SIGNAL( focusChanged( QWidget*, QWidget* ) ), this, SLOT( onFocusChanged( QWidget*, QWidget* ) ) ); } // QgisApp ctor QgisApp::QgisApp() @@ -8791,6 +8792,15 @@ void QgisApp::userRotation() mMapCanvas->refresh(); } +void QgisApp::onFocusChanged( QWidget* /*old*/, QWidget* now ) +{ + // If nothing has focus even though the window is active, ensure map canvas receives it + if ( !now && isActiveWindow() ) + { + mapCanvas()->setFocus(); + } +} + // toggle overview status void QgisApp::isInOverview() { diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index a39a4f60b77..d0e62e730e3 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -1510,6 +1510,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow void updateCrsStatusBar(); + void onFocusChanged( QWidget* old, QWidget* now ); + signals: /** From a2c9710a2da6a0e4b829aba8794ff804c011dcec Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 22 Aug 2017 05:18:48 +1000 Subject: [PATCH 071/364] Followup canvas focus commit --- src/app/qgisapp.cpp | 9 +++++---- src/app/qgisapp.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index a7c94ad2464..7fbfeff0cd7 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -1234,7 +1234,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh } #endif - connect( qApp, SIGNAL( focusChanged( QWidget*, QWidget* ) ), this, SLOT( onFocusChanged( QWidget*, QWidget* ) ) ); + connect( qApp, &QApplication::focusChanged, this, &QgisApp::onFocusChanged ); } // QgisApp ctor QgisApp::QgisApp() @@ -8792,10 +8792,11 @@ void QgisApp::userRotation() mMapCanvas->refresh(); } -void QgisApp::onFocusChanged( QWidget* /*old*/, QWidget* now ) +void QgisApp::onFocusChanged( QWidget *oldWidget, QWidget *newWidget ) { - // If nothing has focus even though the window is active, ensure map canvas receives it - if ( !now && isActiveWindow() ) + Q_UNUSED( oldWidget ); + // If nothing has focus even though this window is active, ensure map canvas receives it + if ( !newWidget && isActiveWindow() ) { mapCanvas()->setFocus(); } diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index d0e62e730e3..1b67bf3695a 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -1510,7 +1510,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow void updateCrsStatusBar(); - void onFocusChanged( QWidget* old, QWidget* now ); + void onFocusChanged( QWidget *oldWidget, QWidget *newWidget ); signals: From 4d3c7e558b1bc955271b5291c67d5d34e5f1d9f5 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 21 Aug 2017 22:43:16 +0200 Subject: [PATCH 072/364] Geopackage: fix crash when deleting raster layers --- src/providers/ogr/qgsgeopackagedataitems.cpp | 27 ++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 067fed5ae83..b0047cdd632 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -577,7 +577,7 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) else { // Remove table - char *errmsg = NULL; + char *errmsg = nullptr; char *sql = sqlite3_mprintf( "DROP table %w;" "DELETE FROM gpkg_contents WHERE table_name = '%q';" @@ -596,20 +596,21 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) ); sqlite3_free( sql ); // Remove from optional tables, may silently fail - for ( const auto tableName : QStringList() - << QStringLiteral( "gpkg_extensions" ) - << QStringLiteral( "gpkg_metadata_reference" ) ) + QStringList optionalTables; + optionalTables << QStringLiteral( "gpkg_extensions" ) + << QStringLiteral( "gpkg_metadata_reference" ); + for ( const auto tableName : optionalTables ) { char *sql = sqlite3_mprintf( "DELETE FROM table %w WHERE table_name = '%q", tableName.toUtf8().constData(), layerName.toUtf8().constData() ); - status = sqlite3_exec( - handle, /* An open database */ - sql, /* SQL to be evaluated */ - NULL, /* Callback function */ - NULL, /* 1st argument to callback */ - NULL /* Error msg written here */ - ); + sqlite3_exec( + handle, /* An open database */ + sql, /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + NULL /* Error msg written here */ + ); sqlite3_free( sql ); } if ( status == SQLITE_OK ) @@ -618,9 +619,9 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) } else { - errCause = tr( "There was an error deleting the layer: %1" ).arg( errmsg ); - sqlite3_free( errmsg ); + errCause = tr( "There was an error deleting the layer: %1" ).arg( QString::fromUtf8( errmsg ) ); } + sqlite3_free( errmsg ); } sqlite3_close( handle ); } From b8d2a17476502af5689a6675f48efec9268409d3 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 21 Aug 2017 12:30:53 +0200 Subject: [PATCH 073/364] Port symbols test from ebf20a5 --- .../core/testqgsinvertedpolygonrenderer.cpp | 12 + tests/testdata/inverted_polys_rule.qml | 297 ++++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 tests/testdata/inverted_polys_rule.qml diff --git a/tests/src/core/testqgsinvertedpolygonrenderer.cpp b/tests/src/core/testqgsinvertedpolygonrenderer.cpp index 7d188fd41ef..5f96022a76d 100644 --- a/tests/src/core/testqgsinvertedpolygonrenderer.cpp +++ b/tests/src/core/testqgsinvertedpolygonrenderer.cpp @@ -27,6 +27,8 @@ #include #include #include +#include "qgsrenderer.h" + //qgis test includes #include "qgsmultirenderchecker.h" @@ -48,6 +50,7 @@ class TestQgsInvertedPolygon : public QObject void singleSubRenderer(); void graduatedSubRenderer(); + void checkSymbolItem(); void preprocess(); void projectionTest(); #if defined(GDAL_VERSION_NUM) && GDAL_VERSION_MAJOR >= 2 @@ -128,6 +131,15 @@ void TestQgsInvertedPolygon::graduatedSubRenderer() QVERIFY( imageCheck( "inverted_polys_graduated" ) ); } +void TestQgsInvertedPolygon::checkSymbolItem() +{ + QVERIFY( setQml( mpPolysLayer, "inverted_polys_rule.qml" ) ); + QString firstRuleKey = mpPolysLayer->renderer()->legendSymbolItems().first().ruleKey(); + QVERIFY( mpPolysLayer->renderer()->legendSymbolItemChecked( firstRuleKey ) ); + mpPolysLayer->renderer()->checkLegendSymbolItem( firstRuleKey, false ); + QVERIFY( !mpPolysLayer->renderer()->legendSymbolItemChecked( firstRuleKey ) ); +} + void TestQgsInvertedPolygon::preprocess() { // FIXME will have to find some overlapping polygons diff --git a/tests/testdata/inverted_polys_rule.qml b/tests/testdata/inverted_polys_rule.qml new file mode 100644 index 00000000000..7c91a92b793 --- /dev/null +++ b/tests/testdata/inverted_polys_rule.qml @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + 2 + From 3d201e7c65ec6c2eebff9b045138a83021ab4844 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Mon, 21 Aug 2017 16:45:16 +0200 Subject: [PATCH 074/364] Delete obsolete context help already ported to user manual and linked to help button --- resources/context_help/PythonConsole | 161 ------------------ .../context_help/QgsGenericProjectionSelector | 18 -- resources/context_help/QgsNewOgrConnection | 1 - .../context_help/QgsStyleV2ManagerDialog | 1 - 4 files changed, 181 deletions(-) delete mode 100644 resources/context_help/PythonConsole delete mode 100644 resources/context_help/QgsGenericProjectionSelector delete mode 100644 resources/context_help/QgsNewOgrConnection delete mode 100644 resources/context_help/QgsStyleV2ManagerDialog diff --git a/resources/context_help/PythonConsole b/resources/context_help/PythonConsole deleted file mode 100644 index 1554b9d227c..00000000000 --- a/resources/context_help/PythonConsole +++ /dev/null @@ -1,161 +0,0 @@ -

Python Console for QGIS

-
Console
-Editor
-Settings

- - - - -
-The QGIS Python Console is an interactive shell for the python command executions. -It also has a python file editor that allows you to edit and save your python scripts. -Both console and editor are based on PyQScintilla2 package.
-The console is split in two main panes, top and bottom one -resizable by using the horizontal splitter. Output area pane is a widget read-only which -shows the commands output. You can drag and drop or copy and paste text into input area and -execute code snippets from the output pane by selecting some text and clicking on the - command from the context menu. -No matter if selected text contains the interpreter prompt (>>>, ...). Input area pane is the interactive -python shell for input commands.
To access to the python file editor use the - button -from the toolbar. The editor allows editing and saving python files and it offers basic functionality -for managing your code (comment and -uncomment code, check syntax, share the code via codepad.org and much more). -

-
- -

Console

-
-Main features: - - - - - -
-
    -
  • Code completion, highlighting syntax and calltips for the following APIs: -
      -
    1. Python
    2. -
    3. PyQGIS
    4. -
    5. PyQt4
    6. -
    7. QScintilla2
    8. -
    9. osgeo-gdal-ogr
    10. -
    -
  • -
    -
  • to view the auto-completion list.
  • -
    -
  • to view the command history list.
  • -
    -
  • Execute code snippets with the command from output pane.
  • -
    -
  • Open QGIS API documentation by typing .
  • -
    -
  • Open PyQGIS Cookbook by typing .
  • -
    -
  • Save and clear the command history accessing from context menu of input pane. -The history will be saved into the file ~/.qgis3/console_history.txt
  • -
    -
-
-Toolbar: -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Clear python console
Import Processing class
Import PyQt4.QtCore class
Tool to import PyQt4.QtGui class
Run command (like Enter key pressed)
Settings
Help
- -

Editor

-
-Main features: - - - - - -
- - - -
    -
  • Code completion, highlighting syntax and calltips for the following APIs: -
      -
    1. Python
    2. -
    3. PyQGIS
    4. -
    5. PyQt4
    6. -
    7. QScintilla2
    8. -
    9. osgeo-gdal-ogr
    10. -
    -
  • -
    -
  • to view the auto-completion list.
  • -
    -
  • Sharing code snippets via codepad.org.
  • -
    -
  • Syntax check.
  • -
    -
  • Object inspector: a class and function browser.
  • -
    -
  • Go to an object definition with a mouse click. (from Object inspector)
  • -
    -
  • Execute code snippets with the command.
  • -
    -
  • Execute the whole script with the command -(this creates a byte-compiled file with the extension .pyc)
  • -
    -
-
- -

Settings

-
-Further settings for python console: -
    -
  • If checked the code completion is enabled. You can get autocompletion -from current document, from installed APIs and both from APIs and current document
  • -
    -
  • Sets the threshold to display the autocompletion list (in chars typed)
  • -
    -
  • If checked enables the autoclosing for bracket
  • -
    -
  • Allows you to save automatically the -script to be executed in order to avoid to save it after any modification. -This action will store a temporary file into the temporary system directory -that will be automatically deleted after running.
  • -
    -
  • You can choose whether use the preload APIs file or load some APIs files saved on your system.
  • -
    -
  • If checked the *.pap file will be used for code completion. To generate a prepared APIs file you have to load least an *.api file and then compile it by clicking on button.
  • -
-

-Note: To save the state of console's widgets you have to close the Python Console -from the close button. This allows you to save the geometry to be restored to the next start. -

diff --git a/resources/context_help/QgsGenericProjectionSelector b/resources/context_help/QgsGenericProjectionSelector deleted file mode 100644 index 6a01331bfa8..00000000000 --- a/resources/context_help/QgsGenericProjectionSelector +++ /dev/null @@ -1,18 +0,0 @@ -

Coordinate Reference System Selector

- -

- The Coordinate Reference System Selector allows you to set the projection for - the current layer. -

-

- You can find a coordinate system by entering its name, EPSG code or the QGIS ID - into the text field. The list of recently used - coordinate systems are available at the top for quick access. -

-

- When operating across layers, for example, computing intersections between - two layers, it is important that both layers have the same CRS. To change the - projection of an existing layer, it is insufficient to simply change the CRS - in that layer's properties. Instead you must save the layer as a new layer, - and choose the desired CRS for the new layer. -

diff --git a/resources/context_help/QgsNewOgrConnection b/resources/context_help/QgsNewOgrConnection deleted file mode 100644 index 083af101086..00000000000 --- a/resources/context_help/QgsNewOgrConnection +++ /dev/null @@ -1 +0,0 @@ -

New OGR Database Connection Dialog

diff --git a/resources/context_help/QgsStyleV2ManagerDialog b/resources/context_help/QgsStyleV2ManagerDialog deleted file mode 100644 index 97fc94e954d..00000000000 --- a/resources/context_help/QgsStyleV2ManagerDialog +++ /dev/null @@ -1 +0,0 @@ -

Style Manager

From 6e3aba342cdbeb2e36c53593cb11859387ba6e12 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Mon, 21 Aug 2017 17:13:04 +0200 Subject: [PATCH 075/364] Fix Python console web link --- python/console/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/console/console.py b/python/console/console.py index b5ad8e85414..eaf8318422e 100644 --- a/python/console/console.py +++ b/python/console/console.py @@ -59,7 +59,7 @@ def show_console(): # Shows help on first launch of the console settings = QgsSettings() if settings.value('pythonConsole/contextHelpOnFirstLaunch', True, type=bool): - QgsHelp.openHelp("../pyqgis_developer_cookbook/intro.html#python-console") + QgsHelp.openHelp("plugins/python_console.html") settings.setValue('pythonConsole/contextHelpOnFirstLaunch', False) return _console From 6fd3c1090a1e62f50c4790fbe091712963064319 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Mon, 21 Aug 2017 17:36:54 +0200 Subject: [PATCH 076/364] Fix wording Fully write the label because there's enough space + alignment --- .../plugins/versioning/DlgVersioning.ui | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/DlgVersioning.ui b/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/DlgVersioning.ui index 627b89a037a..cd79c0a3f11 100644 --- a/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/DlgVersioning.ui +++ b/python/plugins/db_manager/db_plugins/postgis/plugins/versioning/DlgVersioning.ui @@ -11,7 +11,7 @@ - Add versioning support to a table + Add Versioning Support to a Table @@ -19,60 +19,50 @@ - Table is expected to be empty, with a primary key. + Table should be empty, with a primary key + + + Schema - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - + + + 0 + 0 + + Table - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 40 - 48 - - - - - create a view with current content (<TABLE>_current) + Create a view with current content (<TABLE>_current) true @@ -88,10 +78,10 @@ - Prim. key + Primary key - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -108,7 +98,7 @@ Start time - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -125,7 +115,7 @@ End time - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -157,7 +147,7 @@ - SQL to be executed: + SQL to be executed From 23102e3c8ec3521b6e6aedfa2f1a92bf20dce6c8 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 22 Aug 2017 08:38:25 +0200 Subject: [PATCH 077/364] [docker] Install to /usr prefix --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 4623f87fb25..a9663d35548 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ WORKDIR /usr/src/QGIS/build RUN cmake \ -GNinja \ + -DCMAKE_INSTALL_PREFIX=/usr \ -DWITH_STAGED_PLUGINS=ON \ -DWITH_GRASS=ON \ -DSUPPRESS_QT_WARNINGS=ON \ From f38c0320469072dddae6cf2985fe2be013881b32 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 22 Aug 2017 08:43:27 +0200 Subject: [PATCH 078/364] [docker] Install bindings to system python path --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index a9663d35548..d17c0183606 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ WORKDIR /usr/src/QGIS/build RUN cmake \ -GNinja \ -DCMAKE_INSTALL_PREFIX=/usr \ + -DBINDINGS_GLOBAL_INSTALL=ON \ -DWITH_STAGED_PLUGINS=ON \ -DWITH_GRASS=ON \ -DSUPPRESS_QT_WARNINGS=ON \ From ef89a624a1308daec5ebf1b8a8f13fde822e17d1 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Sun, 20 Aug 2017 13:48:39 +0200 Subject: [PATCH 079/364] creatensis.pl: don't require admin rights to create the uninstaller --- ms-windows/osgeo4w/creatensis.pl | 22 ++++++++++++++++------ scripts/spell_check/spelling.dat | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/ms-windows/osgeo4w/creatensis.pl b/ms-windows/osgeo4w/creatensis.pl index b6f0de4bb86..cc25161b804 100755 --- a/ms-windows/osgeo4w/creatensis.pl +++ b/ms-windows/osgeo4w/creatensis.pl @@ -13,6 +13,12 @@ # Download OSGeo4W packages # +BEGIN { + # ignore requireAdministrator execution level while producing the + # uninstaller + $ENV{"__COMPAT_LAYER"} = 'RUNASINVOKER'; +} + use strict; use warnings; use Getopt::Long; @@ -448,16 +454,18 @@ my $installerbase = "$packagename-OSGeo4W-$version-$binary-Setup$archpostfix"; my $run; my $instdest; -unless($^O =~ /win/i) { + +if($^O eq "cygwin") { + $run = "cygstart "; + $instdest = `cygpath -w \$PWD`; +} else { $run = "wine "; $instdest = `winepath -w \$PWD`; - $instdest =~ s/\s+$//; - $instdest =~ s/\\/\\\\/g; -} else { - $run = ""; - $instdest = "."; } +$instdest =~ s/\s+$//; +$instdest =~ s/\\/\\\\/g; + my $args = ""; $args .= " -V$verbose"; @@ -498,7 +506,9 @@ die "running makensis failed [$cmd]" if $?; die "makeuinst.exe not created" unless -f "makeuinst.exe"; unlink "uninstall.exe"; +chmod 0755, "makeuinst.exe"; system "${run}makeuinst.exe"; +sleep 5; die "uninstall.exe not created" unless -f "uninstall.exe"; unlink "makeuinst.exe"; diff --git a/scripts/spell_check/spelling.dat b/scripts/spell_check/spelling.dat index c2f8243fa09..426a23acf7a 100644 --- a/scripts/spell_check/spelling.dat +++ b/scripts/spell_check/spelling.dat @@ -7101,6 +7101,7 @@ tyrranies:tyrannies tyrrany:tyranny ubiquitious:ubiquitous ublisher:publisher +updat:update udpate:update udpated:updated udpates:updates From ab2589bc0330942b5ba74287174a0d9ed1ae9efe Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 22 Aug 2017 11:18:11 +0200 Subject: [PATCH 080/364] Added rasterLayer method --- python/core/qgsmimedatautils.sip | 8 ++++++++ src/core/qgsmimedatautils.cpp | 12 ++++++++++++ src/core/qgsmimedatautils.h | 7 +++++++ 3 files changed, 27 insertions(+) diff --git a/python/core/qgsmimedatautils.sip b/python/core/qgsmimedatautils.sip index 6f9c062d79c..7ab7b35dfb0 100644 --- a/python/core/qgsmimedatautils.sip +++ b/python/core/qgsmimedatautils.sip @@ -48,6 +48,14 @@ Returns encoded representation of the object :rtype: QgsVectorLayer %End + QgsRasterLayer *rasterLayer( bool &owner, QString &error ) const; +%Docstring + Get raster layer from uri if possible, otherwise returns 0 and error is set + \param owner set to true if caller becomes owner + \param error set to error message if cannot get raster + :rtype: QgsRasterLayer +%End + QString layerType; %Docstring Type of URI. Recognized types: "vector" / "raster" / "plugin" / "custom" diff --git a/src/core/qgsmimedatautils.cpp b/src/core/qgsmimedatautils.cpp index 9d1b6cc9a42..7c625efc003 100644 --- a/src/core/qgsmimedatautils.cpp +++ b/src/core/qgsmimedatautils.cpp @@ -100,6 +100,18 @@ QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error return new QgsVectorLayer( uri, name, providerKey ); } +QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const +{ + owner = false; + if ( layerType != QLatin1String( "raster" ) ) + { + error = QObject::tr( "%1: Not a raster layer." ).arg( name ); + return nullptr; + } + owner = true; + return new QgsRasterLayer( uri, name, providerKey ); +} + // ----- bool QgsMimeDataUtils::isUriList( const QMimeData *data ) diff --git a/src/core/qgsmimedatautils.h b/src/core/qgsmimedatautils.h index d7edcb8ae2c..93eaeaf4736 100644 --- a/src/core/qgsmimedatautils.h +++ b/src/core/qgsmimedatautils.h @@ -23,6 +23,7 @@ class QgsLayerItem; class QgsLayerTreeNode; class QgsVectorLayer; +class QgsRasterLayer; /** \ingroup core * \class QgsMimeDataUtils @@ -51,6 +52,12 @@ class CORE_EXPORT QgsMimeDataUtils */ QgsVectorLayer *vectorLayer( bool &owner, QString &error ) const; + /** Get raster layer from uri if possible, otherwise returns 0 and error is set + * \param owner set to true if caller becomes owner + * \param error set to error message if cannot get raster + */ + QgsRasterLayer *rasterLayer( bool &owner, QString &error ) const; + //! Type of URI. Recognized types: "vector" / "raster" / "plugin" / "custom" QString layerType; //! For "vector" / "raster" type: provider id. From 13c9e1de0181346359f9034a103f595ea769f42c Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 22 Aug 2017 11:19:05 +0200 Subject: [PATCH 081/364] OGR provider: moved QgsCPLErrorHandler to its own header file This is being reused in gpkg data items --- src/providers/ogr/qgscplerrorhandler.h | 48 ++++++++++++++++++++++++++ src/providers/ogr/qgsogrprovider.cpp | 26 +------------- 2 files changed, 49 insertions(+), 25 deletions(-) create mode 100644 src/providers/ogr/qgscplerrorhandler.h diff --git a/src/providers/ogr/qgscplerrorhandler.h b/src/providers/ogr/qgscplerrorhandler.h new file mode 100644 index 00000000000..fa816cb1744 --- /dev/null +++ b/src/providers/ogr/qgscplerrorhandler.h @@ -0,0 +1,48 @@ +/*************************************************************************** + qgscplerrorhandler.h - QgsCplErrorHandler + + --------------------- + begin : Oct 29, 2003 + copyright : (C) 2003 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. * + * * + ***************************************************************************/ +#ifndef QGSCPLERRORHANDLER_H +#define QGSCPLERRORHANDLER_H + +#include "gdal.h" +#include "qgsmessagelog.h" + +class QgsCPLErrorHandler +{ + static void CPL_STDCALL showError( CPLErr errClass, int errNo, const char *msg ) + { + if ( errNo != OGRERR_NONE ) + QgsMessageLog::logMessage( QObject::tr( "OGR[%1] error %2: %3" ).arg( errClass ).arg( errNo ).arg( msg ), QObject::tr( "OGR" ) ); + } + + public: + QgsCPLErrorHandler() + { + CPLPushErrorHandler( showError ); + } + + ~QgsCPLErrorHandler() + { + CPLPopErrorHandler(); + } + + private: + QgsCPLErrorHandler( const QgsCPLErrorHandler &other ); + QgsCPLErrorHandler &operator=( const QgsCPLErrorHandler &other ); + +}; + + +#endif // QGSCPLERRORHANDLER_H diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 0126c6535ee..aea2482aa00 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -16,6 +16,7 @@ email : sherman at mrcc.com ***************************************************************************/ #include "qgsogrprovider.h" +#include "qgscplerrorhandler.h" #include "qgsogrfeatureiterator.h" #include "qgslogger.h" #include "qgsmessagelog.h" @@ -80,31 +81,6 @@ static const QString TEXT_PROVIDER_DESCRIPTION = static OGRwkbGeometryType ogrWkbGeometryTypeFromName( const QString &typeName ); -class QgsCPLErrorHandler -{ - static void CPL_STDCALL showError( CPLErr errClass, int errNo, const char *msg ) - { - if ( errNo != OGRERR_NONE ) - QgsMessageLog::logMessage( QObject::tr( "OGR[%1] error %2: %3" ).arg( errClass ).arg( errNo ).arg( msg ), QObject::tr( "OGR" ) ); - } - - public: - QgsCPLErrorHandler() - { - CPLPushErrorHandler( showError ); - } - - ~QgsCPLErrorHandler() - { - CPLPopErrorHandler(); - } - - private: - QgsCPLErrorHandler( const QgsCPLErrorHandler &other ); - QgsCPLErrorHandler &operator=( const QgsCPLErrorHandler &other ); - -}; - bool QgsOgrProvider::convertField( QgsField &field, const QTextCodec &encoding ) { From 384cabd7a51f2ae44d2f5e0b0aed1885fa79a2e9 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 22 Aug 2017 11:20:31 +0200 Subject: [PATCH 082/364] Geopackage: handle raster drop in the browser --- src/providers/ogr/qgsgeopackagedataitems.cpp | 126 +++++++++++-------- 1 file changed, 75 insertions(+), 51 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index de922070814..fcb3ba83ab9 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -23,10 +23,12 @@ #include "qgsvectorlayer.h" #include "qgsrasterlayer.h" #include "qgsogrprovider.h" +#include "qgscplerrorhandler.h" #include "qgsnewgeopackagelayerdialog.h" #include "qgsmessageoutput.h" #include "qgsvectorlayerexporter.h" #include "gdal.h" +#include "gdal_utils.h" #include #include @@ -322,6 +324,8 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct return false; QString uri; + // This sends OGR/GDAL errors to the message log + QgsCPLErrorHandler handler; QStringList importResults; bool hasError = false; @@ -329,55 +333,66 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data ); Q_FOREACH ( const QgsMimeDataUtils::Uri &u, lst ) { - if ( u.layerType == QStringLiteral( "vector" ) ) + // Check that we are not copying over self + if ( u.uri.startsWith( mPath ) ) { - // Check that we are not copying over self - if ( u.uri.startsWith( mPath ) ) - { - importResults.append( tr( "You cannot import layer %1 over itself!" ).arg( u.name ) ); - hasError = true; + importResults.append( tr( "You cannot import layer %1 over itself!" ).arg( u.name ) ); + hasError = true; + } + else + { + QgsMapLayer *srcLayer; + bool owner; + bool isVector = false; + QString error; + // Common checks for raster and vector + // aspatial is treated like vector + if ( u.layerType == QStringLiteral( "vector" ) ) + { + // open the source layer + srcLayer = u.vectorLayer( owner, error ); + isVector = true; } else { - // 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; - } + srcLayer = u.rasterLayer( 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 ); + 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() ) + // 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 ) { - if ( child->name() == u.name ) - { - exists = true; - } + 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 ) + } + 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 ) + { + if ( isVector ) { - + QgsVectorLayer *vectorSrcLayer = dynamic_cast < QgsVectorLayer * >( srcLayer ); QVariantMap options; 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, owner ) ); - + std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( vectorSrcLayer, uri, QStringLiteral( "ogr" ), vectorSrcLayer->crs(), options, owner ) ); // when export is successful: connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]() { @@ -400,24 +415,34 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct QgsApplication::taskManager()->addTask( exportTask.release() ); } - } - else - { - importResults.append( tr( "%1: Not a valid layer!" ).arg( u.name ) ); - hasError = true; - } - } - } - else - { - // TODO: implement raster import - QgsMessageOutput *output = QgsMessageOutput::createMessageOutput(); - output->setTitle( tr( "Import to GeoPackage database failed" ) ); - output->setMessage( tr( "Failed to import some layers!\n\n" ) + QStringLiteral( "Raster import is not yet implemented!\n" ), QgsMessageOutput::MessageText ); - output->showMessage(); - } + else // Import raster + { + // In case we need it + // QgsRasterLayer* rasterSrcLayer = dynamic_cast < QgsRasterLayer* > ( srcLayer ); - } + const char *args[] = { "-of", "gpkg", "-co", QStringLiteral( "RASTER_TABLE=%1" ).arg( u.name ).toUtf8().constData(), "-co", "APPEND_SUBDATASET=YES", nullptr }; + GDALTranslateOptions *psOptions = GDALTranslateOptionsNew( ( char ** )args, nullptr ); + GDALDatasetH hSrcDS = GDALOpen( u.uri.toUtf8().constData(), GA_ReadOnly ); + CPLErrorReset(); + GDALDatasetH hOutDS = GDALTranslate( mPath.toUtf8().constData(), hSrcDS, psOptions, NULL ); + if ( ! hOutDS ) + { + importResults.append( tr( "Failed to import layer %1! See the message logs for details.\n\n" ).arg( u.name ) ); + hasError = true; + + } + GDALClose( hSrcDS ); + GDALTranslateOptionsFree( psOptions ); + } + } // do not overwrite + } + else + { + importResults.append( tr( "%1: Not a valid layer!" ).arg( u.name ) ); + hasError = true; + } + } // check for self copy + } // for each if ( hasError ) { @@ -426,7 +451,6 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct output->setMessage( tr( "Failed to import some layers!\n\n" ) + importResults.join( QStringLiteral( "\n" ) ), QgsMessageOutput::MessageText ); output->showMessage(); } - return true; } From ed426204ca64b41e461514a5d1dfe1b1b4b84bb3 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 22 Aug 2017 13:09:20 +0200 Subject: [PATCH 083/364] I'm eating too much Python (fix a Qt warning for missing arg) --- src/providers/ogr/qgsogrprovider.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index aea2482aa00..1c220902b1f 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -4341,10 +4341,10 @@ QGISEXTERN bool deleteLayer( const QString &uri, QString &errCause ) errCause = QObject::tr( "Success" ); break; } - errCause = QObject::tr( "GDAL result code: %s" ).arg( errCause ); + errCause = QObject::tr( "GDAL result code: %1" ).arg( errCause ); return error == OGRERR_NONE; } // This should never happen: - errCause = QObject::tr( "Layer not found: %s" ).arg( uri ); + errCause = QObject::tr( "Layer not found: %1" ).arg( uri ); return false; } From 73fdb25449f5092fc3ed1c718ea443fd9ffdc846 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 22 Aug 2017 13:10:20 +0200 Subject: [PATCH 084/364] Safer handling of GDAL handles and feedback on success --- src/providers/ogr/qgsgeopackagedataitems.cpp | 26 +++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index fcb3ba83ab9..7563e7e5f4a 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -423,15 +423,29 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct const char *args[] = { "-of", "gpkg", "-co", QStringLiteral( "RASTER_TABLE=%1" ).arg( u.name ).toUtf8().constData(), "-co", "APPEND_SUBDATASET=YES", nullptr }; GDALTranslateOptions *psOptions = GDALTranslateOptionsNew( ( char ** )args, nullptr ); GDALDatasetH hSrcDS = GDALOpen( u.uri.toUtf8().constData(), GA_ReadOnly ); - CPLErrorReset(); - GDALDatasetH hOutDS = GDALTranslate( mPath.toUtf8().constData(), hSrcDS, psOptions, NULL ); - if ( ! hOutDS ) + if ( ! hSrcDS ) { - importResults.append( tr( "Failed to import layer %1! See the message logs for details.\n\n" ).arg( u.name ) ); + importResults.append( tr( "Failed to open source layer %1! See the message logs for details.\n\n" ).arg( u.name ) ); hasError = true; - } - GDALClose( hSrcDS ); + else + { + CPLErrorReset(); + GDALDatasetH hOutDS = GDALTranslate( mPath.toUtf8().constData(), hSrcDS, psOptions, NULL ); + if ( ! hOutDS ) + { + importResults.append( tr( "Failed to import layer %1! See the message logs for details.\n\n" ).arg( u.name ) ); + hasError = true; + } + else // All good! + { + GDALClose( hOutDS ); + // 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(); + } + GDALClose( hSrcDS ); + } GDALTranslateOptionsFree( psOptions ); } } // do not overwrite From b62fd51cc2c4c700ac714bcf159e0d547c00d516 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 22 Aug 2017 23:16:33 +1000 Subject: [PATCH 085/364] Don't allow dragging groups in model algorithm tree --- python/plugins/processing/modeler/ModelerDialog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/plugins/processing/modeler/ModelerDialog.py b/python/plugins/processing/modeler/ModelerDialog.py index f1f041465fb..637a18cee0b 100644 --- a/python/plugins/processing/modeler/ModelerDialog.py +++ b/python/plugins/processing/modeler/ModelerDialog.py @@ -659,6 +659,7 @@ class ModelerDialog(BASE, WIDGET): name = alg.group() groupItem.setText(0, name) groupItem.setToolTip(0, name) + groupItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) if provider.id() in ('qgis', 'native'): groupItem.setIcon(0, provider.icon()) qgis_groups[alg.group()] = groupItem @@ -672,6 +673,7 @@ class ModelerDialog(BASE, WIDGET): providerItem.setText(0, provider.name()) providerItem.setToolTip(0, provider.name()) providerItem.setIcon(0, provider.icon()) + providerItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) From 8218f35383ba0ccd1101121a63efc0e870cf2f0a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 22 Aug 2017 23:20:52 +1000 Subject: [PATCH 086/364] Fix use of incorrect icon for algorithm --- python/plugins/processing/algs/qgis/PointsLayerFromTable.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/python/plugins/processing/algs/qgis/PointsLayerFromTable.py b/python/plugins/processing/algs/qgis/PointsLayerFromTable.py index 2597acdf0c9..36dbea8dcc4 100644 --- a/python/plugins/processing/algs/qgis/PointsLayerFromTable.py +++ b/python/plugins/processing/algs/qgis/PointsLayerFromTable.py @@ -48,12 +48,6 @@ class PointsLayerFromTable(QgisAlgorithm): OUTPUT = 'OUTPUT' TARGET_CRS = 'TARGET_CRS' - def icon(self): - return QgsApplication.getThemeIcon("/providerQgis.svg") - - def svgIconPath(self): - return QgsApplication.iconPath("providerQgis.svg") - def tags(self): return self.tr('points,create,values,attributes').split(',') From 2a442c7886c2a5c4494519a7a8da8a5ea0b963ed Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 22 Aug 2017 23:36:42 +1000 Subject: [PATCH 087/364] Rationalise algorithm groups a bit --- .../processing/algs/qgis/AddTableField.py | 2 +- .../plugins/processing/algs/qgis/Aggregate.py | 2 +- .../algs/qgis/AutoincrementalField.py | 2 +- .../processing/algs/qgis/BasicStatistics.py | 2 +- .../plugins/processing/algs/qgis/Boundary.py | 2 +- .../processing/algs/qgis/BoundingBox.py | 2 +- .../processing/algs/qgis/CheckValidity.py | 2 +- .../processing/algs/qgis/ConcaveHull.py | 2 +- .../processing/algs/qgis/ConvexHull.py | 2 +- .../algs/qgis/CreateAttributeIndex.py | 2 +- .../processing/algs/qgis/Datasources2Vrt.py | 2 +- .../processing/algs/qgis/DefineProjection.py | 2 +- .../plugins/processing/algs/qgis/Delaunay.py | 2 +- .../processing/algs/qgis/DeleteColumn.py | 2 +- .../algs/qgis/DeleteDuplicateGeometries.py | 2 +- .../processing/algs/qgis/DeleteHoles.py | 2 +- .../processing/algs/qgis/DensifyGeometries.py | 2 +- .../algs/qgis/DensifyGeometriesInterval.py | 2 +- .../processing/algs/qgis/Difference.py | 2 +- .../processing/algs/qgis/DropGeometry.py | 2 +- .../processing/algs/qgis/DropMZValues.py | 2 +- .../algs/qgis/EliminateSelection.py | 2 +- .../algs/qgis/EquivalentNumField.py | 2 +- .../processing/algs/qgis/ExecuteSQL.py | 2 +- .../plugins/processing/algs/qgis/Explode.py | 2 +- .../algs/qgis/ExportGeometryInfo.py | 2 +- .../processing/algs/qgis/ExtendLines.py | 2 +- .../processing/algs/qgis/ExtentFromLayer.py | 2 +- .../processing/algs/qgis/ExtractByLocation.py | 2 +- .../processing/algs/qgis/ExtractNodes.py | 2 +- .../algs/qgis/ExtractSpecificNodes.py | 2 +- .../processing/algs/qgis/FieldPyculator.py | 2 +- .../processing/algs/qgis/FieldsCalculator.py | 2 +- .../processing/algs/qgis/FieldsMapper.py | 2 +- .../processing/algs/qgis/FindProjection.py | 2 +- .../processing/algs/qgis/FixGeometry.py | 2 +- .../algs/qgis/FixedDistanceBuffer.py | 2 +- .../algs/qgis/GeometryByExpression.py | 2 +- .../processing/algs/qgis/GeometryConvert.py | 2 +- .../plugins/processing/algs/qgis/GridLine.py | 2 +- .../processing/algs/qgis/GridPolygon.py | 2 +- .../plugins/processing/algs/qgis/Gridify.py | 2 +- .../processing/algs/qgis/HubDistanceLines.py | 2 +- .../processing/algs/qgis/HubDistancePoints.py | 2 +- .../plugins/processing/algs/qgis/HubLines.py | 2 +- .../processing/algs/qgis/HypsometricCurves.py | 2 +- .../processing/algs/qgis/Intersection.py | 2 +- .../processing/algs/qgis/JoinAttributes.py | 2 +- .../processing/algs/qgis/LinesIntersection.py | 2 +- .../processing/algs/qgis/LinesToPolygons.py | 2 +- .../processing/algs/qgis/MeanCoords.py | 2 +- python/plugins/processing/algs/qgis/Merge.py | 2 +- .../processing/algs/qgis/MergeLines.py | 2 +- .../algs/qgis/NearestNeighbourAnalysis.py | 2 +- .../processing/algs/qgis/OffsetLine.py | 2 +- .../algs/qgis/OrientedMinimumBoundingBox.py | 2 +- .../processing/algs/qgis/Orthogonalize.py | 2 +- .../processing/algs/qgis/PointDistance.py | 2 +- .../processing/algs/qgis/PointOnSurface.py | 2 +- .../algs/qgis/PointsAlongGeometry.py | 2 +- .../algs/qgis/PointsDisplacement.py | 2 +- .../processing/algs/qgis/PointsFromLines.py | 2 +- .../algs/qgis/PointsFromPolygons.py | 2 +- .../processing/algs/qgis/PointsInPolygon.py | 2 +- .../algs/qgis/PointsLayerFromTable.py | 2 +- .../processing/algs/qgis/PointsToPaths.py | 2 +- .../algs/qgis/PoleOfInaccessibility.py | 2 +- .../processing/algs/qgis/Polygonize.py | 2 +- .../processing/algs/qgis/PolygonsToLines.py | 2 +- .../processing/algs/qgis/RandomExtract.py | 2 +- .../algs/qgis/RandomExtractWithinSubsets.py | 2 +- .../algs/qgis/RandomPointsAlongLines.py | 2 +- .../algs/qgis/RandomPointsExtent.py | 2 +- .../processing/algs/qgis/RandomPointsLayer.py | 2 +- .../algs/qgis/RandomPointsPolygons.py | 2 +- .../processing/algs/qgis/RandomSelection.py | 2 +- .../algs/qgis/RandomSelectionWithinSubsets.py | 2 +- .../processing/algs/qgis/RasterCalculator.py | 2 +- .../algs/qgis/RasterLayerStatistics.py | 2 +- .../algs/qgis/RectanglesOvalsDiamondsFixed.py | 2 +- .../qgis/RectanglesOvalsDiamondsVariable.py | 2 +- .../processing/algs/qgis/RegularPoints.py | 2 +- .../algs/qgis/ReverseLineDirection.py | 2 +- .../algs/qgis/SaveSelectedFeatures.py | 2 +- .../processing/algs/qgis/SelectByAttribute.py | 2 +- .../algs/qgis/SelectByAttributeSum.py | 2 +- .../algs/qgis/SelectByExpression.py | 2 +- .../processing/algs/qgis/SelectByLocation.py | 2 +- .../plugins/processing/algs/qgis/SetMValue.py | 2 +- .../processing/algs/qgis/SetRasterStyle.py | 2 +- .../processing/algs/qgis/SetVectorStyle.py | 2 +- .../plugins/processing/algs/qgis/SetZValue.py | 2 +- .../algs/qgis/SimplifyGeometries.py | 2 +- .../algs/qgis/SinglePartsToMultiparts.py | 2 +- .../processing/algs/qgis/SingleSidedBuffer.py | 2 +- python/plugins/processing/algs/qgis/Smooth.py | 2 +- .../processing/algs/qgis/SnapGeometries.py | 2 +- .../processing/algs/qgis/SpatialIndex.py | 2 +- .../processing/algs/qgis/SpatialJoin.py | 2 +- .../processing/algs/qgis/SplitWithLines.py | 2 +- .../algs/qgis/StatisticsByCategories.py | 2 +- .../plugins/processing/algs/qgis/SumLines.py | 2 +- .../algs/qgis/SymmetricalDifference.py | 2 +- .../processing/algs/qgis/TextToFloat.py | 2 +- .../processing/algs/qgis/TopoColors.py | 2 +- .../plugins/processing/algs/qgis/Translate.py | 2 +- .../processing/algs/qgis/TruncateTable.py | 2 +- python/plugins/processing/algs/qgis/Union.py | 2 +- .../processing/algs/qgis/UniqueValues.py | 2 +- .../algs/qgis/VariableDistanceBuffer.py | 2 +- .../processing/algs/qgis/VectorSplit.py | 2 +- .../processing/algs/qgis/VoronoiPolygons.py | 2 +- .../processing/algs/qgis/ZonalStatistics.py | 2 +- .../algs/qgis/scripts/Frequency_analysis.py | 2 +- .../algs/qgis/scripts/Keep_n_biggest_parts.py | 2 +- .../Number_of_unique_values_in_classes.py | 2 +- .../processing/algs/saga/SagaNameDecorator.py | 4 ++-- src/core/processing/qgsnativealgorithms.h | 20 +++++++++---------- 118 files changed, 128 insertions(+), 128 deletions(-) diff --git a/python/plugins/processing/algs/qgis/AddTableField.py b/python/plugins/processing/algs/qgis/AddTableField.py index 321c6eb43c2..76a93d687b7 100644 --- a/python/plugins/processing/algs/qgis/AddTableField.py +++ b/python/plugins/processing/algs/qgis/AddTableField.py @@ -43,7 +43,7 @@ class AddTableField(QgisFeatureBasedAlgorithm): TYPES = [QVariant.Int, QVariant.Double, QVariant.String] def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Aggregate.py b/python/plugins/processing/algs/qgis/Aggregate.py index bed5597ece5..6c5cf881204 100644 --- a/python/plugins/processing/algs/qgis/Aggregate.py +++ b/python/plugins/processing/algs/qgis/Aggregate.py @@ -55,7 +55,7 @@ class Aggregate(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def name(self): return 'aggregate' diff --git a/python/plugins/processing/algs/qgis/AutoincrementalField.py b/python/plugins/processing/algs/qgis/AutoincrementalField.py index 9f93b688eb5..5cf8f7281cf 100644 --- a/python/plugins/processing/algs/qgis/AutoincrementalField.py +++ b/python/plugins/processing/algs/qgis/AutoincrementalField.py @@ -37,7 +37,7 @@ class AutoincrementalField(QgisFeatureBasedAlgorithm): self.current = 0 def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def name(self): return 'addautoincrementalfield' diff --git a/python/plugins/processing/algs/qgis/BasicStatistics.py b/python/plugins/processing/algs/qgis/BasicStatistics.py index c6a96bebd37..5ddeb15a3c6 100644 --- a/python/plugins/processing/algs/qgis/BasicStatistics.py +++ b/python/plugins/processing/algs/qgis/BasicStatistics.py @@ -81,7 +81,7 @@ class BasicStatisticsForField(QgisAlgorithm): 'count,distinct,unique,variance,median,quartile,range,majority,minority').split(',') def group(self): - return self.tr('Vector table tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Boundary.py b/python/plugins/processing/algs/qgis/Boundary.py index 558d71276e8..c6ecc309c0f 100644 --- a/python/plugins/processing/algs/qgis/Boundary.py +++ b/python/plugins/processing/algs/qgis/Boundary.py @@ -46,7 +46,7 @@ class Boundary(QgisFeatureBasedAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def name(self): return 'boundary' diff --git a/python/plugins/processing/algs/qgis/BoundingBox.py b/python/plugins/processing/algs/qgis/BoundingBox.py index 796ec52de16..d00d7156b4e 100644 --- a/python/plugins/processing/algs/qgis/BoundingBox.py +++ b/python/plugins/processing/algs/qgis/BoundingBox.py @@ -45,7 +45,7 @@ class BoundingBox(QgisFeatureBasedAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'matrix.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/CheckValidity.py b/python/plugins/processing/algs/qgis/CheckValidity.py index 619e26d9760..7b9fa89ffe1 100644 --- a/python/plugins/processing/algs/qgis/CheckValidity.py +++ b/python/plugins/processing/algs/qgis/CheckValidity.py @@ -66,7 +66,7 @@ class CheckValidity(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'check_geometry.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ConcaveHull.py b/python/plugins/processing/algs/qgis/ConcaveHull.py index 444d2b37467..20ede88873a 100644 --- a/python/plugins/processing/algs/qgis/ConcaveHull.py +++ b/python/plugins/processing/algs/qgis/ConcaveHull.py @@ -50,7 +50,7 @@ class ConcaveHull(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ConvexHull.py b/python/plugins/processing/algs/qgis/ConvexHull.py index 2559e5527f7..214140bab3a 100644 --- a/python/plugins/processing/algs/qgis/ConvexHull.py +++ b/python/plugins/processing/algs/qgis/ConvexHull.py @@ -61,7 +61,7 @@ class ConvexHull(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/CreateAttributeIndex.py b/python/plugins/processing/algs/qgis/CreateAttributeIndex.py index f4694852950..d7021d202de 100644 --- a/python/plugins/processing/algs/qgis/CreateAttributeIndex.py +++ b/python/plugins/processing/algs/qgis/CreateAttributeIndex.py @@ -41,7 +41,7 @@ class CreateAttributeIndex(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Datasources2Vrt.py b/python/plugins/processing/algs/qgis/Datasources2Vrt.py index ac158a9da52..702465f9bb5 100644 --- a/python/plugins/processing/algs/qgis/Datasources2Vrt.py +++ b/python/plugins/processing/algs/qgis/Datasources2Vrt.py @@ -48,7 +48,7 @@ class Datasources2Vrt(QgisAlgorithm): VRT_STRING = 'VRT_STRING' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/DefineProjection.py b/python/plugins/processing/algs/qgis/DefineProjection.py index 520ef50fd49..74b3eb5f401 100644 --- a/python/plugins/processing/algs/qgis/DefineProjection.py +++ b/python/plugins/processing/algs/qgis/DefineProjection.py @@ -44,7 +44,7 @@ class DefineProjection(QgisAlgorithm): CRS = 'CRS' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Delaunay.py b/python/plugins/processing/algs/qgis/Delaunay.py index 89081470625..9bd001fc763 100644 --- a/python/plugins/processing/algs/qgis/Delaunay.py +++ b/python/plugins/processing/algs/qgis/Delaunay.py @@ -60,7 +60,7 @@ class Delaunay(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'delaunay.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/DeleteColumn.py b/python/plugins/processing/algs/qgis/DeleteColumn.py index 2a2c8c34247..f723386a528 100644 --- a/python/plugins/processing/algs/qgis/DeleteColumn.py +++ b/python/plugins/processing/algs/qgis/DeleteColumn.py @@ -37,7 +37,7 @@ class DeleteColumn(QgisFeatureBasedAlgorithm): return self.tr('drop,delete,remove,fields,columns,attributes').split(',') def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/DeleteDuplicateGeometries.py b/python/plugins/processing/algs/qgis/DeleteDuplicateGeometries.py index 143d2d08cf8..9c6b3800a6a 100644 --- a/python/plugins/processing/algs/qgis/DeleteDuplicateGeometries.py +++ b/python/plugins/processing/algs/qgis/DeleteDuplicateGeometries.py @@ -38,7 +38,7 @@ class DeleteDuplicateGeometries(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/DeleteHoles.py b/python/plugins/processing/algs/qgis/DeleteHoles.py index 2e52a69d793..795e6fdef14 100644 --- a/python/plugins/processing/algs/qgis/DeleteHoles.py +++ b/python/plugins/processing/algs/qgis/DeleteHoles.py @@ -45,7 +45,7 @@ class DeleteHoles(QgisFeatureBasedAlgorithm): return self.tr('remove,delete,drop,holes,rings,fill').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def name(self): return 'deleteholes' diff --git a/python/plugins/processing/algs/qgis/DensifyGeometries.py b/python/plugins/processing/algs/qgis/DensifyGeometries.py index 87885473c59..396508ece47 100644 --- a/python/plugins/processing/algs/qgis/DensifyGeometries.py +++ b/python/plugins/processing/algs/qgis/DensifyGeometries.py @@ -41,7 +41,7 @@ class DensifyGeometries(QgisFeatureBasedAlgorithm): return self.tr('add,vertices,points').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py b/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py index 20b62df4ecb..f54d4905e55 100644 --- a/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py +++ b/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py @@ -37,7 +37,7 @@ class DensifyGeometriesInterval(QgisFeatureBasedAlgorithm): INTERVAL = 'INTERVAL' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Difference.py b/python/plugins/processing/algs/qgis/Difference.py index 69073be93a9..fb4f4da4659 100644 --- a/python/plugins/processing/algs/qgis/Difference.py +++ b/python/plugins/processing/algs/qgis/Difference.py @@ -52,7 +52,7 @@ class Difference(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'difference.png')) def group(self): - return self.tr('Vector overlay tools') + return self.tr('Vector overlay') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/DropGeometry.py b/python/plugins/processing/algs/qgis/DropGeometry.py index 57f2e9048be..f80ead4608a 100644 --- a/python/plugins/processing/algs/qgis/DropGeometry.py +++ b/python/plugins/processing/algs/qgis/DropGeometry.py @@ -37,7 +37,7 @@ class DropGeometry(QgisFeatureBasedAlgorithm): return self.tr('remove,drop,delete,geometry,objects').split(',') def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/DropMZValues.py b/python/plugins/processing/algs/qgis/DropMZValues.py index 71eeb443e5a..0078fe699da 100644 --- a/python/plugins/processing/algs/qgis/DropMZValues.py +++ b/python/plugins/processing/algs/qgis/DropMZValues.py @@ -43,7 +43,7 @@ class DropMZValues(QgisFeatureBasedAlgorithm): DROP_Z_VALUES = 'DROP_Z_VALUES' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/EliminateSelection.py b/python/plugins/processing/algs/qgis/EliminateSelection.py index ae54df30e0d..d03a9c8dc13 100644 --- a/python/plugins/processing/algs/qgis/EliminateSelection.py +++ b/python/plugins/processing/algs/qgis/EliminateSelection.py @@ -60,7 +60,7 @@ class EliminateSelection(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'eliminate.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/EquivalentNumField.py b/python/plugins/processing/algs/qgis/EquivalentNumField.py index 1b72ee8799b..5f82900fd60 100644 --- a/python/plugins/processing/algs/qgis/EquivalentNumField.py +++ b/python/plugins/processing/algs/qgis/EquivalentNumField.py @@ -41,7 +41,7 @@ class EquivalentNumField(QgisAlgorithm): FIELD = 'FIELD' def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ExecuteSQL.py b/python/plugins/processing/algs/qgis/ExecuteSQL.py index 6054d7860c9..dfa7baa9055 100644 --- a/python/plugins/processing/algs/qgis/ExecuteSQL.py +++ b/python/plugins/processing/algs/qgis/ExecuteSQL.py @@ -54,7 +54,7 @@ class ExecuteSQL(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Explode.py b/python/plugins/processing/algs/qgis/Explode.py index 1375f77f64d..2af46588f21 100644 --- a/python/plugins/processing/algs/qgis/Explode.py +++ b/python/plugins/processing/algs/qgis/Explode.py @@ -43,7 +43,7 @@ class Explode(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ExportGeometryInfo.py b/python/plugins/processing/algs/qgis/ExportGeometryInfo.py index 29ed2dbc25a..0d5d65a2933 100644 --- a/python/plugins/processing/algs/qgis/ExportGeometryInfo.py +++ b/python/plugins/processing/algs/qgis/ExportGeometryInfo.py @@ -58,7 +58,7 @@ class ExportGeometryInfo(QgisAlgorithm): return self.tr('export,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons').split(',') def group(self): - return self.tr('Vector table tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ExtendLines.py b/python/plugins/processing/algs/qgis/ExtendLines.py index 95f13a6c00f..c3377256fb3 100644 --- a/python/plugins/processing/algs/qgis/ExtendLines.py +++ b/python/plugins/processing/algs/qgis/ExtendLines.py @@ -36,7 +36,7 @@ class ExtendLines(QgisFeatureBasedAlgorithm): END_DISTANCE = 'END_DISTANCE' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ExtentFromLayer.py b/python/plugins/processing/algs/qgis/ExtentFromLayer.py index 4a3364fe448..d0bdd4cbd4d 100644 --- a/python/plugins/processing/algs/qgis/ExtentFromLayer.py +++ b/python/plugins/processing/algs/qgis/ExtentFromLayer.py @@ -61,7 +61,7 @@ class ExtentFromLayer(QgisAlgorithm): return self.tr('extent,envelope,bounds,bounding,boundary,layer').split(',') def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ExtractByLocation.py b/python/plugins/processing/algs/qgis/ExtractByLocation.py index fec53b2c58f..ef87e46e602 100644 --- a/python/plugins/processing/algs/qgis/ExtractByLocation.py +++ b/python/plugins/processing/algs/qgis/ExtractByLocation.py @@ -49,7 +49,7 @@ class ExtractByLocation(QgisAlgorithm): return self.tr('extract,filter,location,intersects,contains,within').split(',') def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ExtractNodes.py b/python/plugins/processing/algs/qgis/ExtractNodes.py index 41848c42f62..b081f09e066 100644 --- a/python/plugins/processing/algs/qgis/ExtractNodes.py +++ b/python/plugins/processing/algs/qgis/ExtractNodes.py @@ -54,7 +54,7 @@ class ExtractNodes(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'extract_nodes.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py b/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py index 76c485a5f80..4e9e9c8d3a6 100644 --- a/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py +++ b/python/plugins/processing/algs/qgis/ExtractSpecificNodes.py @@ -47,7 +47,7 @@ class ExtractSpecificNodes(QgisAlgorithm): NODES = 'NODES' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/FieldPyculator.py b/python/plugins/processing/algs/qgis/FieldPyculator.py index eb00cfcad04..f92847e566a 100644 --- a/python/plugins/processing/algs/qgis/FieldPyculator.py +++ b/python/plugins/processing/algs/qgis/FieldPyculator.py @@ -55,7 +55,7 @@ class FieldsPyculator(QgisAlgorithm): TYPES = [QVariant.Int, QVariant.Double, QVariant.String] def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/FieldsCalculator.py b/python/plugins/processing/algs/qgis/FieldsCalculator.py index 6d05e60dc9a..d75a904c258 100644 --- a/python/plugins/processing/algs/qgis/FieldsCalculator.py +++ b/python/plugins/processing/algs/qgis/FieldsCalculator.py @@ -58,7 +58,7 @@ class FieldsCalculator(QgisAlgorithm): TYPES = [QVariant.Double, QVariant.Int, QVariant.String, QVariant.Date] def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/FieldsMapper.py b/python/plugins/processing/algs/qgis/FieldsMapper.py index d155f2e695c..06be1805454 100644 --- a/python/plugins/processing/algs/qgis/FieldsMapper.py +++ b/python/plugins/processing/algs/qgis/FieldsMapper.py @@ -43,7 +43,7 @@ class FieldsMapper(QgisFeatureBasedAlgorithm): OUTPUT_LAYER = 'OUTPUT_LAYER' def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def initParameters(self, config=None): diff --git a/python/plugins/processing/algs/qgis/FindProjection.py b/python/plugins/processing/algs/qgis/FindProjection.py index 62f61f6772b..5f4049fad47 100644 --- a/python/plugins/processing/algs/qgis/FindProjection.py +++ b/python/plugins/processing/algs/qgis/FindProjection.py @@ -57,7 +57,7 @@ class FindProjection(QgisAlgorithm): return self.tr('crs,srs,coordinate,reference,system,guess,estimate,finder,determine').split(',') def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/FixGeometry.py b/python/plugins/processing/algs/qgis/FixGeometry.py index 78d98e97ab6..25b5ff63415 100644 --- a/python/plugins/processing/algs/qgis/FixGeometry.py +++ b/python/plugins/processing/algs/qgis/FixGeometry.py @@ -46,7 +46,7 @@ class FixGeometry(QgisAlgorithm): return self.tr('repair,invalid,geometry').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/FixedDistanceBuffer.py b/python/plugins/processing/algs/qgis/FixedDistanceBuffer.py index fb8c82bf9e3..c782ae31c71 100644 --- a/python/plugins/processing/algs/qgis/FixedDistanceBuffer.py +++ b/python/plugins/processing/algs/qgis/FixedDistanceBuffer.py @@ -60,7 +60,7 @@ class FixedDistanceBuffer(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'buffer.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/GeometryByExpression.py b/python/plugins/processing/algs/qgis/GeometryByExpression.py index 6be1fbaf73c..1e4af91d54c 100644 --- a/python/plugins/processing/algs/qgis/GeometryByExpression.py +++ b/python/plugins/processing/algs/qgis/GeometryByExpression.py @@ -44,7 +44,7 @@ class GeometryByExpression(QgisFeatureBasedAlgorithm): EXPRESSION = 'EXPRESSION' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/GeometryConvert.py b/python/plugins/processing/algs/qgis/GeometryConvert.py index 5bf3e9d4204..0d9c6247b5b 100644 --- a/python/plugins/processing/algs/qgis/GeometryConvert.py +++ b/python/plugins/processing/algs/qgis/GeometryConvert.py @@ -46,7 +46,7 @@ class GeometryConvert(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/GridLine.py b/python/plugins/processing/algs/qgis/GridLine.py index 3bb87b8d9f5..1f7a159a598 100644 --- a/python/plugins/processing/algs/qgis/GridLine.py +++ b/python/plugins/processing/algs/qgis/GridLine.py @@ -68,7 +68,7 @@ class GridLine(QgisAlgorithm): return self.tr('grid,lines,vector,create,fishnet').split(',') def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/GridPolygon.py b/python/plugins/processing/algs/qgis/GridPolygon.py index 4377a3e48ac..20c1abb3ab7 100644 --- a/python/plugins/processing/algs/qgis/GridPolygon.py +++ b/python/plugins/processing/algs/qgis/GridPolygon.py @@ -67,7 +67,7 @@ class GridPolygon(QgisAlgorithm): return self.tr('grid,lines,vector,create,fishnet').split(',') def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Gridify.py b/python/plugins/processing/algs/qgis/Gridify.py index 06f99983bf8..c4f9bbaceff 100644 --- a/python/plugins/processing/algs/qgis/Gridify.py +++ b/python/plugins/processing/algs/qgis/Gridify.py @@ -42,7 +42,7 @@ class Gridify(QgisFeatureBasedAlgorithm): VSPACING = 'VSPACING' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/HubDistanceLines.py b/python/plugins/processing/algs/qgis/HubDistanceLines.py index 491a82a7ff9..3db4a0f9670 100644 --- a/python/plugins/processing/algs/qgis/HubDistanceLines.py +++ b/python/plugins/processing/algs/qgis/HubDistanceLines.py @@ -64,7 +64,7 @@ class HubDistanceLines(QgisAlgorithm): ] def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/HubDistancePoints.py b/python/plugins/processing/algs/qgis/HubDistancePoints.py index 7ec153557e6..d1ce6f7bd7f 100644 --- a/python/plugins/processing/algs/qgis/HubDistancePoints.py +++ b/python/plugins/processing/algs/qgis/HubDistancePoints.py @@ -61,7 +61,7 @@ class HubDistancePoints(QgisAlgorithm): ] def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/HubLines.py b/python/plugins/processing/algs/qgis/HubLines.py index 70778010037..22ee8d5f9b3 100644 --- a/python/plugins/processing/algs/qgis/HubLines.py +++ b/python/plugins/processing/algs/qgis/HubLines.py @@ -50,7 +50,7 @@ class HubLines(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/HypsometricCurves.py b/python/plugins/processing/algs/qgis/HypsometricCurves.py index 6678a764b58..609d0956dbb 100644 --- a/python/plugins/processing/algs/qgis/HypsometricCurves.py +++ b/python/plugins/processing/algs/qgis/HypsometricCurves.py @@ -56,7 +56,7 @@ class HypsometricCurves(QgisAlgorithm): OUTPUT_DIRECTORY = 'OUTPUT_DIRECTORY' def group(self): - return self.tr('Raster tools') + return self.tr('Raster terrain analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Intersection.py b/python/plugins/processing/algs/qgis/Intersection.py index a4201fbd757..5de81dd1502 100644 --- a/python/plugins/processing/algs/qgis/Intersection.py +++ b/python/plugins/processing/algs/qgis/Intersection.py @@ -68,7 +68,7 @@ class Intersection(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'intersect.png')) def group(self): - return self.tr('Vector overlay tools') + return self.tr('Vector overlay') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/JoinAttributes.py b/python/plugins/processing/algs/qgis/JoinAttributes.py index 5898bd89186..cbdf436770b 100644 --- a/python/plugins/processing/algs/qgis/JoinAttributes.py +++ b/python/plugins/processing/algs/qgis/JoinAttributes.py @@ -51,7 +51,7 @@ class JoinAttributes(QgisAlgorithm): FIELD_2 = 'FIELD_2' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/LinesIntersection.py b/python/plugins/processing/algs/qgis/LinesIntersection.py index eefb5450f09..fecd8ef4e38 100644 --- a/python/plugins/processing/algs/qgis/LinesIntersection.py +++ b/python/plugins/processing/algs/qgis/LinesIntersection.py @@ -58,7 +58,7 @@ class LinesIntersection(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'lines_intersection.png')) def group(self): - return self.tr('Vector overlay tools') + return self.tr('Vector overlay') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/LinesToPolygons.py b/python/plugins/processing/algs/qgis/LinesToPolygons.py index 8ac1abf9c39..faa1e6a341e 100644 --- a/python/plugins/processing/algs/qgis/LinesToPolygons.py +++ b/python/plugins/processing/algs/qgis/LinesToPolygons.py @@ -57,7 +57,7 @@ class LinesToPolygons(QgisFeatureBasedAlgorithm): return self.tr('line,polygon,convert').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/MeanCoords.py b/python/plugins/processing/algs/qgis/MeanCoords.py index 94915ee94b5..eb09504640a 100644 --- a/python/plugins/processing/algs/qgis/MeanCoords.py +++ b/python/plugins/processing/algs/qgis/MeanCoords.py @@ -62,7 +62,7 @@ class MeanCoords(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'mean.png')) def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Merge.py b/python/plugins/processing/algs/qgis/Merge.py index 72252a3fd39..976ca7e2832 100644 --- a/python/plugins/processing/algs/qgis/Merge.py +++ b/python/plugins/processing/algs/qgis/Merge.py @@ -52,7 +52,7 @@ class Merge(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'merge_shapes.png')) def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/MergeLines.py b/python/plugins/processing/algs/qgis/MergeLines.py index 736b5fe5450..dc3467443ee 100644 --- a/python/plugins/processing/algs/qgis/MergeLines.py +++ b/python/plugins/processing/algs/qgis/MergeLines.py @@ -47,7 +47,7 @@ class MergeLines(QgisFeatureBasedAlgorithm): return self.tr('line,merge,join,parts').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/NearestNeighbourAnalysis.py b/python/plugins/processing/algs/qgis/NearestNeighbourAnalysis.py index afcff2e2a15..8e705bbb68e 100644 --- a/python/plugins/processing/algs/qgis/NearestNeighbourAnalysis.py +++ b/python/plugins/processing/algs/qgis/NearestNeighbourAnalysis.py @@ -62,7 +62,7 @@ class NearestNeighbourAnalysis(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'neighbour.png')) def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/OffsetLine.py b/python/plugins/processing/algs/qgis/OffsetLine.py index acdba6ce119..912e3d1fe2f 100644 --- a/python/plugins/processing/algs/qgis/OffsetLine.py +++ b/python/plugins/processing/algs/qgis/OffsetLine.py @@ -48,7 +48,7 @@ class OffsetLine(QgisFeatureBasedAlgorithm): MITER_LIMIT = 'MITER_LIMIT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py b/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py index c3b1914bab1..1d85ee9eb2e 100644 --- a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py +++ b/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py @@ -49,7 +49,7 @@ class OrientedMinimumBoundingBox(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Orthogonalize.py b/python/plugins/processing/algs/qgis/Orthogonalize.py index 00038a52050..acf5b38599c 100644 --- a/python/plugins/processing/algs/qgis/Orthogonalize.py +++ b/python/plugins/processing/algs/qgis/Orthogonalize.py @@ -41,7 +41,7 @@ class Orthogonalize(QgisFeatureBasedAlgorithm): return self.tr('rectangle,perpendicular,right,angles,square,quadrilateralise').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointDistance.py b/python/plugins/processing/algs/qgis/PointDistance.py index 3301fd9e792..ccd71a995b2 100644 --- a/python/plugins/processing/algs/qgis/PointDistance.py +++ b/python/plugins/processing/algs/qgis/PointDistance.py @@ -69,7 +69,7 @@ class PointDistance(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'matrix.png')) def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointOnSurface.py b/python/plugins/processing/algs/qgis/PointOnSurface.py index ad62d884774..7f92aee2c4e 100644 --- a/python/plugins/processing/algs/qgis/PointOnSurface.py +++ b/python/plugins/processing/algs/qgis/PointOnSurface.py @@ -44,7 +44,7 @@ class PointOnSurface(QgisFeatureBasedAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'centroids.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointsAlongGeometry.py b/python/plugins/processing/algs/qgis/PointsAlongGeometry.py index fb1bede3b5e..886149fa68f 100644 --- a/python/plugins/processing/algs/qgis/PointsAlongGeometry.py +++ b/python/plugins/processing/algs/qgis/PointsAlongGeometry.py @@ -61,7 +61,7 @@ class PointsAlongGeometry(QgisAlgorithm): return self.tr('create,interpolate,points,lines').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointsDisplacement.py b/python/plugins/processing/algs/qgis/PointsDisplacement.py index d7c7e314fa4..fbbf5c515df 100644 --- a/python/plugins/processing/algs/qgis/PointsDisplacement.py +++ b/python/plugins/processing/algs/qgis/PointsDisplacement.py @@ -49,7 +49,7 @@ class PointsDisplacement(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointsFromLines.py b/python/plugins/processing/algs/qgis/PointsFromLines.py index 99c7c0f7370..c4bf9f5dc17 100644 --- a/python/plugins/processing/algs/qgis/PointsFromLines.py +++ b/python/plugins/processing/algs/qgis/PointsFromLines.py @@ -54,7 +54,7 @@ class PointsFromLines(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointsFromPolygons.py b/python/plugins/processing/algs/qgis/PointsFromPolygons.py index d0b0d5c5330..513d4016d3a 100644 --- a/python/plugins/processing/algs/qgis/PointsFromPolygons.py +++ b/python/plugins/processing/algs/qgis/PointsFromPolygons.py @@ -54,7 +54,7 @@ class PointsFromPolygons(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointsInPolygon.py b/python/plugins/processing/algs/qgis/PointsInPolygon.py index d94cf9d72df..d43d0db7760 100644 --- a/python/plugins/processing/algs/qgis/PointsInPolygon.py +++ b/python/plugins/processing/algs/qgis/PointsInPolygon.py @@ -59,7 +59,7 @@ class PointsInPolygon(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'sum_points.png')) def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointsLayerFromTable.py b/python/plugins/processing/algs/qgis/PointsLayerFromTable.py index 36dbea8dcc4..ace996efa68 100644 --- a/python/plugins/processing/algs/qgis/PointsLayerFromTable.py +++ b/python/plugins/processing/algs/qgis/PointsLayerFromTable.py @@ -52,7 +52,7 @@ class PointsLayerFromTable(QgisAlgorithm): return self.tr('points,create,values,attributes').split(',') def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PointsToPaths.py b/python/plugins/processing/algs/qgis/PointsToPaths.py index df2105b33c0..85a2f180741 100644 --- a/python/plugins/processing/algs/qgis/PointsToPaths.py +++ b/python/plugins/processing/algs/qgis/PointsToPaths.py @@ -59,7 +59,7 @@ class PointsToPaths(QgisAlgorithm): OUTPUT_TEXT_DIR = 'OUTPUT_TEXT_DIR' def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py b/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py index 3fb46ddfbde..c494887244e 100644 --- a/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py +++ b/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py @@ -58,7 +58,7 @@ class PoleOfInaccessibility(QgisAlgorithm): return self.tr('furthest,point,distant,extreme,maximum,centroid,center,centre').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Polygonize.py b/python/plugins/processing/algs/qgis/Polygonize.py index 96dd3f53df8..8f93b754d5e 100644 --- a/python/plugins/processing/algs/qgis/Polygonize.py +++ b/python/plugins/processing/algs/qgis/Polygonize.py @@ -48,7 +48,7 @@ class Polygonize(QgisAlgorithm): return self.tr('create,lines,polygons,convert').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/PolygonsToLines.py b/python/plugins/processing/algs/qgis/PolygonsToLines.py index c8e1ef96dfd..d07a1bed3d6 100644 --- a/python/plugins/processing/algs/qgis/PolygonsToLines.py +++ b/python/plugins/processing/algs/qgis/PolygonsToLines.py @@ -50,7 +50,7 @@ class PolygonsToLines(QgisFeatureBasedAlgorithm): return self.tr('line,polygon,convert').split(',') def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomExtract.py b/python/plugins/processing/algs/qgis/RandomExtract.py index 4592d922379..4768e5769b7 100644 --- a/python/plugins/processing/algs/qgis/RandomExtract.py +++ b/python/plugins/processing/algs/qgis/RandomExtract.py @@ -45,7 +45,7 @@ class RandomExtract(QgisAlgorithm): NUMBER = 'NUMBER' def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomExtractWithinSubsets.py b/python/plugins/processing/algs/qgis/RandomExtractWithinSubsets.py index a42a90db470..9329349cbc3 100644 --- a/python/plugins/processing/algs/qgis/RandomExtractWithinSubsets.py +++ b/python/plugins/processing/algs/qgis/RandomExtractWithinSubsets.py @@ -48,7 +48,7 @@ class RandomExtractWithinSubsets(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py b/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py index 78fb7df883f..3648465cd6c 100644 --- a/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py +++ b/python/plugins/processing/algs/qgis/RandomPointsAlongLines.py @@ -59,7 +59,7 @@ class RandomPointsAlongLines(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomPointsExtent.py b/python/plugins/processing/algs/qgis/RandomPointsExtent.py index 6f26bc5b881..b62b5974c4c 100644 --- a/python/plugins/processing/algs/qgis/RandomPointsExtent.py +++ b/python/plugins/processing/algs/qgis/RandomPointsExtent.py @@ -65,7 +65,7 @@ class RandomPointsExtent(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_points.png')) def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomPointsLayer.py b/python/plugins/processing/algs/qgis/RandomPointsLayer.py index a16fc8235b7..61f3a032b7e 100644 --- a/python/plugins/processing/algs/qgis/RandomPointsLayer.py +++ b/python/plugins/processing/algs/qgis/RandomPointsLayer.py @@ -63,7 +63,7 @@ class RandomPointsLayer(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_points.png')) def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomPointsPolygons.py b/python/plugins/processing/algs/qgis/RandomPointsPolygons.py index 2cd6187a7f5..a264f8f4df0 100644 --- a/python/plugins/processing/algs/qgis/RandomPointsPolygons.py +++ b/python/plugins/processing/algs/qgis/RandomPointsPolygons.py @@ -69,7 +69,7 @@ class RandomPointsPolygons(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_points.png')) def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomSelection.py b/python/plugins/processing/algs/qgis/RandomSelection.py index 31af20a29f8..aafdb7d1115 100644 --- a/python/plugins/processing/algs/qgis/RandomSelection.py +++ b/python/plugins/processing/algs/qgis/RandomSelection.py @@ -55,7 +55,7 @@ class RandomSelection(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'random_selection.png')) def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RandomSelectionWithinSubsets.py b/python/plugins/processing/algs/qgis/RandomSelectionWithinSubsets.py index c72390a4976..0f1b3c371d2 100644 --- a/python/plugins/processing/algs/qgis/RandomSelectionWithinSubsets.py +++ b/python/plugins/processing/algs/qgis/RandomSelectionWithinSubsets.py @@ -58,7 +58,7 @@ class RandomSelectionWithinSubsets(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'sub_selection.png')) def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RasterCalculator.py b/python/plugins/processing/algs/qgis/RasterCalculator.py index 6944729e500..c4bf2207d13 100644 --- a/python/plugins/processing/algs/qgis/RasterCalculator.py +++ b/python/plugins/processing/algs/qgis/RasterCalculator.py @@ -52,7 +52,7 @@ class RasterCalculator(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Raster') + return self.tr('Raster analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RasterLayerStatistics.py b/python/plugins/processing/algs/qgis/RasterLayerStatistics.py index f0a454fcc93..abc275596b8 100644 --- a/python/plugins/processing/algs/qgis/RasterLayerStatistics.py +++ b/python/plugins/processing/algs/qgis/RasterLayerStatistics.py @@ -52,7 +52,7 @@ class RasterLayerStatistics(QgisAlgorithm): SUM_OF_SQUARES = 'SUM_OF_SQUARES' def group(self): - return self.tr('Raster tools') + return self.tr('Raster analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py index ba69bde8057..f680c8a7328 100644 --- a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py +++ b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsFixed.py @@ -53,7 +53,7 @@ class RectanglesOvalsDiamondsFixed(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py index f90d2b80c95..e59292ec65c 100644 --- a/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py +++ b/python/plugins/processing/algs/qgis/RectanglesOvalsDiamondsVariable.py @@ -54,7 +54,7 @@ class RectanglesOvalsDiamondsVariable(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/RegularPoints.py b/python/plugins/processing/algs/qgis/RegularPoints.py index 4cab9956c8c..66f608aa416 100644 --- a/python/plugins/processing/algs/qgis/RegularPoints.py +++ b/python/plugins/processing/algs/qgis/RegularPoints.py @@ -65,7 +65,7 @@ class RegularPoints(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'regular_points.png')) def group(self): - return self.tr('Vector creation tools') + return self.tr('Vector creation') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ReverseLineDirection.py b/python/plugins/processing/algs/qgis/ReverseLineDirection.py index 9a6e901ce1d..bdde660acd4 100644 --- a/python/plugins/processing/algs/qgis/ReverseLineDirection.py +++ b/python/plugins/processing/algs/qgis/ReverseLineDirection.py @@ -34,7 +34,7 @@ from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm class ReverseLineDirection(QgisFeatureBasedAlgorithm): def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SaveSelectedFeatures.py b/python/plugins/processing/algs/qgis/SaveSelectedFeatures.py index 5880191c193..9104ca16108 100644 --- a/python/plugins/processing/algs/qgis/SaveSelectedFeatures.py +++ b/python/plugins/processing/algs/qgis/SaveSelectedFeatures.py @@ -37,7 +37,7 @@ class SaveSelectedFeatures(QgisAlgorithm): INPUT = 'INPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SelectByAttribute.py b/python/plugins/processing/algs/qgis/SelectByAttribute.py index 4c9ca1f84f0..d6821129d75 100644 --- a/python/plugins/processing/algs/qgis/SelectByAttribute.py +++ b/python/plugins/processing/algs/qgis/SelectByAttribute.py @@ -63,7 +63,7 @@ class SelectByAttribute(QgisAlgorithm): return self.tr('select,attribute,value,contains,null,field').split(',') def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SelectByAttributeSum.py b/python/plugins/processing/algs/qgis/SelectByAttributeSum.py index 141b3bc6f7f..7552b89d0da 100644 --- a/python/plugins/processing/algs/qgis/SelectByAttributeSum.py +++ b/python/plugins/processing/algs/qgis/SelectByAttributeSum.py @@ -45,7 +45,7 @@ class SelectByAttributeSum(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SelectByExpression.py b/python/plugins/processing/algs/qgis/SelectByExpression.py index 7554938a16a..3ec16ab0b8c 100644 --- a/python/plugins/processing/algs/qgis/SelectByExpression.py +++ b/python/plugins/processing/algs/qgis/SelectByExpression.py @@ -42,7 +42,7 @@ class SelectByExpression(QgisAlgorithm): METHOD = 'METHOD' def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SelectByLocation.py b/python/plugins/processing/algs/qgis/SelectByLocation.py index 1864ed16e54..a6e71ac5133 100644 --- a/python/plugins/processing/algs/qgis/SelectByLocation.py +++ b/python/plugins/processing/algs/qgis/SelectByLocation.py @@ -54,7 +54,7 @@ class SelectByLocation(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'select_location.png')) def group(self): - return self.tr('Vector selection tools') + return self.tr('Vector selection') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SetMValue.py b/python/plugins/processing/algs/qgis/SetMValue.py index 54558059af1..06fb1a21691 100644 --- a/python/plugins/processing/algs/qgis/SetMValue.py +++ b/python/plugins/processing/algs/qgis/SetMValue.py @@ -42,7 +42,7 @@ class SetMValue(QgisFeatureBasedAlgorithm): M_VALUE = 'M_VALUE' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SetRasterStyle.py b/python/plugins/processing/algs/qgis/SetRasterStyle.py index 1ad3662e2f3..26cf2ddfa1f 100644 --- a/python/plugins/processing/algs/qgis/SetRasterStyle.py +++ b/python/plugins/processing/algs/qgis/SetRasterStyle.py @@ -42,7 +42,7 @@ class SetRasterStyle(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Raster general tools') + return self.tr('Raster tools') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SetVectorStyle.py b/python/plugins/processing/algs/qgis/SetVectorStyle.py index 1f37eb56117..a230e59195e 100644 --- a/python/plugins/processing/algs/qgis/SetVectorStyle.py +++ b/python/plugins/processing/algs/qgis/SetVectorStyle.py @@ -38,7 +38,7 @@ class SetVectorStyle(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SetZValue.py b/python/plugins/processing/algs/qgis/SetZValue.py index b93a08a1b9d..2ab37651df7 100644 --- a/python/plugins/processing/algs/qgis/SetZValue.py +++ b/python/plugins/processing/algs/qgis/SetZValue.py @@ -42,7 +42,7 @@ class SetZValue(QgisFeatureBasedAlgorithm): Z_VALUE = 'Z_VALUE' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SimplifyGeometries.py b/python/plugins/processing/algs/qgis/SimplifyGeometries.py index 8dec5872498..7080579af01 100644 --- a/python/plugins/processing/algs/qgis/SimplifyGeometries.py +++ b/python/plugins/processing/algs/qgis/SimplifyGeometries.py @@ -47,7 +47,7 @@ class SimplifyGeometries(QgisFeatureBasedAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'simplify.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py b/python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py index 1d4f86a740b..b9ce604d8f1 100644 --- a/python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py +++ b/python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py @@ -55,7 +55,7 @@ class SinglePartsToMultiparts(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'single_to_multi.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SingleSidedBuffer.py b/python/plugins/processing/algs/qgis/SingleSidedBuffer.py index 929392fcff9..806e09888e9 100644 --- a/python/plugins/processing/algs/qgis/SingleSidedBuffer.py +++ b/python/plugins/processing/algs/qgis/SingleSidedBuffer.py @@ -43,7 +43,7 @@ class SingleSidedBuffer(QgisFeatureBasedAlgorithm): MITER_LIMIT = 'MITER_LIMIT' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Smooth.py b/python/plugins/processing/algs/qgis/Smooth.py index 7abcb46f453..324b9fa3e50 100644 --- a/python/plugins/processing/algs/qgis/Smooth.py +++ b/python/plugins/processing/algs/qgis/Smooth.py @@ -38,7 +38,7 @@ class Smooth(QgisFeatureBasedAlgorithm): OFFSET = 'OFFSET' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SnapGeometries.py b/python/plugins/processing/algs/qgis/SnapGeometries.py index 6244abb784b..20e3b751532 100644 --- a/python/plugins/processing/algs/qgis/SnapGeometries.py +++ b/python/plugins/processing/algs/qgis/SnapGeometries.py @@ -46,7 +46,7 @@ class SnapGeometriesToLayer(QgisAlgorithm): BEHAVIOR = 'BEHAVIOR' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SpatialIndex.py b/python/plugins/processing/algs/qgis/SpatialIndex.py index 7662e28f440..38f393b8c69 100644 --- a/python/plugins/processing/algs/qgis/SpatialIndex.py +++ b/python/plugins/processing/algs/qgis/SpatialIndex.py @@ -43,7 +43,7 @@ class SpatialIndex(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SpatialJoin.py b/python/plugins/processing/algs/qgis/SpatialJoin.py index 2d5c087489a..02c290dee68 100644 --- a/python/plugins/processing/algs/qgis/SpatialJoin.py +++ b/python/plugins/processing/algs/qgis/SpatialJoin.py @@ -60,7 +60,7 @@ class SpatialJoin(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'join_location.png')) def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SplitWithLines.py b/python/plugins/processing/algs/qgis/SplitWithLines.py index 28160ea0641..f0dd4eac8bb 100644 --- a/python/plugins/processing/algs/qgis/SplitWithLines.py +++ b/python/plugins/processing/algs/qgis/SplitWithLines.py @@ -47,7 +47,7 @@ class SplitWithLines(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector overlay tools') + return self.tr('Vector overlay') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/StatisticsByCategories.py b/python/plugins/processing/algs/qgis/StatisticsByCategories.py index 8257d67c53c..dfe0097f06f 100644 --- a/python/plugins/processing/algs/qgis/StatisticsByCategories.py +++ b/python/plugins/processing/algs/qgis/StatisticsByCategories.py @@ -49,7 +49,7 @@ class StatisticsByCategories(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector table tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SumLines.py b/python/plugins/processing/algs/qgis/SumLines.py index 0d78aa9cada..8d9fcc8a468 100644 --- a/python/plugins/processing/algs/qgis/SumLines.py +++ b/python/plugins/processing/algs/qgis/SumLines.py @@ -59,7 +59,7 @@ class SumLines(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'sum_lines.png')) def group(self): - return self.tr('Vector analysis tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/SymmetricalDifference.py b/python/plugins/processing/algs/qgis/SymmetricalDifference.py index 49b525ee79d..63078591745 100644 --- a/python/plugins/processing/algs/qgis/SymmetricalDifference.py +++ b/python/plugins/processing/algs/qgis/SymmetricalDifference.py @@ -55,7 +55,7 @@ class SymmetricalDifference(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'sym_difference.png')) def group(self): - return self.tr('Vector overlay tools') + return self.tr('Vector overlay') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/TextToFloat.py b/python/plugins/processing/algs/qgis/TextToFloat.py index 266f663fcdb..d62d9db2a34 100644 --- a/python/plugins/processing/algs/qgis/TextToFloat.py +++ b/python/plugins/processing/algs/qgis/TextToFloat.py @@ -36,7 +36,7 @@ class TextToFloat(QgisFeatureBasedAlgorithm): FIELD = 'FIELD' def group(self): - return self.tr('Vector table tools') + return self.tr('Vector table') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/TopoColors.py b/python/plugins/processing/algs/qgis/TopoColors.py index cc9b8671a76..32f2bc20d53 100644 --- a/python/plugins/processing/algs/qgis/TopoColors.py +++ b/python/plugins/processing/algs/qgis/TopoColors.py @@ -61,7 +61,7 @@ class TopoColor(QgisAlgorithm): return self.tr('topocolor,colors,graph,adjacent,assign').split(',') def group(self): - return self.tr('Cartographic tools') + return self.tr('Cartography') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Translate.py b/python/plugins/processing/algs/qgis/Translate.py index 96b5ac99fba..7a1f9af6c4d 100644 --- a/python/plugins/processing/algs/qgis/Translate.py +++ b/python/plugins/processing/algs/qgis/Translate.py @@ -36,7 +36,7 @@ class Translate(QgisFeatureBasedAlgorithm): DELTA_Y = 'DELTA_Y' def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/TruncateTable.py b/python/plugins/processing/algs/qgis/TruncateTable.py index 9752340e9b5..cda6de6afb1 100644 --- a/python/plugins/processing/algs/qgis/TruncateTable.py +++ b/python/plugins/processing/algs/qgis/TruncateTable.py @@ -40,7 +40,7 @@ class TruncateTable(QgisAlgorithm): return self.tr('empty,delete,layer,clear,features').split(',') def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/Union.py b/python/plugins/processing/algs/qgis/Union.py index c6628693bfb..ee4aa815037 100644 --- a/python/plugins/processing/algs/qgis/Union.py +++ b/python/plugins/processing/algs/qgis/Union.py @@ -64,7 +64,7 @@ class Union(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'union.png')) def group(self): - return self.tr('Vector overlay tools') + return self.tr('Vector overlay') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/UniqueValues.py b/python/plugins/processing/algs/qgis/UniqueValues.py index 265cb8a7c1f..4ad8c7e4ae8 100644 --- a/python/plugins/processing/algs/qgis/UniqueValues.py +++ b/python/plugins/processing/algs/qgis/UniqueValues.py @@ -62,7 +62,7 @@ class UniqueValues(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'unique.png')) def group(self): - return self.tr('Vector table tools') + return self.tr('Vector analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/VariableDistanceBuffer.py b/python/plugins/processing/algs/qgis/VariableDistanceBuffer.py index 3b757c644db..aa888e98ffc 100644 --- a/python/plugins/processing/algs/qgis/VariableDistanceBuffer.py +++ b/python/plugins/processing/algs/qgis/VariableDistanceBuffer.py @@ -59,7 +59,7 @@ class VariableDistanceBuffer(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'buffer.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/VectorSplit.py b/python/plugins/processing/algs/qgis/VectorSplit.py index 8156c53ff9b..b6014117d5d 100644 --- a/python/plugins/processing/algs/qgis/VectorSplit.py +++ b/python/plugins/processing/algs/qgis/VectorSplit.py @@ -50,7 +50,7 @@ class VectorSplit(QgisAlgorithm): OUTPUT = 'OUTPUT' def group(self): - return self.tr('Vector general tools') + return self.tr('Vector general') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/VoronoiPolygons.py b/python/plugins/processing/algs/qgis/VoronoiPolygons.py index 3b979f324f9..c3c08a167f3 100644 --- a/python/plugins/processing/algs/qgis/VoronoiPolygons.py +++ b/python/plugins/processing/algs/qgis/VoronoiPolygons.py @@ -59,7 +59,7 @@ class VoronoiPolygons(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'voronoi.png')) def group(self): - return self.tr('Vector geometry tools') + return self.tr('Vector geometry') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/ZonalStatistics.py b/python/plugins/processing/algs/qgis/ZonalStatistics.py index e22130796ac..e82794971fd 100644 --- a/python/plugins/processing/algs/qgis/ZonalStatistics.py +++ b/python/plugins/processing/algs/qgis/ZonalStatistics.py @@ -56,7 +56,7 @@ class ZonalStatistics(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'zonalstats.png')) def group(self): - return self.tr('Raster tools') + return self.tr('Raster analysis') def __init__(self): super().__init__() diff --git a/python/plugins/processing/algs/qgis/scripts/Frequency_analysis.py b/python/plugins/processing/algs/qgis/scripts/Frequency_analysis.py index 3d341a4a434..e6c1885ee33 100644 --- a/python/plugins/processing/algs/qgis/scripts/Frequency_analysis.py +++ b/python/plugins/processing/algs/qgis/scripts/Frequency_analysis.py @@ -1,4 +1,4 @@ -##Table=group +##Vector analysis=group #inputs diff --git a/python/plugins/processing/algs/qgis/scripts/Keep_n_biggest_parts.py b/python/plugins/processing/algs/qgis/scripts/Keep_n_biggest_parts.py index e764a3465bb..f2111c3a889 100644 --- a/python/plugins/processing/algs/qgis/scripts/Keep_n_biggest_parts.py +++ b/python/plugins/processing/algs/qgis/scripts/Keep_n_biggest_parts.py @@ -1,5 +1,5 @@ from builtins import range -##Vector geometry tools=group +##Vector geometry=group #inputs diff --git a/python/plugins/processing/algs/qgis/scripts/Number_of_unique_values_in_classes.py b/python/plugins/processing/algs/qgis/scripts/Number_of_unique_values_in_classes.py index 925fe8f5a34..5335c66e3f9 100644 --- a/python/plugins/processing/algs/qgis/scripts/Number_of_unique_values_in_classes.py +++ b/python/plugins/processing/algs/qgis/scripts/Number_of_unique_values_in_classes.py @@ -1,4 +1,4 @@ -##Vector table tools=group +##Vector analysis=group # inputs diff --git a/python/plugins/processing/algs/saga/SagaNameDecorator.py b/python/plugins/processing/algs/saga/SagaNameDecorator.py index 83ba4b03b35..efb1568184e 100644 --- a/python/plugins/processing/algs/saga/SagaNameDecorator.py +++ b/python/plugins/processing/algs/saga/SagaNameDecorator.py @@ -63,8 +63,8 @@ groups = {'grid_analysis': 'Raster analysis', 'shapes_lines': 'Vector line tools', 'shapes_points': 'Vector point tools', 'shapes_polygons': 'Vector polygon tools', - 'shapes_tools': 'Vector general tools', - 'shapes_transect': 'Vector general tools', + 'shapes_tools': 'Vector general', + 'shapes_transect': 'Vector general', 'sim_cellular_automata': 'Simulation', 'sim_ecosystems_hugget': 'Simulation', 'sim_fire_spreading': 'Simulation', diff --git a/src/core/processing/qgsnativealgorithms.h b/src/core/processing/qgsnativealgorithms.h index d849d10524e..7c599f7a57c 100644 --- a/src/core/processing/qgsnativealgorithms.h +++ b/src/core/processing/qgsnativealgorithms.h @@ -58,7 +58,7 @@ class QgsCentroidAlgorithm : public QgsProcessingFeatureBasedAlgorithm QString name() const override { return QStringLiteral( "centroids" ); } QString displayName() const override { return QObject::tr( "Centroids" ); } QStringList tags() const override { return QObject::tr( "centroid,center,average,point,middle" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector geometry tools" ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } QString shortHelpString() const override; QgsCentroidAlgorithm *createInstance() const override SIP_FACTORY; @@ -83,7 +83,7 @@ class QgsTransformAlgorithm : public QgsProcessingFeatureBasedAlgorithm QString name() const override { return QStringLiteral( "reprojectlayer" ); } QString displayName() const override { return QObject::tr( "Reproject layer" ); } virtual QStringList tags() const override { return QObject::tr( "transform,reproject,crs,srs,warp" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector general tools" ); } + QString group() const override { return QObject::tr( "Vector general" ); } QString shortHelpString() const override; QgsTransformAlgorithm *createInstance() const override SIP_FACTORY; @@ -118,7 +118,7 @@ class QgsBufferAlgorithm : public QgsProcessingAlgorithm QString name() const override { return QStringLiteral( "buffer" ); } QString displayName() const override { return QObject::tr( "Buffer" ); } virtual QStringList tags() const override { return QObject::tr( "buffer,grow" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector geometry tools" ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } QString shortHelpString() const override; QgsBufferAlgorithm *createInstance() const override SIP_FACTORY; @@ -142,7 +142,7 @@ class QgsDissolveAlgorithm : public QgsProcessingAlgorithm QString name() const override { return QStringLiteral( "dissolve" ); } QString displayName() const override { return QObject::tr( "Dissolve" ); } virtual QStringList tags() const override { return QObject::tr( "dissolve,union,combine,collect" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector geometry tools" ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } QString shortHelpString() const override; QgsDissolveAlgorithm *createInstance() const override SIP_FACTORY; @@ -181,7 +181,7 @@ class QgsExtractByAttributeAlgorithm : public QgsProcessingAlgorithm QString name() const override { return QStringLiteral( "extractbyattribute" ); } QString displayName() const override { return QObject::tr( "Extract by attribute" ); } virtual QStringList tags() const override { return QObject::tr( "extract,filter,attribute,value,contains,null,field" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector selection tools" ); } + QString group() const override { return QObject::tr( "Vector selection" ); } QString shortHelpString() const override; QgsExtractByAttributeAlgorithm *createInstance() const override SIP_FACTORY; @@ -205,7 +205,7 @@ class QgsExtractByExpressionAlgorithm : public QgsProcessingAlgorithm QString name() const override { return QStringLiteral( "extractbyexpression" ); } QString displayName() const override { return QObject::tr( "Extract by expression" ); } virtual QStringList tags() const override { return QObject::tr( "extract,filter,expression,field" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector selection tools" ); } + QString group() const override { return QObject::tr( "Vector selection" ); } QString shortHelpString() const override; QgsExtractByExpressionAlgorithm *createInstance() const override SIP_FACTORY; @@ -229,7 +229,7 @@ class QgsClipAlgorithm : public QgsProcessingAlgorithm QString name() const override { return QStringLiteral( "clip" ); } QString displayName() const override { return QObject::tr( "Clip" ); } virtual QStringList tags() const override { return QObject::tr( "clip,intersect,intersection,mask" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector overlay tools" ); } + QString group() const override { return QObject::tr( "Vector overlay" ); } QString shortHelpString() const override; QgsClipAlgorithm *createInstance() const override SIP_FACTORY; @@ -254,7 +254,7 @@ class QgsSubdivideAlgorithm : public QgsProcessingFeatureBasedAlgorithm QString name() const override { return QStringLiteral( "subdivide" ); } QString displayName() const override { return QObject::tr( "Subdivide" ); } virtual QStringList tags() const override { return QObject::tr( "subdivide,segmentize,split,tesselate" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector geometry tools" ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } QString shortHelpString() const override; QgsSubdivideAlgorithm *createInstance() const override SIP_FACTORY; @@ -285,7 +285,7 @@ class QgsMultipartToSinglepartAlgorithm : public QgsProcessingAlgorithm QString name() const override { return QStringLiteral( "multiparttosingleparts" ); } QString displayName() const override { return QObject::tr( "Multipart to singleparts" ); } virtual QStringList tags() const override { return QObject::tr( "multi,single,multiple,split,dump" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector geometry tools" ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } QString shortHelpString() const override; QgsMultipartToSinglepartAlgorithm *createInstance() const override SIP_FACTORY; @@ -310,7 +310,7 @@ class QgsRemoveNullGeometryAlgorithm : public QgsProcessingAlgorithm QString name() const override { return QStringLiteral( "removenullgeometries" ); } QString displayName() const override { return QObject::tr( "Remove null geometries" ); } virtual QStringList tags() const override { return QObject::tr( "remove,drop,delete,empty,geometry" ).split( ',' ); } - QString group() const override { return QObject::tr( "Vector selection tools" ); } + QString group() const override { return QObject::tr( "Vector selection" ); } QString shortHelpString() const override; QgsRemoveNullGeometryAlgorithm *createInstance() const override SIP_FACTORY; From 9882e4f7bb91df665534330e48c0570172eb396f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 22 Aug 2017 23:40:11 +1000 Subject: [PATCH 088/364] Fix some inconsistent capitalization in algorithm naming --- python/plugins/processing/algs/qgis/DropMZValues.py | 2 +- python/plugins/processing/algs/qgis/ExportGeometryInfo.py | 4 ++-- python/plugins/processing/algs/qgis/PoleOfInaccessibility.py | 2 +- python/plugins/processing/algs/qgis/SetMValue.py | 2 +- python/plugins/processing/algs/qgis/SetZValue.py | 2 +- python/plugins/processing/algs/qgis/ZonalStatistics.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/python/plugins/processing/algs/qgis/DropMZValues.py b/python/plugins/processing/algs/qgis/DropMZValues.py index 0078fe699da..be69f27a11d 100644 --- a/python/plugins/processing/algs/qgis/DropMZValues.py +++ b/python/plugins/processing/algs/qgis/DropMZValues.py @@ -54,7 +54,7 @@ class DropMZValues(QgisFeatureBasedAlgorithm): return 'dropmzvalues' def displayName(self): - return self.tr('Drop M/Z Values') + return self.tr('Drop M/Z values') def outputName(self): return self.tr('Z/M Dropped') diff --git a/python/plugins/processing/algs/qgis/ExportGeometryInfo.py b/python/plugins/processing/algs/qgis/ExportGeometryInfo.py index 0d5d65a2933..eec01d2094a 100644 --- a/python/plugins/processing/algs/qgis/ExportGeometryInfo.py +++ b/python/plugins/processing/algs/qgis/ExportGeometryInfo.py @@ -55,7 +55,7 @@ class ExportGeometryInfo(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'export_geometry.png')) def tags(self): - return self.tr('export,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons').split(',') + return self.tr('export,add,information,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons').split(',') def group(self): return self.tr('Vector geometry') @@ -80,7 +80,7 @@ class ExportGeometryInfo(QgisAlgorithm): return 'exportaddgeometrycolumns' def displayName(self): - return self.tr('Export/Add geometry columns') + return self.tr('Export geometry columns') def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) diff --git a/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py b/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py index c494887244e..65a2959bee3 100644 --- a/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py +++ b/python/plugins/processing/algs/qgis/PoleOfInaccessibility.py @@ -78,7 +78,7 @@ class PoleOfInaccessibility(QgisAlgorithm): return 'poleofinaccessibility' def displayName(self): - return self.tr('Pole of Inaccessibility') + return self.tr('Pole of inaccessibility') def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) diff --git a/python/plugins/processing/algs/qgis/SetMValue.py b/python/plugins/processing/algs/qgis/SetMValue.py index 06fb1a21691..5499622a274 100644 --- a/python/plugins/processing/algs/qgis/SetMValue.py +++ b/python/plugins/processing/algs/qgis/SetMValue.py @@ -52,7 +52,7 @@ class SetMValue(QgisFeatureBasedAlgorithm): return 'setmvalue' def displayName(self): - return self.tr('Set M Value') + return self.tr('Set M value') def outputName(self): return self.tr('M Added') diff --git a/python/plugins/processing/algs/qgis/SetZValue.py b/python/plugins/processing/algs/qgis/SetZValue.py index 2ab37651df7..81c7a9198ba 100644 --- a/python/plugins/processing/algs/qgis/SetZValue.py +++ b/python/plugins/processing/algs/qgis/SetZValue.py @@ -52,7 +52,7 @@ class SetZValue(QgisFeatureBasedAlgorithm): return 'setzvalue' def displayName(self): - return self.tr('Set Z Value') + return self.tr('Set Z value') def outputName(self): return self.tr('Z Added') diff --git a/python/plugins/processing/algs/qgis/ZonalStatistics.py b/python/plugins/processing/algs/qgis/ZonalStatistics.py index e82794971fd..dd3557e538a 100644 --- a/python/plugins/processing/algs/qgis/ZonalStatistics.py +++ b/python/plugins/processing/algs/qgis/ZonalStatistics.py @@ -106,7 +106,7 @@ class ZonalStatistics(QgisAlgorithm): return 'zonalstatistics' def displayName(self): - return self.tr('Zonal Statistics') + return self.tr('Zonal statistics') def prepareAlgorithm(self, parameters, context, feedback): self.bandNumber = self.parameterAsInt(parameters, self.RASTER_BAND, context) From c119c286eb06d674cfcbb31758705880d9d4199d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 00:13:15 +1000 Subject: [PATCH 089/364] Add basic unit tests for constructing processing widget wrappers --- .../processing/gui/ExtentSelectionPanel.py | 12 ++- .../processing/gui/PointSelectionPanel.py | 12 ++- python/plugins/processing/tests/GuiTest.py | 79 +++++++++++++++++++ 3 files changed, 95 insertions(+), 8 deletions(-) diff --git a/python/plugins/processing/gui/ExtentSelectionPanel.py b/python/plugins/processing/gui/ExtentSelectionPanel.py index 0a543e89fd4..0589f73bc14 100644 --- a/python/plugins/processing/gui/ExtentSelectionPanel.py +++ b/python/plugins/processing/gui/ExtentSelectionPanel.py @@ -61,10 +61,14 @@ class ExtentSelectionPanel(BASE, WIDGET): self.btnSelect.clicked.connect(self.selectExtent) - canvas = iface.mapCanvas() - self.prevMapTool = canvas.mapTool() - self.tool = RectangleMapTool(canvas) - self.tool.rectangleCreated.connect(self.updateExtent) + if iface is not None: + canvas = iface.mapCanvas() + self.prevMapTool = canvas.mapTool() + self.tool = RectangleMapTool(canvas) + self.tool.rectangleCreated.connect(self.updateExtent) + else: + self.prevMapTool = None + self.tool = None if param.defaultValue() is not None: context = createContext() diff --git a/python/plugins/processing/gui/PointSelectionPanel.py b/python/plugins/processing/gui/PointSelectionPanel.py index 06c88a07575..798a3ffab32 100644 --- a/python/plugins/processing/gui/PointSelectionPanel.py +++ b/python/plugins/processing/gui/PointSelectionPanel.py @@ -49,11 +49,15 @@ class PointSelectionPanel(BASE, WIDGET): self.dialog = dialog - canvas = iface.mapCanvas() - self.prevMapTool = canvas.mapTool() + if iface is not None: + canvas = iface.mapCanvas() + self.prevMapTool = canvas.mapTool() - self.tool = PointMapTool(canvas) - self.tool.canvasClicked.connect(self.updatePoint) + self.tool = PointMapTool(canvas) + self.tool.canvasClicked.connect(self.updatePoint) + else: + self.prevMapTool = None + self.tool = None if default: tokens = str(default).split(',') diff --git a/python/plugins/processing/tests/GuiTest.py b/python/plugins/processing/tests/GuiTest.py index 04aeb005044..2408cff52d5 100644 --- a/python/plugins/processing/tests/GuiTest.py +++ b/python/plugins/processing/tests/GuiTest.py @@ -29,6 +29,9 @@ from qgis.testing import start_app, unittest from qgis.core import QgsApplication from processing.gui.AlgorithmDialog import AlgorithmDialog +from processing.gui.BatchAlgorithmDialog import BatchAlgorithmDialog +from processing.modeler.ModelerParametersDialog import ModelerParametersDialog +from processing.gui.wrappers import * start_app() @@ -41,5 +44,81 @@ class AlgorithmDialogTest(unittest.TestCase): self.assertEqual(a.mainWidget.alg, alg) +class WrappersTest(unittest.TestCase): + + def checkConstructWrapper(self, param, expected_wrapper_class): + alg = QgsApplication.processingRegistry().algorithmById('native:centroids') + + # algorithm dialog + dlg = AlgorithmDialog(alg) + wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg) + self.assertIsNotNone(wrapper) + self.assertIsInstance(wrapper, expected_wrapper_class) + self.assertEqual(wrapper.dialog, dlg) + self.assertIsNotNone(wrapper.widget) + + # batch dialog + dlg = BatchAlgorithmDialog(alg) + wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg) + self.assertIsNotNone(wrapper) + self.assertIsInstance(wrapper, expected_wrapper_class) + self.assertEqual(wrapper.dialog, dlg) + self.assertIsNotNone(wrapper.widget) + + # modeler dialog + model = QgsProcessingModelAlgorithm() + dlg = ModelerParametersDialog(alg, model) + wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg) + self.assertIsNotNone(wrapper) + self.assertIsInstance(wrapper, expected_wrapper_class) + self.assertEqual(wrapper.dialog, dlg) + self.assertIsNotNone(wrapper.widget) + + def testBoolean(self): + self.checkConstructWrapper(QgsProcessingParameterBoolean('test'), BooleanWidgetWrapper) + + def testCrs(self): + self.checkConstructWrapper(QgsProcessingParameterCrs('test'), CrsWidgetWrapper) + + def testExtent(self): + self.checkConstructWrapper(QgsProcessingParameterExtent('test'), ExtentWidgetWrapper) + + def testPoint(self): + self.checkConstructWrapper(QgsProcessingParameterPoint('test'), PointWidgetWrapper) + + def testFile(self): + self.checkConstructWrapper(QgsProcessingParameterFile('test'), FileWidgetWrapper) + + def testMultiInput(self): + self.checkConstructWrapper(QgsProcessingParameterMultipleLayers('test'), MultipleInputWidgetWrapper) + + def testRasterInput(self): + self.checkConstructWrapper(QgsProcessingParameterRasterLayer('test'), RasterWidgetWrapper) + + def testEnum(self): + self.checkConstructWrapper(QgsProcessingParameterEnum('test'), SelectionWidgetWrapper) + + def testString(self): + self.checkConstructWrapper(QgsProcessingParameterString('test'), StringWidgetWrapper) + + def testExpression(self): + self.checkConstructWrapper(QgsProcessingParameterExpression('test'), ExpressionWidgetWrapper) + + def testVector(self): + self.checkConstructWrapper(QgsProcessingParameterVectorLayer('test'), TableWidgetWrapper) + + def testField(self): + self.checkConstructWrapper(QgsProcessingParameterField('test'), TableFieldWidgetWrapper) + + def testSource(self): + self.checkConstructWrapper(QgsProcessingParameterFeatureSource('test'), VectorWidgetWrapper) + + def testSource(self): + self.checkConstructWrapper(QgsProcessingParameterBand('test'), BandWidgetWrapper) + + def testMapLayer(self): + self.checkConstructWrapper(QgsProcessingParameterMapLayer('test'), MapLayerWidgetWrapper) + + if __name__ == '__main__': unittest.main() From 367aba7059814db1cda9be5cd118ffb0eaf21f13 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 00:16:42 +1000 Subject: [PATCH 090/364] Rename some processing widget wrappers to better match corresponding c++ classes --- python/plugins/processing/gui/wrappers.py | 16 ++++++++-------- python/plugins/processing/tests/GuiTest.py | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 6d4afe46fa7..9dc99b55253 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -521,7 +521,7 @@ class FixedTableWidgetWrapper(WidgetWrapper): return self.widget.table -class MultipleInputWidgetWrapper(WidgetWrapper): +class MultipleLayerWidgetWrapper(WidgetWrapper): def _getOptions(self): if self.param.layerType() == QgsProcessing.TypeVectorAnyGeometry: @@ -766,7 +766,7 @@ class RasterWidgetWrapper(MapLayerWidgetWrapper): self.combo.setEditText(filename) -class SelectionWidgetWrapper(WidgetWrapper): +class EnumWidgetWrapper(WidgetWrapper): def createWidget(self): if self.param.allowMultiple(): @@ -792,7 +792,7 @@ class SelectionWidgetWrapper(WidgetWrapper): return self.widget.currentData() -class VectorWidgetWrapper(WidgetWrapper): +class FeatureSourceWidgetWrapper(WidgetWrapper): NOT_SELECTED = '[Not selected]' @@ -1082,7 +1082,7 @@ class ExpressionWidgetWrapper(WidgetWrapper): return self.comboValue(validator) -class TableWidgetWrapper(WidgetWrapper): +class VectorLayerWidgetWrapper(WidgetWrapper): NOT_SELECTED = '[Not selected]' @@ -1407,23 +1407,23 @@ class WidgetWrapperFactory: elif param.type() == 'file': wrapper = FileWidgetWrapper elif param.type() == 'multilayer': - wrapper = MultipleInputWidgetWrapper + wrapper = MultipleLayerWidgetWrapper elif param.type() == 'number': wrapper = NumberWidgetWrapper elif param.type() == 'raster': wrapper = RasterWidgetWrapper elif param.type() == 'enum': - wrapper = SelectionWidgetWrapper + wrapper = EnumWidgetWrapper elif param.type() == 'string': wrapper = StringWidgetWrapper elif param.type() == 'expression': wrapper = ExpressionWidgetWrapper elif param.type() == 'vector': - wrapper = TableWidgetWrapper + wrapper = VectorLayerWidgetWrapper elif param.type() == 'field': wrapper = TableFieldWidgetWrapper elif param.type() == 'source': - wrapper = VectorWidgetWrapper + wrapper = FeatureSourceWidgetWrapper elif param.type() == 'band': wrapper = BandWidgetWrapper elif param.type() == 'layer': diff --git a/python/plugins/processing/tests/GuiTest.py b/python/plugins/processing/tests/GuiTest.py index 2408cff52d5..d367f1c8edf 100644 --- a/python/plugins/processing/tests/GuiTest.py +++ b/python/plugins/processing/tests/GuiTest.py @@ -90,13 +90,13 @@ class WrappersTest(unittest.TestCase): self.checkConstructWrapper(QgsProcessingParameterFile('test'), FileWidgetWrapper) def testMultiInput(self): - self.checkConstructWrapper(QgsProcessingParameterMultipleLayers('test'), MultipleInputWidgetWrapper) + self.checkConstructWrapper(QgsProcessingParameterMultipleLayers('test'), MultipleLayerWidgetWrapper) def testRasterInput(self): self.checkConstructWrapper(QgsProcessingParameterRasterLayer('test'), RasterWidgetWrapper) def testEnum(self): - self.checkConstructWrapper(QgsProcessingParameterEnum('test'), SelectionWidgetWrapper) + self.checkConstructWrapper(QgsProcessingParameterEnum('test'), EnumWidgetWrapper) def testString(self): self.checkConstructWrapper(QgsProcessingParameterString('test'), StringWidgetWrapper) @@ -105,13 +105,13 @@ class WrappersTest(unittest.TestCase): self.checkConstructWrapper(QgsProcessingParameterExpression('test'), ExpressionWidgetWrapper) def testVector(self): - self.checkConstructWrapper(QgsProcessingParameterVectorLayer('test'), TableWidgetWrapper) + self.checkConstructWrapper(QgsProcessingParameterVectorLayer('test'), VectorLayerWidgetWrapper) def testField(self): self.checkConstructWrapper(QgsProcessingParameterField('test'), TableFieldWidgetWrapper) def testSource(self): - self.checkConstructWrapper(QgsProcessingParameterFeatureSource('test'), VectorWidgetWrapper) + self.checkConstructWrapper(QgsProcessingParameterFeatureSource('test'), FeatureSourceWidgetWrapper) def testSource(self): self.checkConstructWrapper(QgsProcessingParameterBand('test'), BandWidgetWrapper) From 35e49ed90a27b9285929ff4baebed0a66dd09911 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 22 Aug 2017 17:25:50 +0200 Subject: [PATCH 091/364] Bump required GDAL version from 2.0 to 2.1 Because we need GDALTranslate() --- INSTALL | 2 +- cmake/FindGDAL.cmake | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/INSTALL b/INSTALL index f0705bcea62..68f9f6606c7 100644 --- a/INSTALL +++ b/INSTALL @@ -102,7 +102,7 @@ Required build dependencies: - Sqlite3 >= 3.0.0 - SpatiaLite - libspatialindex -- GDAL/OGR >= 2.0 +- GDAL/OGR >= 2.1 - Qwt >= 5.0 & (< 6.1 with internal QwtPolar) - expat >= 1.95 - QScintilla2 diff --git a/cmake/FindGDAL.cmake b/cmake/FindGDAL.cmake index b4b90e4e1a9..3fc769c6026 100644 --- a/cmake/FindGDAL.cmake +++ b/cmake/FindGDAL.cmake @@ -62,8 +62,11 @@ ELSE(WIN32) STRING(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" GDAL_VERSION_MAJOR "${GDAL_VERSION}") STRING(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" GDAL_VERSION_MINOR "${GDAL_VERSION}") IF (GDAL_VERSION_MAJOR LESS 2) - MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.0 or higher.") + MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.") ENDIF (GDAL_VERSION_MAJOR LESS 2) + IF ( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) ) + MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.") + ENDIF( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) ) ENDIF (GDAL_LIBRARY) SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE) @@ -103,8 +106,11 @@ ELSE(WIN32) # version 1.2.5 is known NOT to be supported (missing CPL_STDCALL macro) # According to INSTALL, 2.0+ is required IF (GDAL_VERSION_MAJOR LESS 2) - MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.0 or higher.") + MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.") ENDIF (GDAL_VERSION_MAJOR LESS 2) + IF ( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) ) + MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.") + ENDIF( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) ) # set INCLUDE_DIR to prefix+include EXEC_PROGRAM(${GDAL_CONFIG} From 83b11d548461d160d00dab3ef75742a3d246a735 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 22 Aug 2017 17:28:38 +0200 Subject: [PATCH 092/364] Minor update in the comment --- cmake/FindGDAL.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindGDAL.cmake b/cmake/FindGDAL.cmake index 3fc769c6026..510c38b8ff3 100644 --- a/cmake/FindGDAL.cmake +++ b/cmake/FindGDAL.cmake @@ -104,7 +104,7 @@ ELSE(WIN32) # check for gdal version # version 1.2.5 is known NOT to be supported (missing CPL_STDCALL macro) - # According to INSTALL, 2.0+ is required + # According to INSTALL, 2.1+ is required IF (GDAL_VERSION_MAJOR LESS 2) MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.") ENDIF (GDAL_VERSION_MAJOR LESS 2) From cdbb57d65f95c67c227fd25cee98f5bd58be677c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 00:37:14 +1000 Subject: [PATCH 093/364] Don't throw python exception when modeler algorithm is missing inputs Instead use nicer messagebar for feedback. Also fix untranslatable strings. Refs #17028 --- .../processing/modeler/ModelerParametersDialog.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/modeler/ModelerParametersDialog.py b/python/plugins/processing/modeler/ModelerParametersDialog.py index 536b20ac79c..0afb38d889d 100644 --- a/python/plugins/processing/modeler/ModelerParametersDialog.py +++ b/python/plugins/processing/modeler/ModelerParametersDialog.py @@ -303,7 +303,13 @@ class ModelerParametersDialog(QDialog): for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue - val = self.wrappers[param.name()].value() + try: + val = self.wrappers[param.name()].value() + except InvalidParameterValue: + self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(param.description()), + level=QgsMessageBar.WARNING) + return None + if isinstance(val, QgsProcessingModelChildParameterSource): val = [val] elif not (isinstance(val, list) and all([isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])): @@ -313,7 +319,7 @@ class ModelerParametersDialog(QDialog): subval.source() == QgsProcessingModelChildParameterSource.StaticValue and not param.checkValueIsAcceptable(subval.staticValue())) \ or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): - self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description(), + self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(param.description()), level=QgsMessageBar.WARNING) return None alg.addParameterSources(param.name(), val) From 451a3fab26641ead7c95b62c2f49cb18d24d850c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 00:38:28 +1000 Subject: [PATCH 094/364] Reformat code --- .../modeler/ModelerParametersDialog.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/python/plugins/processing/modeler/ModelerParametersDialog.py b/python/plugins/processing/modeler/ModelerParametersDialog.py index 0afb38d889d..33b82a99fa3 100644 --- a/python/plugins/processing/modeler/ModelerParametersDialog.py +++ b/python/plugins/processing/modeler/ModelerParametersDialog.py @@ -244,8 +244,10 @@ class ModelerParametersDialog(QDialog): elif not isinstance(outTypes, (tuple, list)): outTypes = [outTypes] - return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition)], - [o.typeName() for o in outTypes if issubclass(o, QgsProcessingOutputDefinition)], dataTypes) + return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if + issubclass(p, QgsProcessingParameterDefinition)], + [o.typeName() for o in outTypes if + issubclass(o, QgsProcessingOutputDefinition)], dataTypes) def resolveValueDescription(self, value): if isinstance(value, QgsProcessingModelChildParameterSource): @@ -277,7 +279,8 @@ class ModelerParametersDialog(QDialog): if value is None: value = param.defaultValue() - if isinstance(value, QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue: + if isinstance(value, + QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue: value = value.staticValue() self.wrappers[param.name()].setValue(value) @@ -306,21 +309,24 @@ class ModelerParametersDialog(QDialog): try: val = self.wrappers[param.name()].value() except InvalidParameterValue: - self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(param.description()), + self.bar.pushMessage(self.tr("Error"), + self.tr("Wrong or missing value for parameter '{}'").format(param.description()), level=QgsMessageBar.WARNING) return None if isinstance(val, QgsProcessingModelChildParameterSource): val = [val] - elif not (isinstance(val, list) and all([isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])): + elif not (isinstance(val, list) and all( + [isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])): val = [QgsProcessingModelChildParameterSource.fromStaticValue(val)] for subval in val: if (isinstance(subval, QgsProcessingModelChildParameterSource) and subval.source() == QgsProcessingModelChildParameterSource.StaticValue and - not param.checkValueIsAcceptable(subval.staticValue())) \ + not param.checkValueIsAcceptable(subval.staticValue())) \ or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): - self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(param.description()), - level=QgsMessageBar.WARNING) + self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format( + param.description()), + level=QgsMessageBar.WARNING) return None alg.addParameterSources(param.name(), val) From 4511ea1c12f26ec21c7b24d32f11dffe9517908f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 00:51:42 +1000 Subject: [PATCH 095/364] Add a file selector for file parameters in model algorithms Makes it more obvious to users that a fixed filename can be used here --- python/plugins/processing/gui/wrappers.py | 45 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 9dc99b55253..e918c0b13cc 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -483,24 +483,59 @@ class FileWidgetWrapper(WidgetWrapper): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): return FileSelectionPanel(self.param.behavior() == QgsProcessingParameterFile.Folder, self.param.extension()) else: - widget = QComboBox() - widget.setEditable(True) + self.combo = QComboBox() + self.combo.setEditable(True) files = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFile, OutputFile) for f in files: - widget.addItem(self.dialog.resolveValueDescription(f), f) + self.combo.addItem(self.dialog.resolveValueDescription(f), f) + if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + self.combo.setEditText("") + widget = QWidget() + layout = QHBoxLayout() + layout.setMargin(0) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(2) + layout.addWidget(self.combo) + btn = QToolButton() + btn.setText('…') + btn.setToolTip(self.tr("Select file")) + btn.clicked.connect(self.selectFile) + layout.addWidget(btn) + widget.setLayout(layout) return widget + def selectFile(self): + settings = QgsSettings() + if os.path.isdir(os.path.dirname(self.combo.currentText())): + path = os.path.dirname(self.combo.currentText()) + if settings.contains('/Processing/LastInputPath'): + path = settings.value('/Processing/LastInputPath') + else: + path = '' + + if self.param.extension(): + filter = self.tr('{} files').format(self.param.extension().upper()) + ' (*.' + self.param.extension() + self.tr( + ');;All files (*.*)') + else: + filter = self.tr('All files (*.*)') + + filename, selected_filter = QFileDialog.getOpenFileName(self.widget, + self.tr('Select file'), path, + filter) + if filename: + self.combo.setEditText(filename) + def setValue(self, value): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): self.widget.setText(value) else: - self.setComboValue(value) + self.setComboValue(value, combobox=self.combo) def value(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): return self.widget.getValue() else: - return self.comboValue() + return self.comboValue(combobox=self.combo) class FixedTableWidgetWrapper(WidgetWrapper): From cb70aad7a36c5051e0ba3b5656a144faf62bddc2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 17:21:06 +1000 Subject: [PATCH 096/364] Fix restricting model algorithm input types to valid types for alg (refs #17030) --- .../processing/qgsprocessingparameters.sip | 66 ++++++----- python/plugins/processing/gui/wrappers.py | 2 +- .../models/qgsprocessingmodelalgorithm.cpp | 18 ++- .../processing/qgsprocessingparameters.cpp | 16 +-- src/core/processing/qgsprocessingparameters.h | 72 ++++++------ tests/src/core/testqgsprocessing.cpp | 111 ++++++++++++++++++ 6 files changed, 206 insertions(+), 79 deletions(-) diff --git a/python/core/processing/qgsprocessingparameters.sip b/python/core/processing/qgsprocessingparameters.sip index 7b2e185b2f7..66e8ddb7e9b 100644 --- a/python/core/processing/qgsprocessingparameters.sip +++ b/python/core/processing/qgsprocessingparameters.sip @@ -1420,7 +1420,42 @@ class QgsProcessingParameterExpression : QgsProcessingParameterDefinition }; -class QgsProcessingParameterVectorLayer : QgsProcessingParameterDefinition + +class QgsProcessingParameterLimitedDataTypes +{ +%Docstring + Can be inherited by parameters which require limits to their acceptable data types. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsprocessingparameters.h" +%End + public: + + QgsProcessingParameterLimitedDataTypes( const QList< int > &types = QList< int >() ); +%Docstring + Constructor for QgsProcessingParameterLimitedDataTypes, with a list of acceptable data ``types``. +%End + + QList< int > dataTypes() const; +%Docstring + Returns the geometry types for sources acceptable by the parameter. +.. seealso:: setDataTypes() + :rtype: list of int +%End + + void setDataTypes( const QList< int > &types ); +%Docstring + Sets the geometry ``types`` for sources acceptable by the parameter. +.. seealso:: dataTypes() +%End + + protected: + +}; + +class QgsProcessingParameterVectorLayer : QgsProcessingParameterDefinition, QgsProcessingParameterLimitedDataTypes { %Docstring A vector layer (with or without geometry) parameter for processing algorithms. Consider using @@ -1455,19 +1490,6 @@ class QgsProcessingParameterVectorLayer : QgsProcessingParameterDefinition virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const; - QList< int > dataTypes() const; -%Docstring - Returns the geometry types for sources acceptable by the parameter. -.. seealso:: setDataTypes() - :rtype: list of int -%End - - void setDataTypes( const QList< int > &types ); -%Docstring - Sets the geometry ``types`` for sources acceptable by the parameter. -.. seealso:: dataTypes() -%End - virtual QVariantMap toVariantMap() const; virtual bool fromVariantMap( const QVariantMap &map ); @@ -1579,7 +1601,8 @@ class QgsProcessingParameterField : QgsProcessingParameterDefinition }; -class QgsProcessingParameterFeatureSource : QgsProcessingParameterDefinition + +class QgsProcessingParameterFeatureSource : QgsProcessingParameterDefinition, QgsProcessingParameterLimitedDataTypes { %Docstring An input feature source (such as vector layers) parameter for processing algorithms. @@ -1613,19 +1636,6 @@ class QgsProcessingParameterFeatureSource : QgsProcessingParameterDefinition virtual QString asScriptCode() const; - QList< int > dataTypes() const; -%Docstring - Returns the geometry types for sources acceptable by the parameter. -.. seealso:: setDataTypes() - :rtype: list of int -%End - - void setDataTypes( const QList< int > &types ); -%Docstring - Sets the geometry ``types`` for sources acceptable by the parameter. -.. seealso:: dataTypes() -%End - virtual QVariantMap toVariantMap() const; virtual bool fromVariantMap( const QVariantMap &map ); diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index e918c0b13cc..6e4eec128a3 100644 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -897,7 +897,7 @@ class FeatureSourceWidgetWrapper(WidgetWrapper): return widget else: self.combo = QComboBox() - layers = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer), QgsProcessingOutputVectorLayer) + layers = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer), QgsProcessingOutputVectorLayer, self.param.dataTypes()) self.combo.setEditable(True) for layer in layers: self.combo.addItem(self.dialog.resolveValueDescription(layer), layer) diff --git a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp index 76b1b673ae3..fceaf1d216b 100644 --- a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp +++ b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp @@ -590,18 +590,24 @@ QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSo continue; } } - else if ( def->type() == QgsProcessingParameterFeatureSource::typeName() ) + else if ( def->type() == QgsProcessingParameterFeatureSource::typeName() || def->type() == QgsProcessingParameterVectorLayer::typeName() ) { - const QgsProcessingParameterFeatureSource *sourceDef = static_cast< const QgsProcessingParameterFeatureSource *>( def ); - bool ok = !sourceDef->dataTypes().isEmpty(); + const QgsProcessingParameterLimitedDataTypes *sourceDef = dynamic_cast< const QgsProcessingParameterLimitedDataTypes *>( def ); + if ( !sourceDef ) + continue; + + bool ok = sourceDef->dataTypes().isEmpty(); Q_FOREACH ( int type, sourceDef->dataTypes() ) { - if ( dataTypes.contains( type ) || type == QgsProcessing::TypeMapLayer ) + if ( dataTypes.contains( type ) || type == QgsProcessing::TypeMapLayer || type == QgsProcessing::TypeVector || type == QgsProcessing::TypeVectorAnyGeometry ) { ok = true; break; } } + if ( dataTypes.contains( QgsProcessing::TypeMapLayer ) || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) ) + ok = true; + if ( !ok ) continue; } @@ -636,7 +642,9 @@ QgsProcessingModelChildParameterSources QgsProcessingModelAlgorithm::availableSo if ( out->type() == QgsProcessingOutputVectorLayer::typeName() ) { const QgsProcessingOutputVectorLayer *vectorOut = static_cast< const QgsProcessingOutputVectorLayer *>( out ); - if ( !( dataTypes.contains( vectorOut->dataType() ) || vectorOut->dataType() == QgsProcessing::TypeMapLayer ) ) + + if ( !( dataTypes.contains( vectorOut->dataType() ) || vectorOut->dataType() == QgsProcessing::TypeMapLayer || vectorOut->dataType() == QgsProcessing::TypeVector + || vectorOut->dataType() == QgsProcessing::TypeVectorAnyGeometry ) ) { continue; } diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 15eac0250bd..c47e0fc6a38 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -2184,7 +2184,7 @@ QgsProcessingParameterExpression *QgsProcessingParameterExpression::fromScriptCo QgsProcessingParameterVectorLayer::QgsProcessingParameterVectorLayer( const QString &name, const QString &description, const QList &types, const QVariant &defaultValue, bool optional ) : QgsProcessingParameterDefinition( name, description, defaultValue, optional ) - , mDataTypes( types ) + , QgsProcessingParameterLimitedDataTypes( types ) { } @@ -2234,12 +2234,12 @@ QString QgsProcessingParameterVectorLayer::valueAsPythonString( const QVariant & return layer ? QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' ) : QString(); } -QList QgsProcessingParameterVectorLayer::dataTypes() const +QList QgsProcessingParameterLimitedDataTypes::dataTypes() const { return mDataTypes; } -void QgsProcessingParameterVectorLayer::setDataTypes( const QList &types ) +void QgsProcessingParameterLimitedDataTypes::setDataTypes( const QList &types ) { mDataTypes = types; } @@ -2483,7 +2483,7 @@ QgsProcessingParameterField *QgsProcessingParameterField::fromScriptCode( const QgsProcessingParameterFeatureSource::QgsProcessingParameterFeatureSource( const QString &name, const QString &description, const QList &types, const QVariant &defaultValue, bool optional ) : QgsProcessingParameterDefinition( name, description, defaultValue, optional ) - , mDataTypes( types ) + , QgsProcessingParameterLimitedDataTypes( types ) { } @@ -2603,14 +2603,10 @@ QString QgsProcessingParameterFeatureSource::asScriptCode() const return code.trimmed(); } -QList< int > QgsProcessingParameterFeatureSource::dataTypes() const +QgsProcessingParameterLimitedDataTypes::QgsProcessingParameterLimitedDataTypes( const QList &types ) + : mDataTypes( types ) { - return mDataTypes; -} -void QgsProcessingParameterFeatureSource::setDataTypes( const QList &types ) -{ - mDataTypes = types; } QVariantMap QgsProcessingParameterFeatureSource::toVariantMap() const diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index 80fed2eee49..08aa9c8febf 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -1350,6 +1350,40 @@ class CORE_EXPORT QgsProcessingParameterExpression : public QgsProcessingParamet }; + +/** + * \class QgsProcessingParameterLimitedDataTypes + * \ingroup core + * Can be inherited by parameters which require limits to their acceptable data types. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsProcessingParameterLimitedDataTypes +{ + public: + + /** + * Constructor for QgsProcessingParameterLimitedDataTypes, with a list of acceptable data \a types. + */ + QgsProcessingParameterLimitedDataTypes( const QList< int > &types = QList< int >() ); + + /** + * Returns the geometry types for sources acceptable by the parameter. + * \see setDataTypes() + */ + QList< int > dataTypes() const; + + /** + * Sets the geometry \a types for sources acceptable by the parameter. + * \see dataTypes() + */ + void setDataTypes( const QList< int > &types ); + + protected: + + //! List of acceptable data types for the parameter + QList< int > mDataTypes; +}; + /** * \class QgsProcessingParameterVectorLayer * \ingroup core @@ -1357,7 +1391,7 @@ class CORE_EXPORT QgsProcessingParameterExpression : public QgsProcessingParamet * the more versatile QgsProcessingParameterFeatureSource wherever possible. * \since QGIS 3.0 */ -class CORE_EXPORT QgsProcessingParameterVectorLayer : public QgsProcessingParameterDefinition +class CORE_EXPORT QgsProcessingParameterVectorLayer : public QgsProcessingParameterDefinition, public QgsProcessingParameterLimitedDataTypes { public: @@ -1379,18 +1413,6 @@ class CORE_EXPORT QgsProcessingParameterVectorLayer : public QgsProcessingParame bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override; QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; - /** - * Returns the geometry types for sources acceptable by the parameter. - * \see setDataTypes() - */ - QList< int > dataTypes() const; - - /** - * Sets the geometry \a types for sources acceptable by the parameter. - * \see dataTypes() - */ - void setDataTypes( const QList< int > &types ); - QVariantMap toVariantMap() const override; bool fromVariantMap( const QVariantMap &map ) override; @@ -1399,11 +1421,6 @@ class CORE_EXPORT QgsProcessingParameterVectorLayer : public QgsProcessingParame */ static QgsProcessingParameterVectorLayer *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) SIP_FACTORY; - private: - - QList< int > mDataTypes = QList< int >() << QgsProcessing::TypeVectorAnyGeometry; - - }; /** @@ -1497,13 +1514,14 @@ class CORE_EXPORT QgsProcessingParameterField : public QgsProcessingParameterDef }; + /** * \class QgsProcessingParameterFeatureSource * \ingroup core * An input feature source (such as vector layers) parameter for processing algorithms. * \since QGIS 3.0 */ -class CORE_EXPORT QgsProcessingParameterFeatureSource : public QgsProcessingParameterDefinition +class CORE_EXPORT QgsProcessingParameterFeatureSource : public QgsProcessingParameterDefinition, public QgsProcessingParameterLimitedDataTypes { public: @@ -1524,18 +1542,6 @@ class CORE_EXPORT QgsProcessingParameterFeatureSource : public QgsProcessingPara QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override; QString asScriptCode() const override; - /** - * Returns the geometry types for sources acceptable by the parameter. - * \see setDataTypes() - */ - QList< int > dataTypes() const; - - /** - * Sets the geometry \a types for sources acceptable by the parameter. - * \see dataTypes() - */ - void setDataTypes( const QList< int > &types ); - QVariantMap toVariantMap() const override; bool fromVariantMap( const QVariantMap &map ) override; @@ -1544,10 +1550,6 @@ class CORE_EXPORT QgsProcessingParameterFeatureSource : public QgsProcessingPara */ static QgsProcessingParameterFeatureSource *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) SIP_FACTORY; - private: - - QList< int > mDataTypes = QList< int >() << QgsProcessing::TypeVectorAnyGeometry; - }; /** diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index 5772677c27e..afd02c8a449 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -5154,6 +5154,117 @@ void TestQgsProcessing::modelAcceptableValues() res.clear(); res << sources.at( 0 ).outputChildId() + ':' + sources.at( 0 ).outputName(); QCOMPARE( res, QSet< QString >() << "cx1:OUTPUT" ); + + // test limiting by data types + QgsProcessingModelAlgorithm m2; + QgsProcessingModelParameter vlInput( "vl" ); + // with no limit on data types + m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl" ), vlInput ); + QgsProcessingModelParameter fsInput( "fs" ); + m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs" ), fsInput ); + + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorPoint ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVector ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorAnyGeometry ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeMapLayer ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + + // inputs are limited to vector layers + m2.removeModelParameter( vlInput.parameterName() ); + m2.removeModelParameter( fsInput.parameterName() ); + m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl", QString(), QList() << QgsProcessing::TypeVector ), vlInput ); + m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs", QString(), QList() << QgsProcessing::TypeVector ), fsInput ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorPoint ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVector ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorAnyGeometry ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeMapLayer ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + + // inputs are limited to vector layers with geometries + m2.removeModelParameter( vlInput.parameterName() ); + m2.removeModelParameter( fsInput.parameterName() ); + m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl", QString(), QList() << QgsProcessing::TypeVectorAnyGeometry ), vlInput ); + m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs", QString(), QList() << QgsProcessing::TypeVectorAnyGeometry ), fsInput ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorPoint ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVector ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorAnyGeometry ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeMapLayer ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + + // inputs are limited to vector layers with lines + m2.removeModelParameter( vlInput.parameterName() ); + m2.removeModelParameter( fsInput.parameterName() ); + m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl", QString(), QList() << QgsProcessing::TypeVectorLine ), vlInput ); + m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs", QString(), QList() << QgsProcessing::TypeVectorLine ), fsInput ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorPoint ); + QCOMPARE( sources.count(), 0 ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorPolygon ); + QCOMPARE( sources.count(), 0 ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorLine ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVector ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeVectorAnyGeometry ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); + sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList() << QgsProcessing::TypeMapLayer ); + QCOMPARE( sources.count(), 2 ); + QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) ); + QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) ); } void TestQgsProcessing::tempUtils() From 54e38bcf9e9e6e36f2a544417141f7d414edd8a9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 17:34:02 +1000 Subject: [PATCH 097/364] Fix model execution missing from processing history --- src/core/processing/models/qgsprocessingmodelalgorithm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp index fceaf1d216b..d4ef111a684 100644 --- a/src/core/processing/models/qgsprocessingmodelalgorithm.cpp +++ b/src/core/processing/models/qgsprocessingmodelalgorithm.cpp @@ -1121,6 +1121,7 @@ QgsProcessingAlgorithm *QgsProcessingModelAlgorithm::createInstance() const QgsProcessingModelAlgorithm *alg = new QgsProcessingModelAlgorithm(); alg->loadVariant( toVariant() ); alg->setProvider( provider() ); + alg->setSourceFilePath( sourceFilePath() ); return alg; } From 0669167af2a6e7ba2650be194d7330782766e073 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 23 Aug 2017 17:35:06 +1000 Subject: [PATCH 098/364] Rename processing 'recently used algorithms' to 'recently used' Since we show much more than just 'algorithms' here --- python/plugins/processing/gui/ProcessingToolbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/gui/ProcessingToolbox.py b/python/plugins/processing/gui/ProcessingToolbox.py index 58026ecb2e1..e2cc4309593 100644 --- a/python/plugins/processing/gui/ProcessingToolbox.py +++ b/python/plugins/processing/gui/ProcessingToolbox.py @@ -319,13 +319,13 @@ class ProcessingToolbox(BASE, WIDGET): found = False if updating: recentItem = self.algorithmTree.topLevelItem(0) - if recentItem.text(0) == self.tr('Recently used algorithms'): + if recentItem.text(0) == self.tr('Recently used'): treeWidget = recentItem.treeWidget() treeWidget.takeTopLevelItem( treeWidget.indexOfTopLevelItem(recentItem)) recentItem = QTreeWidgetItem() - recentItem.setText(0, self.tr('Recently used algorithms')) + recentItem.setText(0, self.tr('Recently used')) for algname in recent: alg = QgsApplication.processingRegistry().createAlgorithmById(algname) if alg is not None: From c606abc70293bfb7faedfed52b1cc36e327b7fd7 Mon Sep 17 00:00:00 2001 From: Etienne Trimaille Date: Tue, 22 Aug 2017 19:15:15 +0200 Subject: [PATCH 099/364] enable custom help in python expressions --- python/core/__init__.py | 2 +- tests/src/python/test_qgsexpression.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/python/core/__init__.py b/python/core/__init__.py index f0ff94e110c..1f9468b9db8 100644 --- a/python/core/__init__.py +++ b/python/core/__init__.py @@ -112,7 +112,7 @@ def register_function(function, arg_count, group, usesgeometry=False, helptemplate = string.Template("""

$name function


$doc""") name = kwargs.get('name', function.__name__) - helptext = function.__doc__ or '' + helptext = kwargs.get('helpText') or function.__doc__ or '' helptext = helptext.strip() expandargs = False diff --git a/tests/src/python/test_qgsexpression.py b/tests/src/python/test_qgsexpression.py index 67efbd99762..af318eef797 100644 --- a/tests/src/python/test_qgsexpression.py +++ b/tests/src/python/test_qgsexpression.py @@ -43,6 +43,18 @@ class TestQgsExpressionCustomFunctions(unittest.TestCase): def sqrt(values, feature, parent): pass + @qgsfunction(1, 'testing', register=False) + def help_with_docstring(values, feature, parent): + """The help comes from the python docstring.""" + pass + + help_text = 'The help comes from a variable.' + + @qgsfunction(1, 'testing', register=False, helpText=help_text) + def help_with_variable(values, feature, parent): + """This docstring is not used for the help.""" + pass + @qgsfunction(1, 'testing', register=False, usesgeometry=True) def geomtest(values, feature, parent): pass @@ -68,6 +80,17 @@ class TestQgsExpressionCustomFunctions(unittest.TestCase): args = function.params() self.assertEqual(args, 3) + def testHelp(self): + QgsExpression.registerFunction(self.help_with_variable) + html = ('

help_with_variable function


' + 'The help comes from a variable.') + self.assertEqual(self.help_with_variable.helpText(), html) + + QgsExpression.registerFunction(self.help_with_docstring) + html = ('

help_with_docstring function


' + 'The help comes from the python docstring.') + self.assertEqual(self.help_with_docstring.helpText(), html) + def testAutoArgsAreExpanded(self): function = self.expandargs args = function.params() From 8c7b29db66bf9e91c54e03d018d16656194a9984 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 23 Aug 2017 14:22:05 +0200 Subject: [PATCH 100/364] [docker] Remove source tree from image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d17c0183606..b665c6cd3f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,4 +32,4 @@ RUN cmake \ -DDISABLE_DEPRECATED=ON \ .. \ && ninja install \ - && rm -rf /usr/src/QGIS/build/* + && rm -rf /usr/src/QGIS From 64df114155072625c4b7b0e810e797e7af84ec18 Mon Sep 17 00:00:00 2001 From: "C. Marcel" Date: Tue, 25 Jul 2017 15:58:05 +0200 Subject: [PATCH 101/364] Fix 16888: create gpkg layer does not allow to add Z/M dimension --- src/gui/qgsnewgeopackagelayerdialog.cpp | 7 +++++++ src/ui/qgsnewgeopackagelayerdialogbase.ui | 19 +++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/gui/qgsnewgeopackagelayerdialog.cpp b/src/gui/qgsnewgeopackagelayerdialog.cpp index 38443804238..d6ca1ee05af 100644 --- a/src/gui/qgsnewgeopackagelayerdialog.cpp +++ b/src/gui/qgsnewgeopackagelayerdialog.cpp @@ -349,6 +349,13 @@ bool QgsNewGeoPackageLayerDialog::apply() OGRwkbGeometryType wkbType = static_cast ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() ); + // z-coordinate & m-value. + if (mGeometryWithZCheckBox->isChecked()) + { + wkbType = OGR_GT_SetZ(wkbType); + wkbType = OGR_GT_SetM(wkbType); + } + OGRSpatialReferenceH hSRS = nullptr; // consider spatial reference system of the layer QgsCoordinateReferenceSystem srs = mCrsSelector->crs(); diff --git a/src/ui/qgsnewgeopackagelayerdialogbase.ui b/src/ui/qgsnewgeopackagelayerdialogbase.ui index 51170f88d8c..cde6ad0f4bd 100644 --- a/src/ui/qgsnewgeopackagelayerdialogbase.ui +++ b/src/ui/qgsnewgeopackagelayerdialogbase.ui @@ -61,12 +61,12 @@ 0 0 - 530 - 635 + 514 + 644 - + Add an integer id field as the primary key for the new layer @@ -79,7 +79,7 @@ - + New field @@ -342,7 +342,7 @@ - + Fields list @@ -421,13 +421,20 @@
- + Qt::StrongFocus + + + + Geometries with Z/M coordinate + + +
From 018c13afbf4a53c4bb465f1e157b584e8a78fae2 Mon Sep 17 00:00:00 2001 From: "C. Marcel" Date: Wed, 23 Aug 2017 14:47:29 +0200 Subject: [PATCH 102/364] Use layer type instead of first vertex type --- src/app/nodetool/qgsnodeeditor.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/app/nodetool/qgsnodeeditor.cpp b/src/app/nodetool/qgsnodeeditor.cpp index b9b8b654e73..c98811f0803 100644 --- a/src/app/nodetool/qgsnodeeditor.cpp +++ b/src/app/nodetool/qgsnodeeditor.cpp @@ -47,20 +47,19 @@ QgsNodeEditorModel::QgsNodeEditorModel( QgsVectorLayer *layer, QgsSelectedFeatur , mRCol( -1 ) { - if ( !mSelectedFeature->vertexMap().isEmpty() ) - { - mHasZ = mSelectedFeature->vertexMap().at( 0 )->point().is3D(); - mHasM = mSelectedFeature->vertexMap().at( 0 )->point().isMeasure(); + QgsWkbTypes::Type layerWKBType = mLayer->wkbType(); - if ( mHasZ ) - mZCol = 2; + mHasZ = QgsWkbTypes::hasZ( layerWKBType ); + mHasM = QgsWkbTypes::hasM( layerWKBType ); - if ( mHasM ) - mMCol = 2 + ( mHasZ ? 1 : 0 ); + if ( mHasZ ) + mZCol = 2; - if ( mHasR ) - mRCol = 2 + ( mHasZ ? 1 : 0 ) + ( mHasM ? 1 : 0 ); - } + if ( mHasM ) + mMCol = 2 + ( mHasZ ? 1 : 0 ); + + if ( mHasR ) + mRCol = 2 + ( mHasZ ? 1 : 0 ) + ( mHasM ? 1 : 0 ); QWidget *parentWidget = dynamic_cast< QWidget * >( parent ); if ( parentWidget ) From fc5ad20154c7e667c1f1e09639af8b210c75d155 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 23 Aug 2017 15:17:15 +0200 Subject: [PATCH 103/364] [docker] Set valid workdir --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index b665c6cd3f2..6322876428f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,3 +33,5 @@ RUN cmake \ .. \ && ninja install \ && rm -rf /usr/src/QGIS + +WORKDIR / From d1fb2490a77059b756197ee0e250d8cbc49b80e8 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 23 Aug 2017 16:17:15 +0200 Subject: [PATCH 104/364] Allow multiple raster selection from GDAL source select widget Since I'm using QgsFileWidget I also added multiple files support to that widget and tests for this new behavior. --- python/gui/qgsfilewidget.sip | 5 +- src/gui/qgsfilewidget.cpp | 71 +++++++++++++++++----- src/gui/qgsfilewidget.h | 7 ++- src/providers/gdal/qgsgdalsourceselect.cpp | 16 ++++- tests/src/gui/testqgsfilewidget.cpp | 19 +++++- 5 files changed, 95 insertions(+), 23 deletions(-) diff --git a/python/gui/qgsfilewidget.sip b/python/gui/qgsfilewidget.sip index 45f1904f272..51b4976f491 100644 --- a/python/gui/qgsfilewidget.sip +++ b/python/gui/qgsfilewidget.sip @@ -30,7 +30,8 @@ class QgsFileWidget : QWidget enum StorageMode { GetFile, - GetDirectory + GetDirectory, + GetMultipleFiles, }; enum RelativeStorage @@ -72,7 +73,7 @@ returns the open file dialog title setDialogTitle defines the open file dialog title .. note:: - if not defined, the title is "Select a file" or "Select a directory" depending on the configuration. + if not defined, the title is "Select a file" or "Select a directory" or "Select one or more files" depending on the configuration. %End QString filter() const; diff --git a/src/gui/qgsfilewidget.cpp b/src/gui/qgsfilewidget.cpp index 3af620fe7ec..a80cb1b81cd 100644 --- a/src/gui/qgsfilewidget.cpp +++ b/src/gui/qgsfilewidget.cpp @@ -231,25 +231,40 @@ void QgsFileWidget::openFileDialog() // Handle Storage QString fileName; + QStringList fileNames; QString title; if ( mStorageMode == GetFile ) { title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select a file" ); fileName = QFileDialog::getOpenFileName( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); } + else if ( mStorageMode == GetMultipleFiles ) + { + title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select one ore more files" ); + fileNames = QFileDialog::getOpenFileNames( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); + } else if ( mStorageMode == GetDirectory ) { title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select a directory" ); fileName = QFileDialog::getExistingDirectory( this, title, QFileInfo( oldPath ).absoluteFilePath(), QFileDialog::ShowDirsOnly ); } - if ( fileName.isEmpty() ) + if ( fileName.isEmpty() && fileNames.isEmpty( ) ) return; + if ( mStorageMode != GetMultipleFiles ) + { + fileName = QDir::toNativeSeparators( QDir::cleanPath( QFileInfo( fileName ).absoluteFilePath() ) ); + } + else + { + for ( int i = 0; i < fileNames.length(); i++ ) + { + fileNames.replace( i, QDir::cleanPath( QFileInfo( fileNames.at( i ) ).absoluteFilePath() ) ) ; + } + } - fileName = QDir::toNativeSeparators( QDir::cleanPath( QFileInfo( fileName ).absoluteFilePath() ) ); // Store the last used path: - if ( mStorageMode == GetFile ) { settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), QFileInfo( fileName ).absolutePath() ); @@ -258,12 +273,26 @@ void QgsFileWidget::openFileDialog() { settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), fileName ); } + else if ( mStorageMode == GetMultipleFiles ) + { + settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), fileNames.first( ) ); + } // Handle relative Path storage - fileName = relativePath( fileName, true ); - - // Keep the new value - setFilePath( fileName ); + if ( mStorageMode != GetMultipleFiles ) + { + fileName = relativePath( fileName, true ); + // Keep the new value + setFilePath( fileName ); + } + else + { + for ( int i = 0; i < fileNames.length(); i++ ) + { + fileNames.replace( i, relativePath( fileNames.at( i ), true ) ); + } + setFilePath( QStringLiteral( "\"%1\"" ).arg( fileNames.join( "\" \"" ) ) ); + } } @@ -359,16 +388,30 @@ void QgsFileDropEdit::setFilters( const QString &filters ) QString QgsFileDropEdit::acceptableFilePath( QDropEvent *event ) const { - QString path; + QStringList paths; if ( event->mimeData()->hasUrls() ) { - QFileInfo file( event->mimeData()->urls().first().toLocalFile() ); - if ( ( mStorageMode == QgsFileWidget::GetFile && file.isFile() && - ( mAcceptableExtensions.isEmpty() || mAcceptableExtensions.contains( file.suffix(), Qt::CaseInsensitive ) ) ) - || ( mStorageMode == QgsFileWidget::GetDirectory && file.isDir() ) ) - path = file.filePath(); + for ( const auto url : event->mimeData()->urls() ) + { + QFileInfo file( url.toLocalFile() ); + if ( ( mStorageMode != QgsFileWidget::GetDirectory && file.isFile() && + ( mAcceptableExtensions.isEmpty() || mAcceptableExtensions.contains( file.suffix(), Qt::CaseInsensitive ) ) ) + || ( mStorageMode == QgsFileWidget::GetDirectory && file.isDir() ) ) + paths.append( file.filePath() ); + } + } + if ( paths.size() > 1 ) + { + return QStringLiteral( "\"%1\"" ).arg( paths.join( "\" \"" ) ); + } + else if ( paths.size() == 1 ) + { + return paths.first(); + } + else + { + return QString(); } - return path; } void QgsFileDropEdit::dragEnterEvent( QDragEnterEvent *event ) diff --git a/src/gui/qgsfilewidget.h b/src/gui/qgsfilewidget.h index 74666d5ddfe..6ec3777daab 100644 --- a/src/gui/qgsfilewidget.h +++ b/src/gui/qgsfilewidget.h @@ -61,8 +61,9 @@ class GUI_EXPORT QgsFileWidget : public QWidget */ enum StorageMode { - GetFile, - GetDirectory + GetFile, //! Select a single file + GetDirectory, //! Select a directory + GetMultipleFiles, //! Select multiple files }; /** @@ -94,7 +95,7 @@ class GUI_EXPORT QgsFileWidget : public QWidget /** * \brief setDialogTitle defines the open file dialog title - * \note if not defined, the title is "Select a file" or "Select a directory" depending on the configuration. + * \note if not defined, the title is "Select a file" or "Select a directory" or "Select one or more files" depending on the configuration. */ void setDialogTitle( const QString &title ); diff --git a/src/providers/gdal/qgsgdalsourceselect.cpp b/src/providers/gdal/qgsgdalsourceselect.cpp index 4d6f8bebfd8..06a458f0965 100644 --- a/src/providers/gdal/qgsgdalsourceselect.cpp +++ b/src/providers/gdal/qgsgdalsourceselect.cpp @@ -24,6 +24,7 @@ QgsGdalSourceSelect::QgsGdalSourceSelect( QWidget *parent, Qt::WindowFlags fl, Q setupUi( this ); setupButtons( buttonBox ); mQgsFileWidget->setFilter( QgsProviderRegistry::instance()->fileRasterFilters() ); + mQgsFileWidget->setStorageMode( QgsFileWidget::GetMultipleFiles ); connect( mQgsFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path ) { mRasterPath = path; @@ -38,8 +39,19 @@ QgsGdalSourceSelect::~QgsGdalSourceSelect() void QgsGdalSourceSelect::addButtonClicked() { - QFileInfo baseName( mRasterPath ); - emit addRasterLayer( mRasterPath, baseName.baseName(), QStringLiteral( "gdal" ) ); + // Check if multiple files where selected + if ( mRasterPath.startsWith( '"' ) ) + { + for ( QString path : mRasterPath.split( QRegExp( "\"\\s+\"" ), QString::SkipEmptyParts ) ) + { + QString cleanPath( path.remove( '"' ) ); + emit addRasterLayer( cleanPath, QFileInfo( cleanPath ).baseName(), QStringLiteral( "gdal" ) ); + } + } + else + { + emit addRasterLayer( mRasterPath, QFileInfo( mRasterPath ).baseName(), QStringLiteral( "gdal" ) ); + } } QGISEXTERN QgsGdalSourceSelect *selectWidget( QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode widgetMode ) diff --git a/tests/src/gui/testqgsfilewidget.cpp b/tests/src/gui/testqgsfilewidget.cpp index 4cea165c495..56470120277 100644 --- a/tests/src/gui/testqgsfilewidget.cpp +++ b/tests/src/gui/testqgsfilewidget.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - testqgsdoublespinbox.cpp + testqgsfilewidget.cpp -------------------------------------- Date : December 2014 Copyright : (C) 2014 Nyall Dawson @@ -27,10 +27,10 @@ class TestQgsFileWidget: public QObject 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. - void relativePath(); void toUrl(); void testDroppedFiles(); + void testMultipleFiles(); }; @@ -140,5 +140,20 @@ void TestQgsFileWidget::testDroppedFiles() } +void TestQgsFileWidget::testMultipleFiles() +{ + QgsFileWidget *w = new QgsFileWidget(); + w->setStorageMode( QgsFileWidget::GetFile ); + + std::unique_ptr< QMimeData > mime( new QMimeData() ); + mime->setUrls( QList() << QUrl::fromLocalFile( TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) ) + << QUrl::fromLocalFile( TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) ) ); + std::unique_ptr< QDropEvent > event( new QDropEvent( QPointF( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton, Qt::NoModifier ) ); + + qobject_cast< QgsFileDropEdit * >( w->lineEdit() )->dropEvent( event.get() ); + QCOMPARE( w->lineEdit()->text(), QStringLiteral( "\"%1\" \"%1\"" ).arg( TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) ) ); +} + + QGSTEST_MAIN( TestQgsFileWidget ) #include "testqgsfilewidget.moc" From 21b1c7ced9f0670577e886396ed827de0e5eee3f Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 23 Aug 2017 16:50:37 +0200 Subject: [PATCH 105/364] Fix spelling --- scripts/spell_check/spelling.dat | 1 + src/core/geometry/qgsgeometry.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/spell_check/spelling.dat b/scripts/spell_check/spelling.dat index 426a23acf7a..7cd4cd0634a 100644 --- a/scripts/spell_check/spelling.dat +++ b/scripts/spell_check/spelling.dat @@ -3561,6 +3561,7 @@ imprisonned:imprisoned improvision:improvisation improvment:improvement improvments:improvements +imput:input imrovement:improvement inablility:inability inacccessible:inaccessible diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index 24656d17b93..30c433f544e 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -112,7 +112,7 @@ class CORE_EXPORT QgsGeometry AddPartSelectedGeometryNotFound, //!< The selected geometry cannot be found AddPartNotMultiGeometry, //!< The source geometry is not multi /* Add ring issues*/ - AddRingNotClosed, //!< The imput ring is not closed + AddRingNotClosed, //!< The input ring is not closed AddRingNotValid, //!< The input ring is not valid AddRingCrossesExistingRings, //!< The input ring crosses existing rings (it is not disjoint) AddRingNotInExistingFeature, //!< The input ring doesn't have any existing ring to fit into From 50e8e1c00b592dfa9e7da61f11f44d4f23301cf1 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Wed, 23 Aug 2017 17:55:36 +0200 Subject: [PATCH 106/364] Reorganize buttons to set map extent There's likely no situation where the four buttons are activated simultaneously... Better have three on a line than the current heterogeneous UI. --- src/ui/qgsextentgroupboxwidget.ui | 43 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/ui/qgsextentgroupboxwidget.ui b/src/ui/qgsextentgroupboxwidget.ui index b86ee1983e6..c921f42a842 100755 --- a/src/ui/qgsextentgroupboxwidget.ui +++ b/src/ui/qgsextentgroupboxwidget.ui @@ -6,8 +6,8 @@ 0 0 - 380 - 202 + 679 + 163 @@ -77,7 +77,7 @@ - + @@ -103,7 +103,7 @@ - + Qt::Horizontal @@ -116,7 +116,7 @@ - + Qt::Horizontal @@ -129,20 +129,7 @@ - - - - - 150 - 0 - - - - Current layer extent - - - - + @@ -155,7 +142,7 @@ - + @@ -168,6 +155,19 @@ + + + + + 150 + 0 + + + + Current layer extent + + + @@ -178,7 +178,10 @@ mXMinLineEdit mXMaxLineEdit mYMinLineEdit + mOriginalExtentButton + mButtonCalcFromLayer mCurrentExtentButton + mButtonDrawOnCanvas From 81c31760db85bc6e410542a6e2260c1004a3bf7b Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 24 Aug 2017 07:28:17 +0200 Subject: [PATCH 107/364] Small code style changes --- src/gui/qgsfilewidget.cpp | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/gui/qgsfilewidget.cpp b/src/gui/qgsfilewidget.cpp index a80cb1b81cd..ab4048249d0 100644 --- a/src/gui/qgsfilewidget.cpp +++ b/src/gui/qgsfilewidget.cpp @@ -233,20 +233,21 @@ void QgsFileWidget::openFileDialog() QString fileName; QStringList fileNames; QString title; - if ( mStorageMode == GetFile ) + + switch ( mStorageMode ) { - title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select a file" ); - fileName = QFileDialog::getOpenFileName( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); - } - else if ( mStorageMode == GetMultipleFiles ) - { - title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select one ore more files" ); - fileNames = QFileDialog::getOpenFileNames( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); - } - else if ( mStorageMode == GetDirectory ) - { - title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select a directory" ); - fileName = QFileDialog::getExistingDirectory( this, title, QFileInfo( oldPath ).absoluteFilePath(), QFileDialog::ShowDirsOnly ); + case GetFile: + title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select a file" ); + fileName = QFileDialog::getOpenFileName( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); + break; + case GetMultipleFiles: + title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select one ore more files" ); + fileNames = QFileDialog::getOpenFileNames( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); + break; + case GetDirectory: + title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select a directory" ); + fileName = QFileDialog::getExistingDirectory( this, title, QFileInfo( oldPath ).absoluteFilePath(), QFileDialog::ShowDirsOnly ); + break; } if ( fileName.isEmpty() && fileNames.isEmpty( ) ) @@ -260,22 +261,22 @@ void QgsFileWidget::openFileDialog() { for ( int i = 0; i < fileNames.length(); i++ ) { - fileNames.replace( i, QDir::cleanPath( QFileInfo( fileNames.at( i ) ).absoluteFilePath() ) ) ; + fileNames.replace( i, QDir::toNativeSeparators( QDir::cleanPath( QFileInfo( fileNames.at( i ) ).absoluteFilePath() ) ) ) ; } } // Store the last used path: - if ( mStorageMode == GetFile ) + switch ( mStorageMode ) { - settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), QFileInfo( fileName ).absolutePath() ); - } - else if ( mStorageMode == GetDirectory ) - { - settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), fileName ); - } - else if ( mStorageMode == GetMultipleFiles ) - { - settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), fileNames.first( ) ); + case GetFile: + settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), QFileInfo( fileName ).absolutePath() ); + break; + case GetDirectory: + settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), fileName ); + break; + case GetMultipleFiles: + settings.setValue( QStringLiteral( "UI/lastFileNameWidgetDir" ), QFileInfo( fileNames.first( ) ).absolutePath() ); + break; } // Handle relative Path storage @@ -356,7 +357,6 @@ QString QgsFileWidget::toUrl( const QString &path ) const - ///@cond PRIVATE @@ -391,7 +391,7 @@ QString QgsFileDropEdit::acceptableFilePath( QDropEvent *event ) const QStringList paths; if ( event->mimeData()->hasUrls() ) { - for ( const auto url : event->mimeData()->urls() ) + Q_FOREACH ( const QUrl &url, event->mimeData()->urls() ) { QFileInfo file( url.toLocalFile() ); if ( ( mStorageMode != QgsFileWidget::GetDirectory && file.isFile() && From cc4cee8f9155f88a420a08a608212b92d86ec224 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 24 Aug 2017 07:39:54 +0200 Subject: [PATCH 108/364] Do not quote when single raster file is selected --- src/gui/qgsfilewidget.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gui/qgsfilewidget.cpp b/src/gui/qgsfilewidget.cpp index ab4048249d0..d53588f5449 100644 --- a/src/gui/qgsfilewidget.cpp +++ b/src/gui/qgsfilewidget.cpp @@ -283,7 +283,6 @@ void QgsFileWidget::openFileDialog() if ( mStorageMode != GetMultipleFiles ) { fileName = relativePath( fileName, true ); - // Keep the new value setFilePath( fileName ); } else @@ -292,7 +291,14 @@ void QgsFileWidget::openFileDialog() { fileNames.replace( i, relativePath( fileNames.at( i ), true ) ); } - setFilePath( QStringLiteral( "\"%1\"" ).arg( fileNames.join( "\" \"" ) ) ); + if ( fileNames.length() > 1 ) + { + setFilePath( QStringLiteral( "\"%1\"" ).arg( fileNames.join( "\" \"" ) ) ); + } + else + { + setFilePath( fileNames.first( ) ); + } } } From b9474061558e6bf0ba0c79a66974035f6079fdba Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 24 Aug 2017 08:29:40 +0200 Subject: [PATCH 109/364] More robust multiple paths split and utility static method --- python/gui/qgsfilewidget.sip | 12 +++++++++++- src/gui/qgsfilewidget.cpp | 20 ++++++++++++++++++++ src/gui/qgsfilewidget.h | 17 +++++++++++++---- src/providers/gdal/qgsgdalsourceselect.cpp | 13 ++----------- tests/src/gui/testqgsfilewidget.cpp | 18 +++++++++++++++++- 5 files changed, 63 insertions(+), 17 deletions(-) diff --git a/python/gui/qgsfilewidget.sip b/python/gui/qgsfilewidget.sip index 51b4976f491..7b613ff361d 100644 --- a/python/gui/qgsfilewidget.sip +++ b/python/gui/qgsfilewidget.sip @@ -48,10 +48,20 @@ class QgsFileWidget : QWidget QString filePath(); %Docstring -Returns the current file path + Returns the current file path(s) + when multiple files are selected, they are quoted and separated + by a single space (for example: '"/path/foo" "path/bar"') +.. seealso:: filePaths :rtype: str %End + static QStringList splitFilePaths( const QString &path ); +%Docstring + Split the the quoted and space separated ``path`` and returns a QString list +.. seealso:: filePath + :rtype: list of str +%End + void setFilePath( QString path ); %Docstring Sets the file path diff --git a/src/gui/qgsfilewidget.cpp b/src/gui/qgsfilewidget.cpp index d53588f5449..4895d4a62d7 100644 --- a/src/gui/qgsfilewidget.cpp +++ b/src/gui/qgsfilewidget.cpp @@ -77,6 +77,16 @@ QString QgsFileWidget::filePath() return mFilePath; } +QStringList QgsFileWidget::splitFilePaths( const QString &path ) +{ + QStringList paths; + for ( auto pathsPart : path.split( QRegExp( "\"\\s+\"" ), QString::SkipEmptyParts ) ) + { + paths.append( pathsPart.remove( QRegExp( "(^\\s*\")|(\"\\s*)" ) ) ); + } + return paths; +} + void QgsFileWidget::setFilePath( QString path ) { if ( path == QgsApplication::nullRepresentation() ) @@ -86,6 +96,7 @@ void QgsFileWidget::setFilePath( QString path ) //will trigger textEdited slot mLineEdit->setValue( path ); + } void QgsFileWidget::setReadOnly( bool readOnly ) @@ -130,6 +141,15 @@ void QgsFileWidget::textEdited( const QString &path ) { mFilePath = path; mLinkLabel->setText( toUrl( path ) ); + // Show tooltip if multiple files are selected + if ( path.contains( QStringLiteral( "\" \"" ) ) ) + { + mLineEdit->setToolTip( tr( "Selected files:
  • %1

" ).arg( splitFilePaths( path ).join( QStringLiteral( "
  • " ) ) ) ); + } + else + { + mLineEdit->setToolTip( QString() ); + } emit fileChanged( mFilePath ); } diff --git a/src/gui/qgsfilewidget.h b/src/gui/qgsfilewidget.h index 6ec3777daab..1d8f32d8d62 100644 --- a/src/gui/qgsfilewidget.h +++ b/src/gui/qgsfilewidget.h @@ -81,9 +81,20 @@ class GUI_EXPORT QgsFileWidget : public QWidget */ explicit QgsFileWidget( QWidget *parent SIP_TRANSFERTHIS = nullptr ); - //! Returns the current file path + /** + * \brief Returns the current file path(s) + * when multiple files are selected, they are quoted and separated + * by a single space (for example: '"/path/foo" "path/bar"') + * \see filePaths + */ QString filePath(); + /** + * \brief Split the the quoted and space separated \a path and returns a QString list + * \see filePath + */ + static QStringList splitFilePaths( const QString &path ); + //! Sets the file path void setFilePath( QString path ); @@ -212,9 +223,7 @@ class GUI_EXPORT QgsFileDropEdit: public QgsFilterLineEdit private: - /** - Return file name if object meets drop criteria. - */ + //! Return file name if object meets drop criteria. QString acceptableFilePath( QDropEvent *event ) const; QStringList mAcceptableExtensions; diff --git a/src/providers/gdal/qgsgdalsourceselect.cpp b/src/providers/gdal/qgsgdalsourceselect.cpp index 06a458f0965..fbb3ef618c6 100644 --- a/src/providers/gdal/qgsgdalsourceselect.cpp +++ b/src/providers/gdal/qgsgdalsourceselect.cpp @@ -39,18 +39,9 @@ QgsGdalSourceSelect::~QgsGdalSourceSelect() void QgsGdalSourceSelect::addButtonClicked() { - // Check if multiple files where selected - if ( mRasterPath.startsWith( '"' ) ) + Q_FOREACH ( const QString &path, QgsFileWidget::splitFilePaths( mRasterPath ) ) { - for ( QString path : mRasterPath.split( QRegExp( "\"\\s+\"" ), QString::SkipEmptyParts ) ) - { - QString cleanPath( path.remove( '"' ) ); - emit addRasterLayer( cleanPath, QFileInfo( cleanPath ).baseName(), QStringLiteral( "gdal" ) ); - } - } - else - { - emit addRasterLayer( mRasterPath, QFileInfo( mRasterPath ).baseName(), QStringLiteral( "gdal" ) ); + emit addRasterLayer( path, QFileInfo( path ).baseName(), QStringLiteral( "gdal" ) ); } } diff --git a/tests/src/gui/testqgsfilewidget.cpp b/tests/src/gui/testqgsfilewidget.cpp index 56470120277..0dfc1711f66 100644 --- a/tests/src/gui/testqgsfilewidget.cpp +++ b/tests/src/gui/testqgsfilewidget.cpp @@ -31,6 +31,7 @@ class TestQgsFileWidget: public QObject void toUrl(); void testDroppedFiles(); void testMultipleFiles(); + void testSplitFilePaths(); }; @@ -143,7 +144,7 @@ void TestQgsFileWidget::testDroppedFiles() void TestQgsFileWidget::testMultipleFiles() { QgsFileWidget *w = new QgsFileWidget(); - w->setStorageMode( QgsFileWidget::GetFile ); + w->setStorageMode( QgsFileWidget::GetMultipleFiles ); std::unique_ptr< QMimeData > mime( new QMimeData() ); mime->setUrls( QList() << QUrl::fromLocalFile( TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) ) @@ -155,5 +156,20 @@ void TestQgsFileWidget::testMultipleFiles() } + +void TestQgsFileWidget::testSplitFilePaths() +{ + const QString path = QString( TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) ); + QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%1\"" ).arg( path ) ), QStringList() << path << path ); + QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%1\"" ).arg( path ) ), QStringList() << path << path ); + QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%1\"" ).arg( path ) ), QStringList() << path << path ); + QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( " \"%1\" \"%1\" " ).arg( path ) ), QStringList() << path << path ); + QCOMPARE( QgsFileWidget::splitFilePaths( QStringLiteral( "\"%1\" \"%1\" " ).arg( path ) ), QStringList() << path << path ); + QCOMPARE( QgsFileWidget::splitFilePaths( path ), QStringList() << path ); +} + + + + QGSTEST_MAIN( TestQgsFileWidget ) #include "testqgsfilewidget.moc" From 9f18050e6209b99f25728824a76e3a5ab131dd2e Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Thu, 24 Aug 2017 12:24:32 +0200 Subject: [PATCH 110/364] Filling the help infrastructure (#5064) * Filling the help infrastructure Add missing help button and links to user manual * Remove unneeded comment * remove unneeded comment * Remove unneeded comment --- src/app/dwg/qgsdwgimportdialog.cpp | 6 ++ src/app/dwg/qgsdwgimportdialog.h | 2 + src/app/qgsalignrasterdialog.cpp | 7 ++ src/app/qgsalignrasterdialog.h | 2 + .../qgsattributeactionpropertiesdialog.cpp | 7 ++ src/app/qgsattributeactionpropertiesdialog.h | 2 + src/app/qgsattributetypedialog.cpp | 6 ++ src/app/qgsattributetypedialog.h | 3 + src/app/qgscustomization.cpp | 7 ++ src/app/qgscustomization.h | 3 + src/app/qgsdecorationlayoutextentdialog.cpp | 6 ++ src/app/qgsdecorationlayoutextentdialog.h | 2 + src/app/qgsdxfexportdialog.cpp | 5 ++ src/app/qgsdxfexportdialog.h | 2 + src/app/qgsmapsavedialog.cpp | 6 ++ src/app/qgsmapsavedialog.h | 4 ++ src/app/qgsprojectlayergroupdialog.cpp | 7 ++ src/app/qgsprojectlayergroupdialog.h | 2 + src/app/qgsrastercalcdialog.cpp | 6 ++ src/app/qgsrastercalcdialog.h | 2 + src/gui/qgsconfigureshortcutsdialog.cpp | 6 ++ src/gui/qgsconfigureshortcutsdialog.h | 4 ++ src/ui/qgsalignrasterdialog.ui | 16 +++-- .../qgsattributeactionpropertiesdialogbase.ui | 4 +- src/ui/qgsattributetypeedit.ui | 20 +++--- src/ui/qgsconfigureshortcutsdialog.ui | 2 +- src/ui/qgscustomizationdialogbase.ui | 2 +- src/ui/qgsdecorationlayoutextentdialog.ui | 69 ++++++++----------- src/ui/qgsdwgimportbase.ui | 4 +- src/ui/qgsdxfexportdialogbase.ui | 18 ++--- src/ui/qgsmapsavedialog.ui | 45 ++++++------ src/ui/qgsprojectlayergroupdialogbase.ui | 2 +- src/ui/qgsrastercalcdialogbase.ui | 2 +- 33 files changed, 185 insertions(+), 96 deletions(-) diff --git a/src/app/dwg/qgsdwgimportdialog.cpp b/src/app/dwg/qgsdwgimportdialog.cpp index d1cbecd433d..6430c685655 100644 --- a/src/app/dwg/qgsdwgimportdialog.cpp +++ b/src/app/dwg/qgsdwgimportdialog.cpp @@ -64,6 +64,7 @@ QgsDwgImportDialog::QgsDwgImportDialog( QWidget *parent, Qt::WindowFlags f ) : QDialog( parent, f ) { setupUi( this ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsDwgImportDialog::showHelp ); QgsSettings s; leDatabase->setText( s.value( "/DwgImport/lastDatabase", "" ).toString() ); @@ -478,3 +479,8 @@ void QgsDwgImportDialog::on_buttonBox_accepted() dwgGroup->setExpanded( false ); } } + +void QgsDwgImportDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html#importing-a-dxf-or-dwg-file" ) ); +} diff --git a/src/app/dwg/qgsdwgimportdialog.h b/src/app/dwg/qgsdwgimportdialog.h index 95a57e558d0..5f4539d71c5 100644 --- a/src/app/dwg/qgsdwgimportdialog.h +++ b/src/app/dwg/qgsdwgimportdialog.h @@ -19,6 +19,7 @@ #define QGSDWGIMPORTDIALOG_H #include "ui_qgsdwgimportbase.h" +#include "qgshelp.h" class QgsVectorLayer; class QgsLayerTreeGroup; @@ -40,6 +41,7 @@ class QgsDwgImportDialog : public QDialog, private Ui::QgsDwgImportBase void on_pbDeselectAll_clicked(); void on_leDatabase_textChanged( const QString &text ); void on_leLayerGroup_textChanged( const QString &text ); + void showHelp(); private: QgsVectorLayer *layer( QgsLayerTreeGroup *layerGroup, QString layer, QString table ); diff --git a/src/app/qgsalignrasterdialog.cpp b/src/app/qgsalignrasterdialog.cpp index fd0eb2b95d1..f96decf11c4 100644 --- a/src/app/qgsalignrasterdialog.cpp +++ b/src/app/qgsalignrasterdialog.cpp @@ -108,6 +108,7 @@ QgsAlignRasterDialog::QgsAlignRasterDialog( QWidget *parent ) // TODO: auto-detect reference layer connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsAlignRasterDialog::runAlign ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsAlignRasterDialog::showHelp ); populateLayersView(); @@ -122,6 +123,12 @@ QgsAlignRasterDialog::~QgsAlignRasterDialog() } +void QgsAlignRasterDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_raster/raster_analysis.html#raster-alignment" ) ); +} + + void QgsAlignRasterDialog::populateLayersView() { mCboReferenceLayer->clear(); diff --git a/src/app/qgsalignrasterdialog.h b/src/app/qgsalignrasterdialog.h index 241035aabd1..fa9decf4113 100644 --- a/src/app/qgsalignrasterdialog.h +++ b/src/app/qgsalignrasterdialog.h @@ -17,6 +17,7 @@ #include #include "qgsalignraster.h" +#include "qgshelp.h" #include "ui_qgsalignrasterdialog.h" class QgsAlignRaster; @@ -49,6 +50,7 @@ class QgsAlignRasterDialog : public QDialog, private Ui::QgsAlignRasterDialog void updateCustomGridOffset(); void updateParametersFromReferenceLayer(); + void showHelp(); protected: void populateLayersView(); diff --git a/src/app/qgsattributeactionpropertiesdialog.cpp b/src/app/qgsattributeactionpropertiesdialog.cpp index e314b272e73..9b93cb01f3d 100644 --- a/src/app/qgsattributeactionpropertiesdialog.cpp +++ b/src/app/qgsattributeactionpropertiesdialog.cpp @@ -212,5 +212,12 @@ void QgsAttributeActionPropertiesDialog::init( const QSet &actionScopes connect( mActionText, &QsciScintilla::textChanged, this, &QgsAttributeActionPropertiesDialog::updateButtons ); connect( mChooseIconButton, &QAbstractButton::clicked, this, &QgsAttributeActionPropertiesDialog::chooseIcon ); + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsAttributeActionPropertiesDialog::showHelp ); + updateButtons(); } + +void QgsAttributeActionPropertiesDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#actions-properties" ) ); +} diff --git a/src/app/qgsattributeactionpropertiesdialog.h b/src/app/qgsattributeactionpropertiesdialog.h index 9ed7b440350..6b1780819aa 100644 --- a/src/app/qgsattributeactionpropertiesdialog.h +++ b/src/app/qgsattributeactionpropertiesdialog.h @@ -19,6 +19,7 @@ #include "ui_qgsattributeactionpropertiesdialogbase.h" #include "qgsaction.h" +#include "qgshelp.h" #include @@ -52,6 +53,7 @@ class QgsAttributeActionPropertiesDialog: public QDialog, private Ui::QgsAttribu void insertExpressionOrField(); void chooseIcon(); void updateButtons(); + void showHelp(); private: void init( const QSet &actionScopes ); diff --git a/src/app/qgsattributetypedialog.cpp b/src/app/qgsattributetypedialog.cpp index 6bec2c5d1c3..b34b7ece9d3 100644 --- a/src/app/qgsattributetypedialog.cpp +++ b/src/app/qgsattributetypedialog.cpp @@ -93,6 +93,7 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx restoreGeometry( settings.value( QStringLiteral( "Windows/QgsAttributeTypeDialog/geometry" ) ).toByteArray() ); constraintExpressionWidget->setLayer( vl ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsAttributeTypeDialog::showHelp ); } QgsAttributeTypeDialog::~QgsAttributeTypeDialog() @@ -371,3 +372,8 @@ void QgsAttributeTypeDialog::defaultExpressionChanged() mDefaultPreviewLabel->setText( "" + previewText + "" ); } + +void QgsAttributeTypeDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#configure-the-field-behavior" ) ); +} diff --git a/src/app/qgsattributetypedialog.h b/src/app/qgsattributetypedialog.h index 18837088c46..dc57bd4548a 100644 --- a/src/app/qgsattributetypedialog.h +++ b/src/app/qgsattributetypedialog.h @@ -22,6 +22,7 @@ #include "qgseditorconfigwidget.h" #include "qgsfeature.h" #include "qgsvectordataprovider.h" +#include "qgshelp.h" #include "qgis_app.h" class QDialog; @@ -175,6 +176,8 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut void defaultExpressionChanged(); + void showHelp(); + private: QgsVectorLayer *mLayer = nullptr; int mFieldIdx; diff --git a/src/app/qgscustomization.cpp b/src/app/qgscustomization.cpp index d8c77a441ae..37bd5f3bbad 100644 --- a/src/app/qgscustomization.cpp +++ b/src/app/qgscustomization.cpp @@ -62,6 +62,7 @@ QgsCustomizationDialog::QgsCustomizationDialog( QWidget * parent, QSettings * se connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsCustomizationDialog::apply ); connect( buttonBox->button( QDialogButtonBox::Cancel ), &QAbstractButton::clicked, this, &QgsCustomizationDialog::cancel ); connect( buttonBox->button( QDialogButtonBox::Reset ), &QAbstractButton::clicked, this, &QgsCustomizationDialog::reset ); + connect( buttonBox->button( QDialogButtonBox::Help ), &QAbstractButton::clicked, this, &QgsCustomizationDialog::showHelp ); } @@ -483,6 +484,12 @@ bool QgsCustomizationDialog::catchOn() return actionCatch->isChecked(); } +void QgsCustomizationDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/qgis_configuration.html#customization" ) ); +} + + void QgsCustomization::addTreeItemActions( QTreeWidgetItem *parentItem, const QList &actions ) { Q_FOREACH ( QAction *action, actions ) diff --git a/src/app/qgscustomization.h b/src/app/qgscustomization.h index 359db0a29c3..1846779f8b6 100644 --- a/src/app/qgscustomization.h +++ b/src/app/qgscustomization.h @@ -18,6 +18,7 @@ #define QGSCUSTOMIZATION_H #include "ui_qgscustomizationdialogbase.h" +#include "qgshelp.h" #include #include @@ -77,6 +78,8 @@ class APP_EXPORT QgsCustomizationDialog : public QMainWindow, private Ui::QgsCus void cancel(); + void showHelp(); + // Reset values from settings void reset(); diff --git a/src/app/qgsdecorationlayoutextentdialog.cpp b/src/app/qgsdecorationlayoutextentdialog.cpp index 4dd1e2ba1c7..39de5d6c8f3 100644 --- a/src/app/qgsdecorationlayoutextentdialog.cpp +++ b/src/app/qgsdecorationlayoutextentdialog.cpp @@ -42,6 +42,7 @@ QgsDecorationLayoutExtentDialog::QgsDecorationLayoutExtentDialog( QgsDecorationL updateGuiElements(); connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsDecorationLayoutExtentDialog::apply ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsDecorationLayoutExtentDialog::showHelp ); mSymbolButton->setMapCanvas( QgisApp::instance()->mapCanvas() ); } @@ -84,3 +85,8 @@ void QgsDecorationLayoutExtentDialog::on_buttonBox_rejected() { reject(); } + +void QgsDecorationLayoutExtentDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#decorations" ) ); +} diff --git a/src/app/qgsdecorationlayoutextentdialog.h b/src/app/qgsdecorationlayoutextentdialog.h index 5b87ae9a8e6..698155b7f7d 100644 --- a/src/app/qgsdecorationlayoutextentdialog.h +++ b/src/app/qgsdecorationlayoutextentdialog.h @@ -21,6 +21,7 @@ #include #include "qgis_app.h" #include "qgstextrenderer.h" +#include "qgshelp.h" #include class QgsDecorationLayoutExtent; @@ -38,6 +39,7 @@ class APP_EXPORT QgsDecorationLayoutExtentDialog : public QDialog, private Ui::Q void apply(); void on_buttonBox_accepted(); void on_buttonBox_rejected(); + void showHelp(); private: diff --git a/src/app/qgsdxfexportdialog.cpp b/src/app/qgsdxfexportdialog.cpp index 6b16adc8a80..1350cbcd4f4 100644 --- a/src/app/qgsdxfexportdialog.cpp +++ b/src/app/qgsdxfexportdialog.cpp @@ -444,6 +444,7 @@ QgsDxfExportDialog::QgsDxfExportDialog( QWidget *parent, Qt::WindowFlags f ) connect( this, &QDialog::accepted, this, &QgsDxfExportDialog::saveSettings ); connect( mSelectAllButton, &QAbstractButton::clicked, this, &QgsDxfExportDialog::selectAll ); connect( mDeselectAllButton, &QAbstractButton::clicked, this, &QgsDxfExportDialog::deSelectAll ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsDxfExportDialog::showHelp ); mFileLineEdit->setFocus(); @@ -647,3 +648,7 @@ QString QgsDxfExportDialog::mapTheme() const { return mVisibilityPresets->currentText(); } +void QgsDxfExportDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#create-dxf-files" ) ); +} diff --git a/src/app/qgsdxfexportdialog.h b/src/app/qgsdxfexportdialog.h index fc4510a9e6c..3f42aff4944 100644 --- a/src/app/qgsdxfexportdialog.h +++ b/src/app/qgsdxfexportdialog.h @@ -21,6 +21,7 @@ #include "ui_qgsdxfexportdialogbase.h" #include "qgslayertreemodel.h" #include "qgsdxfexport.h" +#include "qgshelp.h" #include #include @@ -102,6 +103,7 @@ class QgsDxfExportDialog : public QDialog, private Ui::QgsDxfExportDialogBase void saveSettings(); void on_mVisibilityPresets_currentIndexChanged( int index ); void on_mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem &crs ); + void showHelp(); private: void cleanGroup( QgsLayerTreeNode *node ); diff --git a/src/app/qgsmapsavedialog.cpp b/src/app/qgsmapsavedialog.cpp index 21051748f6c..8a3789ea195 100644 --- a/src/app/qgsmapsavedialog.cpp +++ b/src/app/qgsmapsavedialog.cpp @@ -119,6 +119,7 @@ QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, QL } connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsMapSaveDialog::accepted ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsMapSaveDialog::showHelp ); } void QgsMapSaveDialog::updateDpi( int dpi ) @@ -459,3 +460,8 @@ void QgsMapSaveDialog::accepted() } } } + +void QgsMapSaveDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/getting_started.html#output" ) ); +} diff --git a/src/app/qgsmapsavedialog.h b/src/app/qgsmapsavedialog.h index 418dc052a9c..c162785e5e7 100644 --- a/src/app/qgsmapsavedialog.h +++ b/src/app/qgsmapsavedialog.h @@ -24,6 +24,7 @@ #include "qgsmapcanvas.h" #include "qgsmapdecoration.h" #include "qgsrectangle.h" +#include "qgshelp.h" #include #include @@ -97,6 +98,9 @@ class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog int mDpi; QSize mSize; + private slots: + + void showHelp(); }; #endif // QGSMAPSAVEDIALOG_H diff --git a/src/app/qgsprojectlayergroupdialog.cpp b/src/app/qgsprojectlayergroupdialog.cpp index 7f0e6d42e06..2fad82e24b6 100644 --- a/src/app/qgsprojectlayergroupdialog.cpp +++ b/src/app/qgsprojectlayergroupdialog.cpp @@ -47,6 +47,7 @@ QgsProjectLayerGroupDialog::QgsProjectLayerGroupDialog( QWidget *parent, const Q } connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject ); + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProjectLayerGroupDialog::showHelp ); } QgsProjectLayerGroupDialog::~QgsProjectLayerGroupDialog() @@ -233,3 +234,9 @@ void QgsProjectLayerGroupDialog::on_mButtonBox_accepted() } accept(); } + +void QgsProjectLayerGroupDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#nesting-projects" ) ); + +} diff --git a/src/app/qgsprojectlayergroupdialog.h b/src/app/qgsprojectlayergroupdialog.h index 88646442ca2..318aa30c20c 100644 --- a/src/app/qgsprojectlayergroupdialog.h +++ b/src/app/qgsprojectlayergroupdialog.h @@ -17,6 +17,7 @@ #include "QDialog" #include "ui_qgsprojectlayergroupdialogbase.h" +#include "qgshelp.h" #include "qgis_app.h" class QDomElement; @@ -44,6 +45,7 @@ class APP_EXPORT QgsProjectLayerGroupDialog: public QDialog, private Ui::QgsProj void on_mProjectFileLineEdit_editingFinished(); void onTreeViewSelectionChanged(); void on_mButtonBox_accepted(); + void showHelp(); private: void changeProjectFile(); diff --git a/src/app/qgsrastercalcdialog.cpp b/src/app/qgsrastercalcdialog.cpp index e0f9d3994dc..068929c7eaf 100644 --- a/src/app/qgsrastercalcdialog.cpp +++ b/src/app/qgsrastercalcdialog.cpp @@ -30,6 +30,7 @@ QgsRasterCalcDialog::QgsRasterCalcDialog( QWidget *parent, Qt::WindowFlags f ): QDialog( parent, f ) { setupUi( this ); + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterCalcDialog::showHelp ); QgsSettings settings; restoreGeometry( settings.value( QStringLiteral( "Windows/RasterCalc/geometry" ) ).toByteArray() ); @@ -224,6 +225,11 @@ void QgsRasterCalcDialog::on_mButtonBox_accepted() s.setValue( QStringLiteral( "/RasterCalculator/lastOutputDir" ), QVariant( QFileInfo( mOutputLayerLineEdit->text() ).absolutePath() ) ); } +void QgsRasterCalcDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_raster/raster_analysis.html#raster-calculator" ) ); +} + void QgsRasterCalcDialog::on_mOutputLayerPushButton_clicked() { QgsSettings s; diff --git a/src/app/qgsrastercalcdialog.h b/src/app/qgsrastercalcdialog.h index 9e96072ec3d..962b00e810f 100644 --- a/src/app/qgsrastercalcdialog.h +++ b/src/app/qgsrastercalcdialog.h @@ -20,6 +20,7 @@ #include "ui_qgsrastercalcdialogbase.h" #include "qgsrastercalculator.h" +#include "qgshelp.h" #include "qgis_app.h" //! A dialog to enter a raster calculation expression @@ -54,6 +55,7 @@ class APP_EXPORT QgsRasterCalcDialog: public QDialog, private Ui::QgsRasterCalcD void on_mOutputLayerLineEdit_textChanged( const QString &text ); //! Enables OK button if calculator expression is valid and output file path exists void setAcceptButtonState(); + void showHelp(); //calculator buttons void on_mPlusPushButton_clicked(); diff --git a/src/gui/qgsconfigureshortcutsdialog.cpp b/src/gui/qgsconfigureshortcutsdialog.cpp index f2abe421840..194252e8015 100644 --- a/src/gui/qgsconfigureshortcutsdialog.cpp +++ b/src/gui/qgsconfigureshortcutsdialog.cpp @@ -41,6 +41,7 @@ QgsConfigureShortcutsDialog::QgsConfigureShortcutsDialog( QWidget *parent, QgsSh if ( !mManager ) mManager = QgsGui::shortcutsManager(); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsConfigureShortcutsDialog::showHelp ); // Vérifier nommage des boutons connect( btnChangeShortcut, &QAbstractButton::clicked, this, &QgsConfigureShortcutsDialog::changeShortcut ); connect( btnResetShortcut, &QAbstractButton::clicked, this, &QgsConfigureShortcutsDialog::resetShortcut ); connect( btnSetNoShortcut, &QAbstractButton::clicked, this, &QgsConfigureShortcutsDialog::setNoShortcut ); @@ -491,3 +492,8 @@ void QgsConfigureShortcutsDialog::on_mLeFilter_textChanged( const QString &text } } } + +void QgsConfigureShortcutsDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/qgis_configuration.html#keyboard-shortcuts" ) ); +} diff --git a/src/gui/qgsconfigureshortcutsdialog.h b/src/gui/qgsconfigureshortcutsdialog.h index 351c6bbd7c6..0527b844962 100644 --- a/src/gui/qgsconfigureshortcutsdialog.h +++ b/src/gui/qgsconfigureshortcutsdialog.h @@ -20,6 +20,7 @@ #include "qgis.h" #include "ui_qgsconfigureshortcutsdialog.h" +#include "qgshelp.h" #include "qgis_gui.h" class QShortcut; @@ -60,6 +61,9 @@ class GUI_EXPORT QgsConfigureShortcutsDialog : public QDialog, private Ui::QgsCo void actionChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous ); + //! Open the associated help + void showHelp(); + private: //! Saves the dialog window state diff --git a/src/ui/qgsalignrasterdialog.ui b/src/ui/qgsalignrasterdialog.ui index 1d4fc2a5616..b4898b17e39 100644 --- a/src/ui/qgsalignrasterdialog.ui +++ b/src/ui/qgsalignrasterdialog.ui @@ -208,7 +208,7 @@ - QDialogButtonBox::Close|QDialogButtonBox::Ok + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok @@ -218,17 +218,23 @@ - QgsProjectionSelectionWidget - QWidget -
    qgsprojectionselectionwidget.h
    + QgsCollapsibleGroupBox + QGroupBox +
    qgscollapsiblegroupbox.h
    1
    QgsExtentGroupBox - QGroupBox + QgsCollapsibleGroupBox
    qgsextentgroupbox.h
    1
    + + QgsProjectionSelectionWidget + QWidget +
    qgsprojectionselectionwidget.h
    + 1 +
    mBtnAdd diff --git a/src/ui/qgsattributeactionpropertiesdialogbase.ui b/src/ui/qgsattributeactionpropertiesdialogbase.ui index fc4ac877fea..addc885fe3c 100644 --- a/src/ui/qgsattributeactionpropertiesdialogbase.ui +++ b/src/ui/qgsattributeactionpropertiesdialogbase.ui @@ -67,7 +67,7 @@ - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok @@ -136,7 +136,7 @@ - + Qt::TabFocus diff --git a/src/ui/qgsattributetypeedit.ui b/src/ui/qgsattributetypeedit.ui index abbf7296e82..8a824c49ee3 100644 --- a/src/ui/qgsattributetypeedit.ui +++ b/src/ui/qgsattributetypeedit.ui @@ -104,16 +104,6 @@ - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - @@ -190,6 +180,16 @@ + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + diff --git a/src/ui/qgsconfigureshortcutsdialog.ui b/src/ui/qgsconfigureshortcutsdialog.ui index 95bf9ac8f19..9ee749a6d3d 100644 --- a/src/ui/qgsconfigureshortcutsdialog.ui +++ b/src/ui/qgsconfigureshortcutsdialog.ui @@ -109,7 +109,7 @@ Qt::Horizontal - QDialogButtonBox::Close + QDialogButtonBox::Close|QDialogButtonBox::Help diff --git a/src/ui/qgscustomizationdialogbase.ui b/src/ui/qgscustomizationdialogbase.ui index 820d372c833..712acb69327 100644 --- a/src/ui/qgscustomizationdialogbase.ui +++ b/src/ui/qgscustomizationdialogbase.ui @@ -56,7 +56,7 @@ Qt::Horizontal - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Reset diff --git a/src/ui/qgsdecorationlayoutextentdialog.ui b/src/ui/qgsdecorationlayoutextentdialog.ui index 015f02e041d..d61af4b3571 100755 --- a/src/ui/qgsdecorationlayoutextentdialog.ui +++ b/src/ui/qgsdecorationlayoutextentdialog.ui @@ -6,8 +6,8 @@ 0 0 - 293 - 143 + 377 + 148 @@ -32,29 +32,22 @@ false - - + + true + + + 0 + 0 + + - Label extents + Font - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -68,32 +61,13 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + true - - - 0 - 0 - - - Font + Label extents @@ -119,6 +93,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -128,7 +115,7 @@ Qt::Horizontal - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsdwgimportbase.ui b/src/ui/qgsdwgimportbase.ui index 84135c9ba91..b9e22c9b518 100644 --- a/src/ui/qgsdwgimportbase.ui +++ b/src/ui/qgsdwgimportbase.ui @@ -7,7 +7,7 @@ 0 0 497 - 366 + 415 @@ -20,7 +20,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsdxfexportdialogbase.ui b/src/ui/qgsdxfexportdialogbase.ui index f10a6d58d72..3c04d5a92be 100644 --- a/src/ui/qgsdxfexportdialogbase.ui +++ b/src/ui/qgsdxfexportdialogbase.ui @@ -72,11 +72,11 @@ - + Qt::StrongFocus - + true @@ -131,7 +131,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok @@ -167,7 +167,7 @@ - + Qt::StrongFocus @@ -176,17 +176,17 @@ + + QgsScaleWidget + QWidget +
    qgsscalewidget.h
    +
    QgsProjectionSelectionWidget QWidget
    qgsprojectionselectionwidget.h
    1
    - - QgsScaleWidget - QWidget -
    qgsscalewidget.h
    -
    QgsLayerTreeView QTreeView diff --git a/src/ui/qgsmapsavedialog.ui b/src/ui/qgsmapsavedialog.ui index 776640891df..5625cd51e87 100644 --- a/src/ui/qgsmapsavedialog.ui +++ b/src/ui/qgsmapsavedialog.ui @@ -7,7 +7,7 @@ 0 0 600 - 225 + 318 @@ -25,17 +25,17 @@
    - - Rasterize map + + false Advanced effects such as blend modes or vector layer transparency cannot be exported as vectors. Rasterizing the map is recommended when such effects are used. - - false + + Rasterize map - + false @@ -50,7 +50,7 @@ Rasterizing the map is recommended when such effects are used. - > + Draw annotations @@ -133,9 +133,6 @@ Rasterizing the map is recommended when such effects are used. - - 13 - 0 @@ -145,6 +142,9 @@ Rasterizing the map is recommended when such effects are used. Lock aspect ratio (including while drawing extent onto canvas) + + 13 + @@ -204,8 +204,7 @@ Rasterizing the map is recommended when such effects are used. - - + @@ -235,7 +234,7 @@ Rasterizing the map is recommended when such effects are used. Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Save + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Save @@ -248,6 +247,16 @@ Rasterizing the map is recommended when such effects are used.
    qgscollapsiblegroupbox.h
    1 + + QgsSpinBox + QSpinBox +
    qgsspinbox.h
    +
    + + QgsScaleWidget + QWidget +
    qgsscalewidget.h
    +
    QgsExtentGroupBox QgsCollapsibleGroupBox @@ -260,16 +269,6 @@ Rasterizing the map is recommended when such effects are used.
    qgsratiolockbutton.h
    1
    - - QgsSpinBox - QSpinBox -
    qgsspinbox.h
    -
    - - QgsScaleWidget - QWidget -
    qgsscalewidget.h
    -
    mExtentGroupBox diff --git a/src/ui/qgsprojectlayergroupdialogbase.ui b/src/ui/qgsprojectlayergroupdialogbase.ui index 01d4f3b31b4..29b73b364fe 100644 --- a/src/ui/qgsprojectlayergroupdialogbase.ui +++ b/src/ui/qgsprojectlayergroupdialogbase.ui @@ -41,7 +41,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsrastercalcdialogbase.ui b/src/ui/qgsrastercalcdialogbase.ui index 9474f56409f..fa4708638e0 100644 --- a/src/ui/qgsrastercalcdialogbase.ui +++ b/src/ui/qgsrastercalcdialogbase.ui @@ -504,7 +504,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok From bd8decee09cff7ebe5f01ddd8c2fdd546cb5e3a9 Mon Sep 17 00:00:00 2001 From: "C. Marcel" Date: Thu, 24 Aug 2017 13:54:30 +0200 Subject: [PATCH 111/364] Split Z/M checkbox into 2 checkboxes. --- src/gui/qgsnewgeopackagelayerdialog.cpp | 10 ++--- src/ui/qgsnewgeopackagelayerdialogbase.ui | 47 ++++++++++++++--------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/gui/qgsnewgeopackagelayerdialog.cpp b/src/gui/qgsnewgeopackagelayerdialog.cpp index d6ca1ee05af..6137c427bfa 100644 --- a/src/gui/qgsnewgeopackagelayerdialog.cpp +++ b/src/gui/qgsnewgeopackagelayerdialog.cpp @@ -350,11 +350,11 @@ bool QgsNewGeoPackageLayerDialog::apply() ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() ); // z-coordinate & m-value. - if (mGeometryWithZCheckBox->isChecked()) - { - wkbType = OGR_GT_SetZ(wkbType); - wkbType = OGR_GT_SetM(wkbType); - } + if ( mGeometryWithZCheckBox->isChecked() ) + wkbType = OGR_GT_SetZ( wkbType ); + + if ( mGeometryWithMCheckBox->isChecked() ) + wkbType = OGR_GT_SetM( wkbType ); OGRSpatialReferenceH hSRS = nullptr; // consider spatial reference system of the layer diff --git a/src/ui/qgsnewgeopackagelayerdialogbase.ui b/src/ui/qgsnewgeopackagelayerdialogbase.ui index cde6ad0f4bd..dfd9b0a7e8b 100644 --- a/src/ui/qgsnewgeopackagelayerdialogbase.ui +++ b/src/ui/qgsnewgeopackagelayerdialogbase.ui @@ -62,11 +62,11 @@ 0 0 514 - 644 + 663 - + Add an integer id field as the primary key for the new layer @@ -79,7 +79,7 @@ - + New field @@ -342,7 +342,7 @@ - + Fields list @@ -421,19 +421,30 @@ - - + + Qt::StrongFocus - - - - Geometries with Z/M coordinate - - + + + + + + Geometries with Z coordinate + + + + + + + Geometries with M coordinate + + + + @@ -443,18 +454,18 @@ - - QgsScrollArea - QScrollArea -
    qgsscrollarea.h
    - 1 -
    QgsProjectionSelectionWidget QWidget
    qgsprojectionselectionwidget.h
    1
    + + QgsScrollArea + QScrollArea +
    qgsscrollarea.h
    + 1 +
    scrollArea From 8771e838c71962f3ba94ca5b5736a1c1c3ddb9d4 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 24 Aug 2017 16:37:10 +0200 Subject: [PATCH 112/364] Geopackage import rasters with drag and drop This implementation uses GDALTranslate and a QgsTask. --- src/providers/ogr/CMakeLists.txt | 4 + src/providers/ogr/qgsgeopackagedataitems.cpp | 276 ++++++++++-------- src/providers/ogr/qgsgeopackagedataitems.h | 3 + .../ogr/qgsgeopackagerasterwriter.cpp | 74 +++++ src/providers/ogr/qgsgeopackagerasterwriter.h | 52 ++++ .../ogr/qgsgeopackagerasterwritertask.cpp | 55 ++++ .../ogr/qgsgeopackagerasterwritertask.h | 83 ++++++ 7 files changed, 427 insertions(+), 120 deletions(-) create mode 100644 src/providers/ogr/qgsgeopackagerasterwriter.cpp create mode 100644 src/providers/ogr/qgsgeopackagerasterwriter.h create mode 100644 src/providers/ogr/qgsgeopackagerasterwritertask.cpp create mode 100644 src/providers/ogr/qgsgeopackagerasterwritertask.h diff --git a/src/providers/ogr/CMakeLists.txt b/src/providers/ogr/CMakeLists.txt index 0542b83c969..572abbf9c0c 100644 --- a/src/providers/ogr/CMakeLists.txt +++ b/src/providers/ogr/CMakeLists.txt @@ -8,6 +8,8 @@ SET (OGR_SRCS qgsogrsourceselect.cpp qgsgeopackagedataitems.cpp qgsgeopackageconnection.cpp + qgsgeopackagerasterwriter.cpp + qgsgeopackagerasterwritertask.cpp ) SET(OGR_MOC_HDRS @@ -17,6 +19,8 @@ SET(OGR_MOC_HDRS qgsogrsourceselect.h qgsgeopackagedataitems.h qgsgeopackageconnection.h + qgsgeopackagerasterwriter.h + qgsgeopackagerasterwritertask.h ) ######################################################## diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 7563e7e5f4a..d053bbd82e1 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -23,12 +23,11 @@ #include "qgsvectorlayer.h" #include "qgsrasterlayer.h" #include "qgsogrprovider.h" -#include "qgscplerrorhandler.h" #include "qgsnewgeopackagelayerdialog.h" #include "qgsmessageoutput.h" #include "qgsvectorlayerexporter.h" +#include "qgsgeopackagerasterwritertask.h" #include "gdal.h" -#include "gdal_utils.h" #include #include @@ -324,19 +323,17 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct return false; QString uri; - // This sends OGR/GDAL errors to the message log - QgsCPLErrorHandler handler; QStringList importResults; bool hasError = false; QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data ); - Q_FOREACH ( const QgsMimeDataUtils::Uri &u, lst ) + Q_FOREACH ( const QgsMimeDataUtils::Uri &dropUri, lst ) { // Check that we are not copying over self - if ( u.uri.startsWith( mPath ) ) + if ( dropUri.uri.startsWith( mPath ) ) { - importResults.append( tr( "You cannot import layer %1 over itself!" ).arg( u.name ) ); + importResults.append( tr( "You cannot import layer %1 over itself!" ).arg( dropUri.name ) ); hasError = true; } @@ -348,19 +345,19 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct QString error; // Common checks for raster and vector // aspatial is treated like vector - if ( u.layerType == QStringLiteral( "vector" ) ) + if ( dropUri.layerType == QStringLiteral( "vector" ) ) { // open the source layer - srcLayer = u.vectorLayer( owner, error ); + srcLayer = dropUri.vectorLayer( owner, error ); isVector = true; } else { - srcLayer = u.rasterLayer( owner, error ); + srcLayer = dropUri.rasterLayer( owner, error ); } if ( !srcLayer ) { - importResults.append( tr( "%1: %2" ).arg( u.name ).arg( error ) ); + importResults.append( tr( "%1: %2" ).arg( dropUri.name ).arg( error ) ); hasError = true; continue; } @@ -375,22 +372,22 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct // Q_FOREACH won't detach ... for ( const auto child : children() ) { - if ( child->name() == u.name ) + if ( child->name() == dropUri.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 ) + tr( "Destination layer %1 already exists. Do you want to overwrite it?" ).arg( dropUri.name ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes ) { - if ( isVector ) + if ( isVector ) // Import vectors and aspatial { QgsVectorLayer *vectorSrcLayer = dynamic_cast < QgsVectorLayer * >( srcLayer ); QVariantMap options; options.insert( QStringLiteral( "driverName" ), QStringLiteral( "GPKG" ) ); options.insert( QStringLiteral( "update" ), true ); options.insert( QStringLiteral( "overwrite" ), true ); - options.insert( QStringLiteral( "layerName" ), u.name ); + options.insert( QStringLiteral( "layerName" ), dropUri.name ); std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( vectorSrcLayer, uri, QStringLiteral( "ogr" ), vectorSrcLayer->crs(), options, owner ) ); // when export is successful: @@ -408,7 +405,7 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct { 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->setMessage( tr( "Failed to import some vector layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText ); output->showMessage(); } } ); @@ -417,42 +414,39 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct } else // Import raster { - // In case we need it - // QgsRasterLayer* rasterSrcLayer = dynamic_cast < QgsRasterLayer* > ( srcLayer ); - const char *args[] = { "-of", "gpkg", "-co", QStringLiteral( "RASTER_TABLE=%1" ).arg( u.name ).toUtf8().constData(), "-co", "APPEND_SUBDATASET=YES", nullptr }; - GDALTranslateOptions *psOptions = GDALTranslateOptionsNew( ( char ** )args, nullptr ); - GDALDatasetH hSrcDS = GDALOpen( u.uri.toUtf8().constData(), GA_ReadOnly ); - if ( ! hSrcDS ) + std::unique_ptr< QgsGeoPackageRasterWriterTask > exportTask( new QgsGeoPackageRasterWriterTask( dropUri, mPath ) ); + // when export is successful: + connect( exportTask.get(), &QgsGeoPackageRasterWriterTask::writeComplete, this, [ = ]() { - importResults.append( tr( "Failed to open source layer %1! See the message logs for details.\n\n" ).arg( u.name ) ); - hasError = true; - } - else + // 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(), &QgsGeoPackageRasterWriterTask::errorOccurred, this, [ = ]( QgsGeoPackageRasterWriter::WriterError error, const QString & errorMessage ) { - CPLErrorReset(); - GDALDatasetH hOutDS = GDALTranslate( mPath.toUtf8().constData(), hSrcDS, psOptions, NULL ); - if ( ! hOutDS ) + if ( error != QgsGeoPackageRasterWriter::WriterError::ErrUserCanceled ) { - importResults.append( tr( "Failed to import layer %1! See the message logs for details.\n\n" ).arg( u.name ) ); - hasError = true; + QgsMessageOutput *output = QgsMessageOutput::createMessageOutput(); + output->setTitle( tr( "Import to GeoPackage database" ) ); + output->setMessage( tr( "Failed to import some raster layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText ); + output->showMessage(); } - else // All good! - { - GDALClose( hOutDS ); - // 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(); - } - GDALClose( hSrcDS ); - } - GDALTranslateOptionsFree( psOptions ); + // Always try to delete the imported raster, in case the gpkg has been left + // in an inconsistent status. Ignore delete errors. + QString deleteErr; + deleteGeoPackageRasterLayer( QStringLiteral( "GPKG:%1:%2" ).arg( mPath, dropUri.name ), deleteErr ); + } ); + + QgsApplication::taskManager()->addTask( exportTask.release() ); } } // do not overwrite } else { - importResults.append( tr( "%1: Not a valid layer!" ).arg( u.name ) ); + importResults.append( tr( "%1: Not a valid layer!" ).arg( dropUri.name ) ); hasError = true; } } // check for self copy @@ -495,6 +489,124 @@ QgsLayerItem::LayerType QgsGeoPackageConnectionItem::layerTypeFromDb( const QStr return QgsLayerItem::LayerType::TableLayer; } +bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri, QString &errCause ) +{ + bool result = false; + // Better safe than sorry + if ( ! uri.isEmpty( ) ) + { + QStringList pieces( uri.split( ':' ) ); + if ( pieces.size() != 3 ) + { + errCause = QStringLiteral( "Layer URI is malformed: layer %1 cannot be deleted!" ).arg( uri ); + } + else + { + QString baseUri = pieces.at( 1 ); + QString layerName = pieces.at( 2 ); + sqlite3 *handle; + int status = sqlite3_open_v2( baseUri.toUtf8().constData(), &handle, SQLITE_OPEN_READWRITE, NULL ); + if ( status != SQLITE_OK ) + { + errCause = sqlite3_errmsg( handle ); + } + else + { + // Remove table + char *errmsg = nullptr; + char *sql = sqlite3_mprintf( + "DROP table IF EXISTS \"%w\";" + "DELETE FROM gpkg_contents WHERE table_name = '%q';" + "DELETE FROM gpkg_tile_matrix WHERE table_name = '%q';" + "DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%q';", + layerName.toUtf8().constData(), + layerName.toUtf8().constData(), + layerName.toUtf8().constData(), + layerName.toUtf8().constData() ); + status = sqlite3_exec( + handle, /* An open database */ + sql, /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + &errmsg /* Error msg written here */ + ); + sqlite3_free( sql ); + // Remove from optional tables, may silently fail + QStringList optionalTables; + optionalTables << QStringLiteral( "gpkg_extensions" ) + << QStringLiteral( "gpkg_metadata_reference" ); + Q_FOREACH ( const QString &tableName, optionalTables ) + { + char *sql = sqlite3_mprintf( "DELETE FROM %w WHERE table_name = '%q'", + tableName.toUtf8().constData(), + layerName.toUtf8().constData() ); + sqlite3_exec( + handle, /* An open database */ + sql, /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + NULL /* Error msg written here */ + ); + sqlite3_free( sql ); + } + // Other tables, ignore errors + { + char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_coverage_ancillary WHERE tile_matrix_set_name = '%q'", + layerName.toUtf8().constData() ); + sqlite3_exec( + handle, /* An open database */ + sql, /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + NULL /* Error msg written here */ + ); + sqlite3_free( sql ); + } + { + char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_tile_ancillary WHERE tpudt_name = '%q'", + layerName.toUtf8().constData() ); + sqlite3_exec( + handle, /* An open database */ + sql, /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + NULL /* Error msg written here */ + ); + sqlite3_free( sql ); + } + // Vacuum + { + sqlite3_exec( + handle, /* An open database */ + "VACUUM", /* SQL to be evaluated */ + NULL, /* Callback function */ + NULL, /* 1st argument to callback */ + NULL /* Error msg written here */ + ); + } + + if ( status == SQLITE_OK ) + { + result = true; + } + else + { + errCause = tr( "There was an error deleting the layer %1: %2" ).arg( layerName, QString::fromUtf8( errmsg ) ); + } + sqlite3_free( errmsg ); + } + sqlite3_close( handle ); + } + } + else + { + // This should never happen! + errCause = tr( "Layer URI is empty: layer cannot be deleted!" ); + } + return result; +} + + void QgsGeoPackageConnectionItem::deleteConnection() { QgsGeoPackageConnection::deleteConnection( name() ); @@ -563,7 +675,7 @@ void QgsGeoPackageAbstractLayerItem::deleteLayer() { QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer %1 deleted successfully." ).arg( mName ) ); if ( mParent ) - mParent->refresh(); + mParent->refreshConnections(); } } else @@ -602,83 +714,7 @@ QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent, bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause ) { - bool result = false; - // Better safe than sorry - if ( ! mUri.isEmpty( ) ) - { - QStringList pieces( mUri.split( ':' ) ); - if ( pieces.size() != 3 ) - { - errCause = QStringLiteral( "Layer URI is malformed: layer %1 cannot be deleted!" ).arg( mName ); - } - else - { - QString baseUri = pieces.at( 1 ); - QString layerName = pieces.at( 2 ); - sqlite3 *handle; - int status = sqlite3_open_v2( baseUri.toUtf8().constData(), &handle, SQLITE_OPEN_READWRITE, NULL ); - if ( status != SQLITE_OK ) - { - errCause = sqlite3_errmsg( handle ); - } - else - { - // Remove table - char *errmsg = nullptr; - char *sql = sqlite3_mprintf( - "DROP table %w;" - "DELETE FROM gpkg_contents WHERE table_name = '%q';" - "DELETE FROM gpkg_tile_matrix WHERE table_name = '%q';" - "DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%q';", - layerName.toUtf8().constData(), - layerName.toUtf8().constData(), - layerName.toUtf8().constData(), - layerName.toUtf8().constData() ); - status = sqlite3_exec( - handle, /* An open database */ - sql, /* SQL to be evaluated */ - NULL, /* Callback function */ - NULL, /* 1st argument to callback */ - &errmsg /* Error msg written here */ - ); - sqlite3_free( sql ); - // Remove from optional tables, may silently fail - QStringList optionalTables; - optionalTables << QStringLiteral( "gpkg_extensions" ) - << QStringLiteral( "gpkg_metadata_reference" ); - for ( const auto tableName : optionalTables ) - { - char *sql = sqlite3_mprintf( "DELETE FROM table %w WHERE table_name = '%q", - tableName.toUtf8().constData(), - layerName.toUtf8().constData() ); - sqlite3_exec( - handle, /* An open database */ - sql, /* SQL to be evaluated */ - NULL, /* Callback function */ - NULL, /* 1st argument to callback */ - NULL /* Error msg written here */ - ); - sqlite3_free( sql ); - } - if ( status == SQLITE_OK ) - { - result = true; - } - else - { - errCause = tr( "There was an error deleting the layer: %1" ).arg( QString::fromUtf8( errmsg ) ); - } - sqlite3_free( errmsg ); - } - sqlite3_close( handle ); - } - } - else - { - // This should never happen! - errCause = QStringLiteral( "Layer URI is empty: layer %1 cannot be deleted!" ).arg( mName ); - } - return result; + return QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( mUri, errCause ); } diff --git a/src/providers/ogr/qgsgeopackagedataitems.h b/src/providers/ogr/qgsgeopackagedataitems.h index 5b2f41fb846..9c60b27bc64 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.h +++ b/src/providers/ogr/qgsgeopackagedataitems.h @@ -82,6 +82,9 @@ class QgsGeoPackageConnectionItem : public QgsDataCollectionItem //! Return the layer type from \a geometryType static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType ); + //! Delete a geopackage layer + static bool deleteGeoPackageRasterLayer( const QString uri, QString &errCause ); + public slots: #ifdef HAVE_GUI void editConnection(); diff --git a/src/providers/ogr/qgsgeopackagerasterwriter.cpp b/src/providers/ogr/qgsgeopackagerasterwriter.cpp new file mode 100644 index 00000000000..dd52a1b56e6 --- /dev/null +++ b/src/providers/ogr/qgsgeopackagerasterwriter.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + qgsgeopackagerasterwriter.cpp - QgsGeoPackageRasterWriter + + --------------------- + begin : 23.8.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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. * + * * + ***************************************************************************/ + +///@cond PRIVATE + +#include "gdal.h" +#include "gdal_utils.h" + +#include "qgsgeopackagerasterwriter.h" +#include "qgscplerrorhandler.h" + +#include + +QgsGeoPackageRasterWriter::QgsGeoPackageRasterWriter( const QgsMimeDataUtils::Uri sourceUri, const QString outputUrl ): + mSourceUri( sourceUri ), + mOutputUrl( outputUrl ) +{ + +} + +QgsGeoPackageRasterWriter::WriterError QgsGeoPackageRasterWriter::writeRaster( QgsFeedback *feedback, QString *errorMessage ) +{ + const char *args[] = { "-of", "gpkg", "-co", QStringLiteral( "RASTER_TABLE=%1" ).arg( mSourceUri.name ).toUtf8().constData(), "-co", "APPEND_SUBDATASET=YES", nullptr }; + // This sends OGR/GDAL errors to the message log + QgsCPLErrorHandler handler; + GDALTranslateOptions *psOptions = GDALTranslateOptionsNew( ( char ** )args, nullptr ); + + GDALTranslateOptionsSetProgress( psOptions, [ ]( double dfComplete, const char *pszMessage, void *pProgressData ) -> int + { + Q_UNUSED( pszMessage ); + QgsFeedback *feedback = static_cast< QgsFeedback * >( pProgressData ); + feedback->setProgress( dfComplete * 100 ); + return ! feedback->isCanceled(); + }, feedback ); + + GDALDatasetH hSrcDS = GDALOpen( mSourceUri.uri.toUtf8().constData(), GA_ReadOnly ); + if ( ! hSrcDS ) + { + *errorMessage = QObject::tr( "Failed to open source layer %1! See the OGR panel in the message logs for details.\n\n" ).arg( mSourceUri.name ); + mHasError = true; + } + else + { + CPLErrorReset(); + GDALDatasetH hOutDS = GDALTranslate( mOutputUrl.toUtf8().constData(), hSrcDS, psOptions, NULL ); + if ( ! hOutDS ) + { + *errorMessage = QObject::tr( "Failed to import layer %1! See the OGR panel in the message logs for details.\n\n" ).arg( mSourceUri.name ); + mHasError = true; + } + else // All good! + { + GDALClose( hOutDS ); + } + GDALClose( hSrcDS ); + } + GDALTranslateOptionsFree( psOptions ); + return ( feedback && feedback->isCanceled() ) ? ErrUserCanceled : ( mHasError ? WriteError : NoError ) ; +} + +///@endcond diff --git a/src/providers/ogr/qgsgeopackagerasterwriter.h b/src/providers/ogr/qgsgeopackagerasterwriter.h new file mode 100644 index 00000000000..0d8ccef1c96 --- /dev/null +++ b/src/providers/ogr/qgsgeopackagerasterwriter.h @@ -0,0 +1,52 @@ +/*************************************************************************** + qgsgeopackagerasterwriter.h - QgsGeoPackageRasterWriter + + --------------------- + begin : 23.8.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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 QGSGEOPACKAGERASTERWRITER_H +#define QGSGEOPACKAGERASTERWRITER_H + +///@cond PRIVATE + +#define SIP_NO_FILE + +#include "qgsmimedatautils.h" +#include "qgsfeedback.h" + +class QgsGeoPackageRasterWriter +{ + public: + + //! Error codes + enum WriterError + { + NoError = 0, //!< No errors were encountered + WriteError, //! Generic GDAL Translate error + ErrUserCanceled, //!< User canceled the export + }; + + QgsGeoPackageRasterWriter( const QgsMimeDataUtils::Uri sourceUri, const QString destinationPath ); + WriterError writeRaster( QgsFeedback *feedback, QString *errorMessage ); + const QString outputUrl() const { return mOutputUrl; } + + private: + QgsMimeDataUtils::Uri mSourceUri; + QString mOutputUrl; + bool mHasError = false; +}; + + +///@endcond + +#endif // QGSGEOPACKAGERASTERWRITER_H + diff --git a/src/providers/ogr/qgsgeopackagerasterwritertask.cpp b/src/providers/ogr/qgsgeopackagerasterwritertask.cpp new file mode 100644 index 00000000000..989b9f0f9c8 --- /dev/null +++ b/src/providers/ogr/qgsgeopackagerasterwritertask.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + qgsgeopackagerasterwritertask.cpp - QgsGeoPackageRasterWriterTask + + --------------------- + begin : 23.8.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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 "qgsgeopackagerasterwritertask.h" + + +///@cond PRIVATE + + +QgsGeoPackageRasterWriterTask::QgsGeoPackageRasterWriterTask( const QgsMimeDataUtils::Uri sourceUri, const QString destinationPath ) + : QgsTask( tr( "Saving %1" ).arg( destinationPath ), QgsTask::CanCancel ) + , mWriter( sourceUri, destinationPath ) + , mFeedback( new QgsFeedback() ) +{ + +} + +void QgsGeoPackageRasterWriterTask::cancel() +{ + mError = QgsGeoPackageRasterWriter::WriterError::ErrUserCanceled; + mFeedback.get()->cancel(); +} + +bool QgsGeoPackageRasterWriterTask::run() +{ + connect( mFeedback.get(), &QgsFeedback::progressChanged, this, &QgsGeoPackageRasterWriterTask::setProgress ); + mError = mWriter.writeRaster( mFeedback.get(), &mErrorMessage ); + return mError == QgsGeoPackageRasterWriter::WriterError::NoError; +} + +void QgsGeoPackageRasterWriterTask::finished( bool result ) +{ + if ( result ) + { + emit writeComplete( mWriter.outputUrl() ); + } + else + { + emit errorOccurred( mError, mErrorMessage ); + } +} + +///@endcond diff --git a/src/providers/ogr/qgsgeopackagerasterwritertask.h b/src/providers/ogr/qgsgeopackagerasterwritertask.h new file mode 100644 index 00000000000..946f0a096bd --- /dev/null +++ b/src/providers/ogr/qgsgeopackagerasterwritertask.h @@ -0,0 +1,83 @@ +/*************************************************************************** + qgsgeopackagerasterwritertask.h - QgsGeoPackageRasterWriterTask + + --------------------- + begin : 23.8.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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 QGSGEOPACKAGERASTERWRITERTASK_H +#define QGSGEOPACKAGERASTERWRITERTASK_H + + +///@cond PRIVATE + +#define SIP_NO_FILE + +#include "qgis_core.h" +#include "qgsgeopackagerasterwriter.h" +#include "qgstaskmanager.h" +#include "qgsfeedback.h" + + +/** + * \class QgsGeoPackageRasterWriterTask + * QgsTask task which performs a QgsGeoPackageRasterWriter layer saving operation as a background + * task. This can be used to save a raster layer out to a file without blocking the + * QGIS interface. + * \since QGIS 3.0 + * \see QgsGeoPackageRasterWriterTask + */ +class QgsGeoPackageRasterWriterTask : public QgsTask +{ + Q_OBJECT + + public: + + /** + * Constructor for QgsVectorFileWriterTask. Takes a source \a layer, destination \a fileName + * and save \a options. + */ + QgsGeoPackageRasterWriterTask( const QgsMimeDataUtils::Uri sourceUri, const QString destinationPath ); + + virtual void cancel() override; + + signals: + + /** + * Emitted when writing the layer is successfully completed. The \a newFilename + * parameter indicates the file path for the written file. + */ + void writeComplete( const QString &newFilename ); + + /** + * Emitted when an error occurs which prevented the file being written (or if + * the task is canceled). The writing \a error and \a errorMessage will be reported. + */ + void errorOccurred( QgsGeoPackageRasterWriter::WriterError error, const QString &errorMessage ); + + protected: + + virtual bool run() override; + virtual void finished( bool result ) override; + + private: + + QgsGeoPackageRasterWriter mWriter; + std::unique_ptr< QgsFeedback > mFeedback; + QgsGeoPackageRasterWriter::WriterError mError = QgsGeoPackageRasterWriter::WriterError::NoError ; + QString mErrorMessage; + +}; + + +///@endcond + +#endif // QGSGEOPACKAGERASTERWRITERTASK_H From 4f967e4298333f6309d3d531e401688a5de6941b Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 24 Aug 2017 17:24:12 +0200 Subject: [PATCH 113/364] Removed not qobject from moc --- src/providers/ogr/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/providers/ogr/CMakeLists.txt b/src/providers/ogr/CMakeLists.txt index 572abbf9c0c..37fa3aae361 100644 --- a/src/providers/ogr/CMakeLists.txt +++ b/src/providers/ogr/CMakeLists.txt @@ -19,7 +19,6 @@ SET(OGR_MOC_HDRS qgsogrsourceselect.h qgsgeopackagedataitems.h qgsgeopackageconnection.h - qgsgeopackagerasterwriter.h qgsgeopackagerasterwritertask.h ) From 4b009f96ecb92db0056c18285cb1b8375c239ed1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 24 Aug 2017 16:24:50 +1000 Subject: [PATCH 114/364] Use std::round instead of qRound Now that our minimum VS studio version allowed supports std::round, we should use that in place of Qt's qRound method. Because: - it doesn't truncate to int, resulting in unpredictable behaviour (refs #16925) - better to stick to standard c++ methods wherever possible, since they're likely better supported and optimised by the compilers - it's a tiny reduction to the barrier for entry to QGIS development (I'm sick of pointing out the need to use qRound during PR reviews!) --- python/core/qgis.sip | 6 - src/analysis/raster/qgsalignraster.cpp | 8 +- src/app/composer/qgscomposermapwidget.cpp | 2 +- src/app/pluginmanager/qgspluginmanager.cpp | 4 +- src/app/qgsalignrasterdialog.cpp | 2 +- src/app/qgsdecorationgrid.cpp | 2 +- src/app/qgsdecorationscalebar.cpp | 2 +- src/app/qgsmaptoolrotatefeature.cpp | 2 +- src/app/qgsmeasuredialog.cpp | 2 +- src/core/composer/qgscomposermapgrid.cpp | 2 +- src/core/effects/qgsimageoperation.cpp | 2 +- src/core/expression/qgsexpressionfunction.cpp | 4 +- src/core/geometry/qgsgeos.cpp | 12 +- .../processing/qgsprocessingparameters.cpp | 4 +- src/core/qgis.h | 8 +- src/core/qgscolorramp.cpp | 4 +- src/core/qgsdatadefinedsizelegend.cpp | 16 +- src/core/qgsfield.cpp | 4 +- src/core/qgsmaptopixelgeometrysimplifier.cpp | 8 +- src/core/qgspointxy.cpp | 20 +- src/core/qgsproperty.cpp | 2 +- src/core/qgsvectorlayerlabeling.cpp | 2 +- src/core/raster/qgscolorrampshader.cpp | 2 +- src/core/raster/qgsrasterblock.cpp | 8 +- src/core/raster/qgsrasterchecker.cpp | 2 +- src/core/raster/qgsrasterdataprovider.cpp | 12 +- src/core/raster/qgsrasterinterface.cpp | 4 +- src/core/raster/qgsrasterprojector.cpp | 4 +- src/core/symbology/qgsfillsymbollayer.cpp | 6 +- src/core/symbology/qgsheatmaprenderer.cpp | 2 +- src/core/symbology/qgsmarkersymbollayer.cpp | 2 +- src/core/symbology/qgssymbollayerutils.cpp | 18 +- src/gui/editorwidgets/qgsspinbox.cpp | 2 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 2 +- src/gui/qgscolorwidgets.cpp | 14 +- src/gui/qgsgradientstopeditor.cpp | 2 +- src/gui/qgsmapcanvasmap.cpp | 2 +- src/gui/qgsmapmouseevent.cpp | 2 +- src/gui/qgsmaptool.cpp | 2 +- src/gui/qgsmaptoolidentify.cpp | 4 +- src/gui/qgsrasterlayersaveasdialog.cpp | 4 +- src/gui/qgsscalecombobox.cpp | 4 +- .../symbology/qgsellipsesymbollayerwidget.cpp | 2 +- src/gui/symbology/qgssvgselectorwidget.cpp | 2 +- src/gui/symbology/qgssymbollayerwidget.cpp | 4 +- .../grass/qtermwidget/TerminalDisplay.cpp | 3332 +++++++++-------- src/providers/gdal/qgsgdalprovider.cpp | 12 +- tests/src/analysis/testqgsalignraster.cpp | 11 +- tests/src/app/testqgisappclipboard.cpp | 4 +- tests/src/app/testqgsnodetool.cpp | 2 +- tests/src/core/testqgis.cpp | 15 - tests/src/core/testqgstaskmanager.cpp | 16 +- tests/src/core/testqgsvectordataprovider.cpp | 2 +- .../src/providers/testqgswcspublicservers.cpp | 2 +- 54 files changed, 1811 insertions(+), 1809 deletions(-) diff --git a/python/core/qgis.sip b/python/core/qgis.sip index 796fbb18338..c5598d33ade 100644 --- a/python/core/qgis.sip +++ b/python/core/qgis.sip @@ -153,12 +153,6 @@ Compare two doubles using specified number of significant digits :rtype: bool %End -double qgsRound( double x ); -%Docstring -A round function which returns a double to guard against overflows - :rtype: float -%End - double qgsRound( double number, double places ); %Docstring Returns a double ``number``, rounded (as close as possible) to the specified number of ``places``. diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index ec0d63b54cb..8e5f68357d4 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -30,16 +30,16 @@ static double ceil_with_tolerance( double value ) { - if ( qAbs( value - qRound( value ) ) < 1e-6 ) - return qRound( value ); + if ( qAbs( value - std::round( value ) ) < 1e-6 ) + return std::round( value ); else return qCeil( value ); } static double floor_with_tolerance( double value ) { - if ( qAbs( value - qRound( value ) ) < 1e-6 ) - return qRound( value ); + if ( qAbs( value - std::round( value ) ) < 1e-6 ) + return std::round( value ); else return qFloor( value ); } diff --git a/src/app/composer/qgscomposermapwidget.cpp b/src/app/composer/qgscomposermapwidget.cpp index 869de892114..8e657ae6ef0 100644 --- a/src/app/composer/qgscomposermapwidget.cpp +++ b/src/app/composer/qgscomposermapwidget.cpp @@ -444,7 +444,7 @@ void QgsComposerMapWidget::on_mScaleLineEdit_editingFinished() return; } - if ( qRound( scaleDenominator ) == qRound( mComposerMap->scale() ) ) + if ( std::round( scaleDenominator ) == std::round( mComposerMap->scale() ) ) return; mComposerMap->beginCommand( tr( "Map scale changed" ) ); diff --git a/src/app/pluginmanager/qgspluginmanager.cpp b/src/app/pluginmanager/qgspluginmanager.cpp index dbe73d072ae..843805137b6 100644 --- a/src/app/pluginmanager/qgspluginmanager.cpp +++ b/src/app/pluginmanager/qgspluginmanager.cpp @@ -699,8 +699,8 @@ void QgsPluginManager::showPluginDetails( QStandardItem *item ) voteLabel->show(); voteSlider->show(); voteSubmit->show(); - QgsDebugMsg( QString( "vote slider:%1" ).arg( qRound( metadata->value( "average_vote" ).toFloat() ) ) ); - voteSlider->setValue( qRound( metadata->value( "average_vote" ).toFloat() ) ); + QgsDebugMsg( QString( "vote slider:%1" ).arg( std::round( metadata->value( "average_vote" ).toFloat() ) ) ); + voteSlider->setValue( std::round( metadata->value( "average_vote" ).toFloat() ) ); mCurrentPluginId = metadata->value( "plugin_id" ).toInt(); } else diff --git a/src/app/qgsalignrasterdialog.cpp b/src/app/qgsalignrasterdialog.cpp index fd0eb2b95d1..4bd21535ad6 100644 --- a/src/app/qgsalignrasterdialog.cpp +++ b/src/app/qgsalignrasterdialog.cpp @@ -61,7 +61,7 @@ struct QgsAlignRasterDialogProgress : public QgsAlignRaster::ProgressHandler explicit QgsAlignRasterDialogProgress( QProgressBar *pb ) : mPb( pb ) {} virtual bool progress( double complete ) override { - mPb->setValue( ( int ) qRound( complete * 100 ) ); + mPb->setValue( ( int ) std::round( complete * 100 ) ); qApp->processEvents(); // to actually show the progress in GUI return true; } diff --git a/src/app/qgsdecorationgrid.cpp b/src/app/qgsdecorationgrid.cpp index 5e7c28bc0a4..aab776a1a09 100644 --- a/src/app/qgsdecorationgrid.cpp +++ b/src/app/qgsdecorationgrid.cpp @@ -790,7 +790,7 @@ bool QgsDecorationGrid::getIntervalFromExtent( double *values, bool useXAxis ) int factor = pow( 10, floor( log10( interval ) ) ); if ( factor != 0 ) { - interval2 = qRound( interval / factor ) * factor; + interval2 = std::round( interval / factor ) * factor; QgsDebugMsg( QString( "interval2: %1" ).arg( interval2 ) ); if ( !qgsDoubleNear( interval2, 0.0 ) ) interval = interval2; diff --git a/src/app/qgsdecorationscalebar.cpp b/src/app/qgsdecorationscalebar.cpp index cdb5692decb..dae366accf3 100644 --- a/src/app/qgsdecorationscalebar.cpp +++ b/src/app/qgsdecorationscalebar.cpp @@ -164,7 +164,7 @@ void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRender if ( mSnapping ) { double scaler = pow( 10.0, myPowerOf10 ); - myActualSize = qRound( myActualSize / scaler ) * scaler; + myActualSize = std::round( myActualSize / scaler ) * scaler; myScaleBarWidth = myActualSize / myMapUnitsPerPixelDouble; } diff --git a/src/app/qgsmaptoolrotatefeature.cpp b/src/app/qgsmaptoolrotatefeature.cpp index 4d62c571dca..99a051b64e6 100644 --- a/src/app/qgsmaptoolrotatefeature.cpp +++ b/src/app/qgsmaptoolrotatefeature.cpp @@ -89,7 +89,7 @@ void QgsAngleMagnetWidget::setAngle( double angle ) const int magnet = mMagnetSpinBox->value(); if ( magnet ) { - mAngleSpinBox->setValue( qRound( angle / magnet ) * magnet ); + mAngleSpinBox->setValue( std::round( angle / magnet ) * magnet ); } else { diff --git a/src/app/qgsmeasuredialog.cpp b/src/app/qgsmeasuredialog.cpp index 450bb0b50dd..8d7a7431330 100644 --- a/src/app/qgsmeasuredialog.cpp +++ b/src/app/qgsmeasuredialog.cpp @@ -280,7 +280,7 @@ QString QgsMeasureDialog::formatDistance( double distance, bool convertUnits ) c { // special handling for degrees - because we can't use smaller units (eg m->mm), we need to make sure there's // enough decimal places to show a usable measurement value - int minPlaces = qRound( log10( 1.0 / distance ) ) + 1; + int minPlaces = std::round( log10( 1.0 / distance ) ) + 1; decimals = qMax( decimals, minPlaces ); } return QgsDistanceArea::formatDistance( distance, decimals, mDistanceUnits, baseUnit ); diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index 7dfe1aa74eb..f3aa398c130 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -1445,7 +1445,7 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGr { QString hemisphere; - double coordRounded = qRound( value * pow( 10.0, mGridAnnotationPrecision ) ) / pow( 10.0, mGridAnnotationPrecision ); + double coordRounded = std::round( value * pow( 10.0, mGridAnnotationPrecision ) ) / pow( 10.0, mGridAnnotationPrecision ); if ( coord == QgsComposerMapGrid::Longitude ) { //don't use E/W suffixes if ambiguous (e.g., 180 degrees) diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index 496b0e8badf..910d4b7816c 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -349,7 +349,7 @@ void QgsImageOperation::MultiplyOpacityPixelOperation::operator()( QRgb &rgb, co { Q_UNUSED( x ); Q_UNUSED( y ); - rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), qBound( 0, qRound( mFactor * qAlpha( rgb ) ), 255 ) ); + rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), qBound( 0.0, std::round( mFactor * qAlpha( rgb ) ), 255.0 ) ); } // overlay color diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index aaa21e0b2f4..512c0628e42 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -2884,13 +2884,13 @@ static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext { double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); double scaler = pow( 10.0, QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) ); - return QVariant( qRound( number * scaler ) / scaler ); + return QVariant( std::round( number * scaler ) / scaler ); } if ( values.length() >= 1 ) { double number = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ); - return QVariant( qRound( number ) ); + return QVariant( qlonglong( std::round( number ) ) ); } return QVariant(); diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index f639422ee82..c610bee2dfd 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -1701,11 +1701,11 @@ GEOSCoordSequence *QgsGeos::createCoordinateSequence( const QgsCurve *curve, dou { for ( int i = 0; i < numOutPoints; ++i ) { - GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, i, qgsRound( line->xAt( i % numPoints ) / precision ) * precision ); - GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, i, qgsRound( line->yAt( i % numPoints ) / precision ) * precision ); + GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, i, std::round( line->xAt( i % numPoints ) / precision ) * precision ); + GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, i, std::round( line->yAt( i % numPoints ) / precision ) * precision ); if ( hasZ ) { - GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, i, 2, qgsRound( line->zAt( i % numPoints ) / precision ) * precision ); + GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, i, 2, std::round( line->zAt( i % numPoints ) / precision ) * precision ); } if ( hasM ) { @@ -1765,11 +1765,11 @@ GEOSGeometry *QgsGeos::createGeosPointXY( double x, double y, bool hasZ, double } if ( precision > 0. ) { - GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, 0, qgsRound( x / precision ) * precision ); - GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, 0, qgsRound( y / precision ) * precision ); + GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, 0, std::round( x / precision ) * precision ); + GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, 0, std::round( y / precision ) * precision ); if ( hasZ ) { - GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, 0, 2, qgsRound( z / precision ) * precision ); + GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, 0, 2, std::round( z / precision ) * precision ); } } else diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index c47e0fc6a38..5caea31263e 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -112,13 +112,13 @@ int QgsProcessingParameters::parameterAsInt( const QgsProcessingParameterDefinit //work around this by first converting to double, and then checking whether the double is convertible to int if ( ok ) { - double round = qgsRound( dbl ); + double round = std::round( dbl ); if ( round > INT_MAX || round < -INT_MAX ) { //double too large to fit in int return 0; } - return qRound( dbl ); + return std::round( dbl ); } return val.toInt(); diff --git a/src/core/qgis.h b/src/core/qgis.h index 3cac2097084..2d80dd59dc7 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -236,13 +236,7 @@ inline bool qgsDoubleNearSig( double a, double b, int significantDigits = 10 ) double br = frexp( b, &bexp ); return aexp == bexp && - qRound( ar * pow( 10.0, significantDigits ) ) == qRound( br * pow( 10.0, significantDigits ) ); -} - -//! A round function which returns a double to guard against overflows -inline double qgsRound( double x ) -{ - return x < 0.0 ? std::ceil( x - 0.5 ) : std::floor( x + 0.5 ); + std::round( ar * pow( 10.0, significantDigits ) ) == std::round( br * pow( 10.0, significantDigits ) ); } /** diff --git a/src/core/qgscolorramp.cpp b/src/core/qgscolorramp.cpp index 329d3dbd9e9..715333a827a 100644 --- a/src/core/qgscolorramp.cpp +++ b/src/core/qgscolorramp.cpp @@ -396,7 +396,7 @@ QList QgsLimitedRandomColorRamp::randomColors( int count, //see http://basecase.org/env/on-rainbows for more details currentHueAngle += 137.50776; //scale hue to between hueMax and hueMin - h = qBound( 0, qRound( ( fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 359 ); + h = qBound( 0.0, std::round( ( fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 359.0 ); s = qBound( 0, ( qrand() % ( safeSatMax - safeSatMin + 1 ) ) + safeSatMin, 255 ); v = qBound( 0, ( qrand() % ( safeValMax - safeValMin + 1 ) ) + safeValMin, 255 ); colors.append( QColor::fromHsv( h, s, v ) ); @@ -468,7 +468,7 @@ void QgsRandomColorRamp::setTotalColorCount( const int colorCount ) //build up a list of colors for ( int idx = 0; idx < colorCount; ++ idx ) { - int h = qRound( currentHue ) % 360; + int h = static_cast< int >( std::round( currentHue ) ) % 360; int s = ( qrand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN; int v = ( qrand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN; mPrecalculatedColors << QColor::fromHsv( h, s, v ); diff --git a/src/core/qgsdatadefinedsizelegend.cpp b/src/core/qgsdatadefinedsizelegend.cpp index db2952c9605..a56cb8ca5a3 100644 --- a/src/core/qgsdatadefinedsizelegend.cpp +++ b/src/core/qgsdatadefinedsizelegend.cpp @@ -156,9 +156,9 @@ void QgsDataDefinedSizeLegend::drawCollapsedLegend( QgsRenderContext &context, Q // make sure we draw bigger symbols first std::sort( classes.begin(), classes.end(), []( const SizeClass & a, const SizeClass & b ) { return a.size > b.size; } ); - int hLengthLine = qRound( context.convertToPainterUnits( hLengthLineMM, QgsUnitTypes::RenderMillimeters ) ); - int hSpaceLineText = qRound( context.convertToPainterUnits( hSpaceLineTextMM, QgsUnitTypes::RenderMillimeters ) ); - int dpm = qRound( context.scaleFactor() * 1000 ); // scale factor = dots per millimeter + int hLengthLine = std::round( context.convertToPainterUnits( hLengthLineMM, QgsUnitTypes::RenderMillimeters ) ); + int hSpaceLineText = std::round( context.convertToPainterUnits( hSpaceLineTextMM, QgsUnitTypes::RenderMillimeters ) ); + int dpm = std::round( context.scaleFactor() * 1000 ); // scale factor = dots per millimeter // get font metrics - we need a temporary image just to get the metrics right for the given DPI QImage tmpImg( QSize( 1, 1 ), QImage::Format_ARGB32_Premultiplied ); @@ -194,10 +194,10 @@ void QgsDataDefinedSizeLegend::drawCollapsedLegend( QgsRenderContext &context, Q switch ( mVAlign ) { case AlignCenter: - symbolTopY << qRound( outputLargestSize / 2 - outputSymbolSize / 2 ); + symbolTopY << std::round( outputLargestSize / 2 - outputSymbolSize / 2 ); break; case AlignBottom: - symbolTopY << qRound( outputLargestSize - outputSymbolSize ); + symbolTopY << std::round( outputLargestSize - outputSymbolSize ); break; } } @@ -222,7 +222,7 @@ void QgsDataDefinedSizeLegend::drawCollapsedLegend( QgsRenderContext &context, Q int totalTextHeight = textBottomY - textTopY; int fullWidth = outputLargestSize + hLengthLine + hSpaceLineText + maxTextWidth; - int fullHeight = qMax( qRound( outputLargestSize ) - textTopY, totalTextHeight ); + int fullHeight = qMax( static_cast< int >( std::round( outputLargestSize ) ) - textTopY, totalTextHeight ); if ( outputSize ) *outputSize = QSize( fullWidth, fullHeight ); @@ -292,8 +292,8 @@ QImage QgsDataDefinedSizeLegend::collapsedLegendImage( QgsRenderContext &context QSize contentSize; drawCollapsedLegend( context, &contentSize ); - int padding = qRound( context.convertToPainterUnits( paddingMM, QgsUnitTypes::RenderMillimeters ) ); - int dpm = qRound( context.scaleFactor() * 1000 ); // scale factor = dots per millimeter + int padding = std::round( context.convertToPainterUnits( paddingMM, QgsUnitTypes::RenderMillimeters ) ); + int dpm = std::round( context.scaleFactor() * 1000 ); // scale factor = dots per millimeter QImage img( contentSize.width() + padding * 2, contentSize.height() + padding * 2, QImage::Format_ARGB32_Premultiplied ); img.setDotsPerMeterX( dpm ); diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 5648c24aa48..64a1bc2c9ca 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -249,14 +249,14 @@ bool QgsField::convertCompatible( QVariant &v ) const return false; } - double round = qgsRound( dbl ); + double round = std::round( dbl ); if ( round > INT_MAX || round < -INT_MAX ) { //double too large to fit in int v = QVariant( d->type ); return false; } - v = QVariant( qRound( dbl ) ); + v = QVariant( static_cast< int >( std::round( dbl ) ) ); return true; } diff --git a/src/core/qgsmaptopixelgeometrysimplifier.cpp b/src/core/qgsmaptopixelgeometrysimplifier.cpp index cf89a19f38b..43c1bfd8190 100644 --- a/src/core/qgsmaptopixelgeometrysimplifier.cpp +++ b/src/core/qgsmaptopixelgeometrysimplifier.cpp @@ -47,12 +47,12 @@ float QgsMapToPixelSimplifier::calculateLengthSquared2D( double x1, double y1, d bool QgsMapToPixelSimplifier::equalSnapToGrid( double x1, double y1, double x2, double y2, double gridOriginX, double gridOriginY, float gridInverseSizeXY ) { - int grid_x1 = qRound( ( x1 - gridOriginX ) * gridInverseSizeXY ); - int grid_x2 = qRound( ( x2 - gridOriginX ) * gridInverseSizeXY ); + int grid_x1 = std::round( ( x1 - gridOriginX ) * gridInverseSizeXY ); + int grid_x2 = std::round( ( x2 - gridOriginX ) * gridInverseSizeXY ); if ( grid_x1 != grid_x2 ) return false; - int grid_y1 = qRound( ( y1 - gridOriginY ) * gridInverseSizeXY ); - int grid_y2 = qRound( ( y2 - gridOriginY ) * gridInverseSizeXY ); + int grid_y1 = std::round( ( y1 - gridOriginY ) * gridInverseSizeXY ); + int grid_y2 = std::round( ( y2 - gridOriginY ) * gridInverseSizeXY ); if ( grid_y1 != grid_y2 ) return false; return true; diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index 591ca02ee51..3f09997f5b7 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -95,7 +95,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix double mySecondsY = double( myFloatMinutesY - myIntMinutesY ) * 60; //make sure rounding to specified precision doesn't create seconds >= 60 - if ( qRound( mySecondsX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( mySecondsX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) { mySecondsX = qMax( mySecondsX - 60, 0.0 ); myIntMinutesX++; @@ -105,7 +105,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix myDegreesX++; } } - if ( qRound( mySecondsY * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( mySecondsY * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) { mySecondsY = qMax( mySecondsY - 60, 0.0 ); myIntMinutesY++; @@ -138,18 +138,18 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix } //check if coordinate is all zeros for the specified precision, and if so, //remove the sign and hemisphere strings - if ( myDegreesX == 0 && myIntMinutesX == 0 && qRound( mySecondsX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 0 && myIntMinutesX == 0 && std::round( mySecondsX * pow( 10.0, precision ) ) == 0 ) { myXSign = QString(); myXHemisphere = QString(); } - if ( myDegreesY == 0 && myIntMinutesY == 0 && qRound( mySecondsY * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesY == 0 && myIntMinutesY == 0 && std::round( mySecondsY * pow( 10.0, precision ) ) == 0 ) { myYSign = QString(); myYHemisphere = QString(); } //also remove directional prefix from 180 degree longitudes - if ( myDegreesX == 180 && myIntMinutesX == 0 && qRound( mySecondsX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 180 && myIntMinutesX == 0 && std::round( mySecondsX * pow( 10.0, precision ) ) == 0 ) { myXHemisphere = QString(); } @@ -193,12 +193,12 @@ QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const double myFloatMinutesY = double( ( qAbs( mY ) - myDegreesY ) * 60 ); //make sure rounding to specified precision doesn't create minutes >= 60 - if ( qRound( myFloatMinutesX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( myFloatMinutesX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) { myFloatMinutesX = qMax( myFloatMinutesX - 60, 0.0 ); myDegreesX++; } - if ( qRound( myFloatMinutesY * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( myFloatMinutesY * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) { myFloatMinutesY = qMax( myFloatMinutesY - 60, 0.0 ); myDegreesY++; @@ -226,18 +226,18 @@ QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const } //check if coordinate is all zeros for the specified precision, and if so, //remove the sign and hemisphere strings - if ( myDegreesX == 0 && qRound( myFloatMinutesX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 0 && std::round( myFloatMinutesX * pow( 10.0, precision ) ) == 0 ) { myXSign = QString(); myXHemisphere = QString(); } - if ( myDegreesY == 0 && qRound( myFloatMinutesY * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesY == 0 && std::round( myFloatMinutesY * pow( 10.0, precision ) ) == 0 ) { myYSign = QString(); myYHemisphere = QString(); } //also remove directional prefix from 180 degree longitudes - if ( myDegreesX == 180 && qRound( myFloatMinutesX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 180 && std::round( myFloatMinutesX * pow( 10.0, precision ) ) == 0 ) { myXHemisphere = QString(); } diff --git a/src/core/qgsproperty.cpp b/src/core/qgsproperty.cpp index bea47a1a600..f058bbe89ec 100644 --- a/src/core/qgsproperty.cpp +++ b/src/core/qgsproperty.cpp @@ -605,7 +605,7 @@ int QgsProperty::valueAsInt( const QgsExpressionContext &context, int defaultVal { if ( ok ) *ok = true; - return qRound( dbl ); + return std::round( dbl ); } else { diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp index 54fa48e1da0..5a7a8918bdc 100644 --- a/src/core/qgsvectorlayerlabeling.cpp +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -203,7 +203,7 @@ std::unique_ptr backgroundToMarkerLayer( const QgsTextBack QColor strokeColor = settings.strokeColor(); if ( settings.opacity() < 1 ) { - int alpha = qRound( settings.opacity() * 255 ); + int alpha = std::round( settings.opacity() * 255 ); fillColor.setAlpha( alpha ); strokeColor.setAlpha( alpha ); } diff --git a/src/core/raster/qgscolorrampshader.cpp b/src/core/raster/qgscolorrampshader.cpp index 0d7657d1bf4..1202e8369db 100644 --- a/src/core/raster/qgscolorrampshader.cpp +++ b/src/core/raster/qgscolorrampshader.cpp @@ -286,7 +286,7 @@ void QgsColorRampShader::classifyColorRamp( const int classes, const int band, c // calculate a reasonable number of decimals to display double maxabs = log10( qMax( qAbs( max ), qAbs( min ) ) ); - int nDecimals = qRound( qMax( 3.0 + maxabs - log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); + int nDecimals = std::round( qMax( 3.0 + maxabs - log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); QList colorRampItems; for ( ; value_it != entryValues.end(); ++value_it, ++color_it ) diff --git a/src/core/raster/qgsrasterblock.cpp b/src/core/raster/qgsrasterblock.cpp index 796f3820726..29813294d9b 100644 --- a/src/core/raster/qgsrasterblock.cpp +++ b/src/core/raster/qgsrasterblock.cpp @@ -960,20 +960,20 @@ QRect QgsRasterBlock::subRect( const QgsRectangle &extent, int width, int height if ( subExtent.yMaximum() < extent.yMaximum() ) { - top = qRound( ( extent.yMaximum() - subExtent.yMaximum() ) / yRes ); + top = std::round( ( extent.yMaximum() - subExtent.yMaximum() ) / yRes ); } if ( subExtent.yMinimum() > extent.yMinimum() ) { - bottom = qRound( ( extent.yMaximum() - subExtent.yMinimum() ) / yRes ) - 1; + bottom = std::round( ( extent.yMaximum() - subExtent.yMinimum() ) / yRes ) - 1; } if ( subExtent.xMinimum() > extent.xMinimum() ) { - left = qRound( ( subExtent.xMinimum() - extent.xMinimum() ) / xRes ); + left = std::round( ( subExtent.xMinimum() - extent.xMinimum() ) / xRes ); } if ( subExtent.xMaximum() < extent.xMaximum() ) { - right = qRound( ( subExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1; + right = std::round( ( subExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1; } QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 ); QgsDebugMsgLevel( QString( "subRect: %1 %2 %3 %4" ).arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 ); diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index 9c4ce48177b..5d4fbe6064e 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -203,7 +203,7 @@ double QgsRasterChecker::tolerance( double val, int places ) { // float precision is about 7 decimal digits, double about 16 // default places = 6 - return 1. * qPow( 10, qRound( log10( qAbs( val ) ) - places ) ); + return 1. * qPow( 10, std::round( log10( qAbs( val ) ) - places ) ); } QString QgsRasterChecker::compareHead() diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index 1bcf6a5371e..75da35436db 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -102,10 +102,10 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b } // Calculate row/col limits (before tmpExtent is aligned) - int fromRow = qRound( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes ); - int toRow = qRound( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; - int fromCol = qRound( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes ); - int toCol = qRound( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1; + int fromRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes ); + int toRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; + int fromCol = std::round( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes ); + int toCol = std::round( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1; QgsDebugMsgLevel( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 ); @@ -133,8 +133,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b row = ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } - int tmpWidth = qRound( tmpExtent.width() / tmpXRes ); - int tmpHeight = qRound( tmpExtent.height() / tmpYRes ); + int tmpWidth = std::round( tmpExtent.width() / tmpXRes ); + int tmpHeight = std::round( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index ea57d70c683..66ada3f9575 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -536,8 +536,8 @@ void QgsRasterInterface::cumulativeCut( int bandNo, double myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount; int myCount = 0; - int myMinCount = static_cast< int >( qRound( lowerCount * myHistogram.nonNullCount ) ); - int myMaxCount = static_cast< int >( qRound( upperCount * myHistogram.nonNullCount ) ); + int myMinCount = static_cast< int >( std::round( lowerCount * myHistogram.nonNullCount ) ); + int myMaxCount = static_cast< int >( std::round( upperCount * myHistogram.nonNullCount ) ); bool myLowerFound = false; QgsDebugMsgLevel( QString( "binCount = %1 minimum = %2 maximum = %3 myBinXStep = %4" ).arg( myHistogram.binCount ).arg( myHistogram.minimum ).arg( myHistogram.maximum ).arg( myBinXStep ), 4 ); QgsDebugMsgLevel( QString( "myMinCount = %1 myMaxCount = %2" ).arg( myMinCount ).arg( myMaxCount ), 4 ); diff --git a/src/core/raster/qgsrasterprojector.cpp b/src/core/raster/qgsrasterprojector.cpp index 3a2391645cd..b57bdd0b06e 100644 --- a/src/core/raster/qgsrasterprojector.cpp +++ b/src/core/raster/qgsrasterprojector.cpp @@ -369,8 +369,8 @@ void ProjectorData::calcSrcRowsCols() QgsDebugMsgLevel( QString( "mSrcExtent.width = %1 mSrcExtent.height = %2" ).arg( mSrcExtent.width() ).arg( mSrcExtent.height() ), 4 ); // we have to round to keep alignment set in calcSrcExtent - mSrcRows = static_cast< int >( qRound( mSrcExtent.height() / myMinYSize ) ); - mSrcCols = static_cast< int >( qRound( mSrcExtent.width() / myMinXSize ) ); + mSrcRows = static_cast< int >( std::round( mSrcExtent.height() / myMinYSize ) ); + mSrcCols = static_cast< int >( std::round( mSrcExtent.width() / myMinXSize ) ); QgsDebugMsgLevel( QString( "mSrcRows = %1 mSrcCols = %2" ).arg( mSrcRows ).arg( mSrcCols ), 4 ); } diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index 8ee28e36fb8..c69cd237961 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -2561,8 +2561,8 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & { // We have to adjust marker intervals to integer pixel size to get // repeatable pattern. - double intervalScale = qRound( outputPixelInterval ) / outputPixelInterval; - outputPixelInterval = qRound( outputPixelInterval ); + double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval; + outputPixelInterval = std::round( outputPixelInterval ); for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ ) { @@ -2607,7 +2607,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & // Round offset to correspond to one pixel height, otherwise lines may // be shifted on tile border if offset falls close to pixel center - int offsetHeight = qRound( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) ); + int offsetHeight = std::round( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) ); outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 ); } diff --git a/src/core/symbology/qgsheatmaprenderer.cpp b/src/core/symbology/qgsheatmaprenderer.cpp index 001d0ad8d37..a71ce71d0e4 100644 --- a/src/core/symbology/qgsheatmaprenderer.cpp +++ b/src/core/symbology/qgsheatmaprenderer.cpp @@ -59,7 +59,7 @@ void QgsHeatmapRenderer::initializeValues( QgsRenderContext &context ) mValues.fill( 0 ); mCalculatedMaxValue = 0; mFeaturesRendered = 0; - mRadiusPixels = qRound( context.convertToPainterUnits( mRadius, mRadiusUnit, mRadiusMapUnitScale ) / mRenderQuality ); + mRadiusPixels = std::round( context.convertToPainterUnits( mRadius, mRadiusUnit, mRadiusMapUnitScale ) / mRenderQuality ); mRadiusSquared = mRadiusPixels * mRadiusPixels; } diff --git a/src/core/symbology/qgsmarkersymbollayer.cpp b/src/core/symbology/qgsmarkersymbollayer.cpp index cbfdf8a2b96..8603a863491 100644 --- a/src/core/symbology/qgsmarkersymbollayer.cpp +++ b/src/core/symbology/qgsmarkersymbollayer.cpp @@ -862,7 +862,7 @@ bool QgsSimpleMarkerSymbolLayer::prepareCache( QgsSymbolRenderContext &context ) double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale ); // calculate necessary image size for the cache - double pw = qRound( ( ( qgsDoubleNear( mPen.widthF(), 0.0 ) ? 1 : mPen.widthF() * 4 ) + 1 ) ) / 2 * 2; // make even (round up); handle cosmetic pen + double pw = static_cast< int >( std::round( ( ( qgsDoubleNear( mPen.widthF(), 0.0 ) ? 1 : mPen.widthF() * 4 ) + 1 ) ) ) / 2 * 2; // make even (round up); handle cosmetic pen int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width double center = imageSize / 2.0; diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 9c0bfe4fec6..e2b5baf6f7a 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3412,9 +3412,9 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & QRegExp rgbPercentFormatRx( "^\\s*(?:rgb)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*\\)?\\s*;?\\s*$" ); if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 ) { - int r = qRound( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 ); - int g = qRound( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 ); - int b = qRound( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 ); + int r = std::round( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 ); + int g = std::round( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 ); + int b = std::round( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 ); parsedColor.setRgb( r, g, b ); if ( parsedColor.isValid() ) { @@ -3430,7 +3430,7 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & int r = rgbaFormatRx.cap( 1 ).toInt(); int g = rgbaFormatRx.cap( 2 ).toInt(); int b = rgbaFormatRx.cap( 3 ).toInt(); - int a = qRound( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 ); + int a = std::round( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 ); parsedColor.setRgb( r, g, b, a ); if ( parsedColor.isValid() ) { @@ -3443,10 +3443,10 @@ QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool & QRegExp rgbaPercentFormatRx( "^\\s*(?:rgba)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" ); if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 ) { - int r = qRound( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 ); - int g = qRound( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 ); - int b = qRound( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 ); - int a = qRound( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 ); + int r = std::round( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 ); + int g = std::round( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 ); + int b = std::round( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 ); + int a = std::round( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 ); parsedColor.setRgb( r, g, b, a ); if ( parsedColor.isValid() ) { @@ -4075,7 +4075,7 @@ double QgsSymbolLayerUtils::rescaleUom( double size, QgsUnitTypes::RenderUnit un // of pixels as integers, even if SLD allows for float values in there if ( roundToUnit ) { - rescaled = qRound( rescaled ); + rescaled = std::round( rescaled ); } return rescaled; } diff --git a/src/gui/editorwidgets/qgsspinbox.cpp b/src/gui/editorwidgets/qgsspinbox.cpp index e205015c6a4..47910757f22 100644 --- a/src/gui/editorwidgets/qgsspinbox.cpp +++ b/src/gui/editorwidgets/qgsspinbox.cpp @@ -150,7 +150,7 @@ int QgsSpinBox::valueFromText( const QString &text ) const return mShowClearButton ? clearValue() : value(); } - return qRound( QgsExpression::evaluateToDouble( trimmedText, value() ) ); + return std::round( QgsExpression::evaluateToDouble( trimmedText, value() ) ); } QValidator::State QgsSpinBox::validate( QString &input, int &pos ) const diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index b32268c3fb1..46e7694e9f7 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -655,7 +655,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) previousPt.x() - penultimatePt.x() ); softAngle -= deltaAngle; } - int quo = qRound( softAngle / commonAngle ); + int quo = std::round( softAngle / commonAngle ); if ( qAbs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES ) { // also check the distance in pixel to the line, otherwise it's too sticky at long ranges diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index 978406caedc..be041c56bda 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -566,8 +566,8 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) double newL = ( ( -sin( rad0 ) * r ) / triangleSideLength ) + 0.5; double widthShare = 1.0 - ( fabs( newL - 0.5 ) * 2.0 ); double newS = ( ( ( cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare; - s = qMin( qRound( qMax( 0.0, newS ) * 255.0 ), 255 ); - l = qMin( qRound( qMax( 0.0, newL ) * 255.0 ), 255 ); + s = qMin( static_cast< int >( std::round( qMax( 0.0, newS ) * 255.0 ) ), 255 ); + l = qMin( static_cast< int >( std::round( qMax( 0.0, newL ) * 255.0 ) ), 255 ); newColor = QColor::fromHsl( h, s, l ); //explicitly set the hue again, so that it's exact newColor.setHsv( h, newColor.hsvSaturation(), newColor.value(), alpha ); @@ -1375,7 +1375,7 @@ int QgsColorSliderWidget::convertRealToDisplay( const int realValue ) const //for whom "255" is a totally arbitrary value! if ( mComponent == QgsColorWidget::Saturation || mComponent == QgsColorWidget::Value || mComponent == QgsColorWidget::Alpha ) { - return qRound( 100.0 * realValue / 255.0 ); + return std::round( 100.0 * realValue / 255.0 ); } //leave all other values intact @@ -1387,7 +1387,7 @@ int QgsColorSliderWidget::convertDisplayToReal( const int displayValue ) const //scale saturation, value or alpha from 0->100 range (see note in convertRealToDisplay) if ( mComponent == QgsColorWidget::Saturation || mComponent == QgsColorWidget::Value || mComponent == QgsColorWidget::Alpha ) { - return qRound( 255.0 * displayValue / 100.0 ); + return std::round( 255.0 * displayValue / 100.0 ); } //leave all other values intact @@ -1584,7 +1584,7 @@ void QgsColorPreviewWidget::paintEvent( QPaintEvent *event ) if ( mColor2.isValid() ) { //drawing with two color sections - int verticalSplit = qRound( height() / 2.0 ); + int verticalSplit = std::round( height() / 2.0 ); drawColor( mCurrentColor, QRect( 0, 0, width(), verticalSplit ), painter ); drawColor( mColor2, QRect( 0, verticalSplit, width(), height() - verticalSplit ), painter ); } @@ -1634,7 +1634,7 @@ void QgsColorPreviewWidget::mouseReleaseEvent( QMouseEvent *e ) if ( mColor2.isValid() ) { //two color sections, check if dragged color was the second color - int verticalSplit = qRound( height() / 2.0 ); + int verticalSplit = std::round( height() / 2.0 ); if ( mDragStartPosition.y() >= verticalSplit ) { clickedColor = mColor2; @@ -1669,7 +1669,7 @@ void QgsColorPreviewWidget::mouseMoveEvent( QMouseEvent *e ) if ( mColor2.isValid() ) { //two color sections, check if dragged color was the second color - int verticalSplit = qRound( height() / 2.0 ); + int verticalSplit = std::round( height() / 2.0 ); if ( mDragStartPosition.y() >= verticalSplit ) { dragColor = mColor2; diff --git a/src/gui/qgsgradientstopeditor.cpp b/src/gui/qgsgradientstopeditor.cpp index 903b8db8fec..f471a79b6d7 100644 --- a/src/gui/qgsgradientstopeditor.cpp +++ b/src/gui/qgsgradientstopeditor.cpp @@ -392,7 +392,7 @@ void QgsGradientStopEditor::drawStopMarker( QPainter &painter, QPoint topMiddle, painter.setBrush( selected ? QColor( 150, 150, 150 ) : Qt::white ); painter.setPen( selected ? Qt::black : QColor( 150, 150, 150 ) ); // 0.5 offsets to make edges pixel grid aligned - painter.translate( qRound( topMiddle.x() - MARKER_WIDTH / 2.0 ) + 0.5, topMiddle.y() + 0.5 ); + painter.translate( std::round( topMiddle.x() - MARKER_WIDTH / 2.0 ) + 0.5, topMiddle.y() + 0.5 ); painter.drawPolygon( sOuterTriangle ); // draw the checkerboard background for marker diff --git a/src/gui/qgsmapcanvasmap.cpp b/src/gui/qgsmapcanvasmap.cpp index 55d666a1047..ac47bf6c380 100644 --- a/src/gui/qgsmapcanvasmap.cpp +++ b/src/gui/qgsmapcanvasmap.cpp @@ -60,7 +60,7 @@ QRectF QgsMapCanvasMap::boundingRect() const void QgsMapCanvasMap::paint( QPainter *painter ) { - int w = qRound( mItemSize.width() ) - 2, h = qRound( mItemSize.height() ) - 2; // setRect() makes the size +2 :-( + int w = std::round( mItemSize.width() ) - 2, h = std::round( mItemSize.height() ) - 2; // setRect() makes the size +2 :-( if ( mImage.size() != QSize( w, h ) ) { QgsDebugMsg( QString( "map paint DIFFERENT SIZE: img %1,%2 item %3,%4" ).arg( mImage.width() ).arg( mImage.height() ).arg( w ).arg( h ) ); diff --git a/src/gui/qgsmapmouseevent.cpp b/src/gui/qgsmapmouseevent.cpp index 0cb5147770f..6ba92548623 100644 --- a/src/gui/qgsmapmouseevent.cpp +++ b/src/gui/qgsmapmouseevent.cpp @@ -135,5 +135,5 @@ QPoint QgsMapMouseEvent::mapToPixelCoordinates( const QgsPointXY &point ) mMapCanvas->mapSettings().mapToPixel().transformInPlace( x, y ); - return QPoint( qRound( x ), qRound( y ) ); + return QPoint( std::round( x ), std::round( y ) ); } diff --git a/src/gui/qgsmaptool.cpp b/src/gui/qgsmaptool.cpp index 2fce839a551..32973d8dfcf 100644 --- a/src/gui/qgsmaptool.cpp +++ b/src/gui/qgsmaptool.cpp @@ -77,7 +77,7 @@ QPoint QgsMapTool::toCanvasCoordinates( const QgsPointXY &point ) { qreal x = point.x(), y = point.y(); mCanvas->getCoordinateTransform()->transformInPlace( x, y ); - return QPoint( qRound( x ), qRound( y ) ); + return QPoint( std::round( x ), std::round( y ) ); } diff --git a/src/gui/qgsmaptoolidentify.cpp b/src/gui/qgsmaptoolidentify.cpp index c3103dfb8c0..bf0a94305af 100644 --- a/src/gui/qgsmaptoolidentify.cpp +++ b/src/gui/qgsmaptoolidentify.cpp @@ -540,8 +540,8 @@ bool QgsMapToolIdentify::identifyRasterLayer( QList *results, Qg // are similar to source width and height used to reproject layer for drawing. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. - int width = qRound( viewExtent.width() / mapUnitsPerPixel ); - int height = qRound( viewExtent.height() / mapUnitsPerPixel ); + int width = std::round( viewExtent.width() / mapUnitsPerPixel ); + int height = std::round( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); diff --git a/src/gui/qgsrasterlayersaveasdialog.cpp b/src/gui/qgsrasterlayersaveasdialog.cpp index bf4c79dee57..1686665a6e0 100644 --- a/src/gui/qgsrasterlayersaveasdialog.cpp +++ b/src/gui/qgsrasterlayersaveasdialog.cpp @@ -366,8 +366,8 @@ void QgsRasterLayerSaveAsDialog::setResolution( double xRes, double yRes, const void QgsRasterLayerSaveAsDialog::recalcSize() { QgsRectangle extent = outputRectangle(); - int xSize = xResolution() != 0 ? static_cast( qRound( extent.width() / xResolution() ) ) : 0; - int ySize = yResolution() != 0 ? static_cast( qRound( extent.height() / yResolution() ) ) : 0; + int xSize = xResolution() != 0 ? static_cast( std::round( extent.width() / xResolution() ) ) : 0; + int ySize = yResolution() != 0 ? static_cast( std::round( extent.height() / yResolution() ) ) : 0; mColumnsLineEdit->setText( QString::number( xSize ) ); mRowsLineEdit->setText( QString::number( ySize ) ); updateResolutionStateMsg(); diff --git a/src/gui/qgsscalecombobox.cpp b/src/gui/qgsscalecombobox.cpp index 54b8a2b3dba..e7817d2a3ce 100644 --- a/src/gui/qgsscalecombobox.cpp +++ b/src/gui/qgsscalecombobox.cpp @@ -185,11 +185,11 @@ QString QgsScaleComboBox::toString( double scale ) } else if ( scale <= 1 ) { - return QStringLiteral( "%1:1" ).arg( QLocale::system().toString( qRound( 1.0 / scale ) ) ); + return QStringLiteral( "%1:1" ).arg( QLocale::system().toString( static_cast< int >( std::round( 1.0 / scale ) ) ) ); } else { - return QStringLiteral( "1:%1" ).arg( QLocale::system().toString( qRound( scale ) ) ); + return QStringLiteral( "1:%1" ).arg( QLocale::system().toString( static_cast< int >( std::round( scale ) ) ) ); } } diff --git a/src/gui/symbology/qgsellipsesymbollayerwidget.cpp b/src/gui/symbology/qgsellipsesymbollayerwidget.cpp index bf57ced9d67..0caa367868c 100644 --- a/src/gui/symbology/qgsellipsesymbollayerwidget.cpp +++ b/src/gui/symbology/qgsellipsesymbollayerwidget.cpp @@ -51,7 +51,7 @@ QgsEllipseSymbolLayerWidget::QgsEllipseSymbolLayerWidget( const QgsVectorLayer * names << QStringLiteral( "circle" ) << QStringLiteral( "rectangle" ) << QStringLiteral( "diamond" ) << QStringLiteral( "cross" ) << QStringLiteral( "triangle" ) << QStringLiteral( "right_half_triangle" ) << QStringLiteral( "left_half_triangle" ) << QStringLiteral( "semi_circle" ); int size = mShapeListWidget->iconSize().width(); - size = qMax( 30, qRound( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ); + size = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ) ); mShapeListWidget->setGridSize( QSize( size * 1.2, size * 1.2 ) ); mShapeListWidget->setIconSize( QSize( size, size ) ); diff --git a/src/gui/symbology/qgssvgselectorwidget.cpp b/src/gui/symbology/qgssvgselectorwidget.cpp index e0133db031e..b7368ee50ed 100644 --- a/src/gui/symbology/qgssvgselectorwidget.cpp +++ b/src/gui/symbology/qgssvgselectorwidget.cpp @@ -377,7 +377,7 @@ QgsSvgSelectorWidget::QgsSvgSelectorWidget( QWidget *parent ) // TODO: in-code gui setup with option to vertically or horizontally stack SVG groups/images widgets setupUi( this ); - mIconSize = qMax( 30, qRound( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ); + mIconSize = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) ); mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) ); mGroupsTreeView->setHeaderHidden( true ); diff --git a/src/gui/symbology/qgssymbollayerwidget.cpp b/src/gui/symbology/qgssymbollayerwidget.cpp index 49fa8a2eafa..8e92550641f 100644 --- a/src/gui/symbology/qgssymbollayerwidget.cpp +++ b/src/gui/symbology/qgssymbollayerwidget.cpp @@ -400,7 +400,7 @@ QgsSimpleMarkerSymbolLayerWidget::QgsSimpleMarkerSymbolLayerWidget( const QgsVec mSizeDDBtn->setSymbol( mAssistantPreviewSymbol ); int size = lstNames->iconSize().width(); - size = qMax( 30, qRound( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ); + size = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ) ); lstNames->setGridSize( QSize( size * 1.2, size * 1.2 ) ); lstNames->setIconSize( QSize( size, size ) ); @@ -1747,7 +1747,7 @@ QgsSvgMarkerSymbolLayerWidget::QgsSvgMarkerSymbolLayerWidget( const QgsVectorLay spinOffsetY->setClearValue( 0.0 ); spinAngle->setClearValue( 0.0 ); - mIconSize = qMax( 30, qRound( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ); + mIconSize = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) ); viewImages->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) ); populateList(); diff --git a/src/plugins/grass/qtermwidget/TerminalDisplay.cpp b/src/plugins/grass/qtermwidget/TerminalDisplay.cpp index 72622c9ce33..73fcab70119 100644 --- a/src/plugins/grass/qtermwidget/TerminalDisplay.cpp +++ b/src/plugins/grass/qtermwidget/TerminalDisplay.cpp @@ -73,8 +73,8 @@ using namespace Konsole; #define yMouseScroll 1 #define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "abcdefgjijklmnopqrstuvwxyz" \ - "0123456789./+@" + "abcdefgjijklmnopqrstuvwxyz" \ + "0123456789./+@" const ColorEntry Konsole::base_color_table[TABLE_COLORS] = // The following are almost IBM standard color codes, with some slight @@ -83,17 +83,17 @@ const ColorEntry Konsole::base_color_table[TABLE_COLORS] = { // Fixme: could add faint colors here, also. // normal - ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 1), // Dfore, Dback - ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0x18,0x18), 0), // Black, Red - ColorEntry(QColor(0x18,0xB2,0x18), 0), ColorEntry( QColor(0xB2,0x68,0x18), 0), // Green, Yellow - ColorEntry(QColor(0x18,0x18,0xB2), 0), ColorEntry( QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta - ColorEntry(QColor(0x18,0xB2,0xB2), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 0), // Cyan, White + ColorEntry( QColor( 0x00, 0x00, 0x00 ), 0 ), ColorEntry( QColor( 0xB2, 0xB2, 0xB2 ), 1 ), // Dfore, Dback + ColorEntry( QColor( 0x00, 0x00, 0x00 ), 0 ), ColorEntry( QColor( 0xB2, 0x18, 0x18 ), 0 ), // Black, Red + ColorEntry( QColor( 0x18, 0xB2, 0x18 ), 0 ), ColorEntry( QColor( 0xB2, 0x68, 0x18 ), 0 ), // Green, Yellow + ColorEntry( QColor( 0x18, 0x18, 0xB2 ), 0 ), ColorEntry( QColor( 0xB2, 0x18, 0xB2 ), 0 ), // Blue, Magenta + ColorEntry( QColor( 0x18, 0xB2, 0xB2 ), 0 ), ColorEntry( QColor( 0xB2, 0xB2, 0xB2 ), 0 ), // Cyan, White // intensiv - ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 1), - ColorEntry(QColor(0x68,0x68,0x68), 0), ColorEntry( QColor(0xFF,0x54,0x54), 0), - ColorEntry(QColor(0x54,0xFF,0x54), 0), ColorEntry( QColor(0xFF,0xFF,0x54), 0), - ColorEntry(QColor(0x54,0x54,0xFF), 0), ColorEntry( QColor(0xFF,0x54,0xFF), 0), - ColorEntry(QColor(0x54,0xFF,0xFF), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 0) + ColorEntry( QColor( 0x00, 0x00, 0x00 ), 0 ), ColorEntry( QColor( 0xFF, 0xFF, 0xFF ), 1 ), + ColorEntry( QColor( 0x68, 0x68, 0x68 ), 0 ), ColorEntry( QColor( 0xFF, 0x54, 0x54 ), 0 ), + ColorEntry( QColor( 0x54, 0xFF, 0x54 ), 0 ), ColorEntry( QColor( 0xFF, 0xFF, 0x54 ), 0 ), + ColorEntry( QColor( 0x54, 0x54, 0xFF ), 0 ), ColorEntry( QColor( 0xFF, 0x54, 0xFF ), 0 ), + ColorEntry( QColor( 0x54, 0xFF, 0xFF ), 0 ), ColorEntry( QColor( 0xFF, 0xFF, 0xFF ), 0 ) }; // scroll increment used when dragging selection at top/bottom of window. @@ -120,61 +120,61 @@ const QChar LTR_OVERRIDE_CHAR( 0x202D ); IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White */ -ScreenWindow* TerminalDisplay::screenWindow() const +ScreenWindow *TerminalDisplay::screenWindow() const { - return _screenWindow; + return _screenWindow; } -void TerminalDisplay::setScreenWindow(ScreenWindow* window) +void TerminalDisplay::setScreenWindow( ScreenWindow *window ) { - // disconnect existing screen window if any - if ( _screenWindow ) - { - disconnect( _screenWindow , 0 , this , 0 ); - } + // disconnect existing screen window if any + if ( _screenWindow ) + { + disconnect( _screenWindow, 0, this, 0 ); + } - _screenWindow = window; + _screenWindow = window; - if ( window ) - { + if ( window ) + { // TODO: Determine if this is an issue. //#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?" - connect( _screenWindow.data() , &ScreenWindow::outputChanged , this , &TerminalDisplay::updateLineProperties ); - connect( _screenWindow.data() , &ScreenWindow::outputChanged , this , &TerminalDisplay::updateImage ); - connect( _screenWindow.data() , &ScreenWindow::outputChanged , this , &TerminalDisplay::updateFilters ); - connect( _screenWindow.data() , &ScreenWindow::scrolled , this , &TerminalDisplay::updateFilters ); - window->setWindowLines(_lines); - } + connect( _screenWindow.data(), &ScreenWindow::outputChanged, this, &TerminalDisplay::updateLineProperties ); + connect( _screenWindow.data(), &ScreenWindow::outputChanged, this, &TerminalDisplay::updateImage ); + connect( _screenWindow.data(), &ScreenWindow::outputChanged, this, &TerminalDisplay::updateFilters ); + connect( _screenWindow.data(), &ScreenWindow::scrolled, this, &TerminalDisplay::updateFilters ); + window->setWindowLines( _lines ); + } } -const ColorEntry* TerminalDisplay::colorTable() const +const ColorEntry *TerminalDisplay::colorTable() const { return _colorTable; } -void TerminalDisplay::setBackgroundColor(const QColor& color) +void TerminalDisplay::setBackgroundColor( const QColor &color ) { - _colorTable[DEFAULT_BACK_COLOR].color = color; - QPalette p = palette(); - p.setColor( backgroundRole(), color ); - setPalette( p ); + _colorTable[DEFAULT_BACK_COLOR].color = color; + QPalette p = palette(); + p.setColor( backgroundRole(), color ); + setPalette( p ); - // Avoid propagating the palette change to the scroll bar - _scrollBar->setPalette( QApplication::palette() ); + // Avoid propagating the palette change to the scroll bar + _scrollBar->setPalette( QApplication::palette() ); - update(); + update(); } -void TerminalDisplay::setForegroundColor(const QColor& color) +void TerminalDisplay::setForegroundColor( const QColor &color ) { - _colorTable[DEFAULT_FORE_COLOR].color = color; + _colorTable[DEFAULT_FORE_COLOR].color = color; - update(); + update(); } -void TerminalDisplay::setColorTable(const ColorEntry table[]) +void TerminalDisplay::setColorTable( const ColorEntry table[] ) { - for (int i = 0; i < TABLE_COLORS; i++) - _colorTable[i] = table[i]; + for ( int i = 0; i < TABLE_COLORS; i++ ) + _colorTable[i] = table[i]; - setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color); + setBackgroundColor( _colorTable[DEFAULT_BACK_COLOR].color ); } /* ------------------------------------------------------------------------- */ @@ -195,48 +195,49 @@ void TerminalDisplay::setColorTable(const ColorEntry table[]) QCodec. */ -static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);} -static inline bool isLineCharString(const QString& string) +static inline bool isLineChar( quint16 c ) { return ( ( c & 0xFF80 ) == 0x2500 );} +static inline bool isLineCharString( const QString &string ) { - return (string.length() > 0) && (isLineChar(string.at(0).unicode())); + return ( string.length() > 0 ) && ( isLineChar( string.at( 0 ).unicode() ) ); } // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. unsigned short Konsole::vt100_graphics[32] = -{ // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 +{ + // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 }; -void TerminalDisplay::fontChange(const QFont&) +void TerminalDisplay::fontChange( const QFont & ) { - QFontMetrics fm(font()); + QFontMetrics fm( font() ); _fontHeight = fm.height() + _lineSpacing; // waba TerminalDisplay 1.123: // "Base character width on widest ASCII character. This prevents too wide // characters in the presence of double wide (e.g. Japanese) characters." // Get the width from representative normal width characters - _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR)); + _fontWidth = qRound( ( double )fm.width( REPCHAR ) / ( double )strlen( REPCHAR ) ); _fixedFont = true; - int fw = fm.width(REPCHAR[0]); - for(unsigned int i=1; i< strlen(REPCHAR); i++) + int fw = fm.width( REPCHAR[0] ); + for ( unsigned int i = 1; i < strlen( REPCHAR ); i++ ) { - if (fw != fm.width(REPCHAR[i])) + if ( fw != fm.width( REPCHAR[i] ) ) { _fixedFont = false; break; } } - if (_fontWidth < 1) - _fontWidth=1; + if ( _fontWidth < 1 ) + _fontWidth = 1; _fontAscent = fm.ascent(); @@ -245,42 +246,42 @@ void TerminalDisplay::fontChange(const QFont&) update(); } -void TerminalDisplay::setVTFont(const QFont& f) +void TerminalDisplay::setVTFont( const QFont &f ) { QFont font = f; - // This was originally set for OS X only: - // mac uses floats for font width specification. - // this ensures the same handling for all platforms - // but then there was revealed that various Linux distros - // have this problem too... - font.setStyleStrategy(QFont::ForceIntegerMetrics); + // This was originally set for OS X only: + // mac uses floats for font width specification. + // this ensures the same handling for all platforms + // but then there was revealed that various Linux distros + // have this problem too... + font.setStyleStrategy( QFont::ForceIntegerMetrics ); - QFontMetrics metrics(font); + QFontMetrics metrics( font ); - if ( !QFontInfo(font).fixedPitch() ) + if ( !QFontInfo( font ).fixedPitch() ) { - qDebug() << "Using a variable-width font in the terminal. This may cause performance degradation and display/alignment errors."; + qDebug() << "Using a variable-width font in the terminal. This may cause performance degradation and display/alignment errors."; } if ( metrics.height() < height() && metrics.maxWidth() < width() ) { // hint that text should be drawn without anti-aliasing. // depending on the user's font configuration, this may not be respected - if (!_antialiasText) - font.setStyleStrategy( QFont::NoAntialias ); + if ( !_antialiasText ) + font.setStyleStrategy( QFont::NoAntialias ); // experimental optimization. Konsole assumes that the terminal is using a // mono-spaced font, in which case kerning information should have an effect. // Disabling kerning saves some computation when rendering text. - font.setKerning(false); + font.setKerning( false ); - QWidget::setFont(font); - fontChange(font); + QWidget::setFont( font ); + fontChange( font ); } } -void TerminalDisplay::setFont(const QFont &) +void TerminalDisplay::setFont( const QFont & ) { // ignore font change request if not coming from konsole itself } @@ -291,58 +292,58 @@ void TerminalDisplay::setFont(const QFont &) /* */ /* ------------------------------------------------------------------------- */ -TerminalDisplay::TerminalDisplay(QWidget *parent) -:QWidget(parent) -,_screenWindow(0) -,_allowBell(true) -,_gridLayout(0) -,_fontHeight(1) -,_fontWidth(1) -,_fontAscent(1) -,_boldIntense(true) -,_lines(1) -,_columns(1) -,_usedLines(1) -,_usedColumns(1) -,_contentHeight(1) -,_contentWidth(1) -,_image(0) -,_randomSeed(0) -,_resizing(false) -,_terminalSizeHint(false) -,_terminalSizeStartup(true) -,_bidiEnabled(false) -,_actSel(0) -,_wordSelectionMode(false) -,_lineSelectionMode(false) -,_preserveLineBreaks(false) -,_columnSelectionMode(false) -,_scrollbarLocation(NoScrollBar) -,_wordCharacters(QStringLiteral(":@-./_~")) -,_bellMode(SystemBeepBell) -,_blinking(false) -,_hasBlinker(false) -,_cursorBlinking(false) -,_hasBlinkingCursor(false) -,_allowBlinkingText(true) -,_ctrlDrag(false) -,_tripleClickMode(SelectWholeLine) -,_isFixedSize(false) -,_possibleTripleClick(false) -,_resizeWidget(0) -,_resizeTimer(0) -,_flowControlWarningEnabled(false) -,_outputSuspendedLabel(0) -,_lineSpacing(0) -,_colorsInverted(false) -,_blendColor(qRgba(0,0,0,0xff)) -,_filterChain(new TerminalImageFilterChain()) -,_cursorShape(BlockCursor) -,mMotionAfterPasting(NoMoveScreenWindow) +TerminalDisplay::TerminalDisplay( QWidget *parent ) + : QWidget( parent ) + , _screenWindow( 0 ) + , _allowBell( true ) + , _gridLayout( 0 ) + , _fontHeight( 1 ) + , _fontWidth( 1 ) + , _fontAscent( 1 ) + , _boldIntense( true ) + , _lines( 1 ) + , _columns( 1 ) + , _usedLines( 1 ) + , _usedColumns( 1 ) + , _contentHeight( 1 ) + , _contentWidth( 1 ) + , _image( 0 ) + , _randomSeed( 0 ) + , _resizing( false ) + , _terminalSizeHint( false ) + , _terminalSizeStartup( true ) + , _bidiEnabled( false ) + , _actSel( 0 ) + , _wordSelectionMode( false ) + , _lineSelectionMode( false ) + , _preserveLineBreaks( false ) + , _columnSelectionMode( false ) + , _scrollbarLocation( NoScrollBar ) + , _wordCharacters( QStringLiteral( ":@-./_~" ) ) + , _bellMode( SystemBeepBell ) + , _blinking( false ) + , _hasBlinker( false ) + , _cursorBlinking( false ) + , _hasBlinkingCursor( false ) + , _allowBlinkingText( true ) + , _ctrlDrag( false ) + , _tripleClickMode( SelectWholeLine ) + , _isFixedSize( false ) + , _possibleTripleClick( false ) + , _resizeWidget( 0 ) + , _resizeTimer( 0 ) + , _flowControlWarningEnabled( false ) + , _outputSuspendedLabel( 0 ) + , _lineSpacing( 0 ) + , _colorsInverted( false ) + , _blendColor( qRgba( 0, 0, 0, 0xff ) ) + , _filterChain( new TerminalImageFilterChain() ) + , _cursorShape( BlockCursor ) + , mMotionAfterPasting( NoMoveScreenWindow ) { // terminal applications are not designed with Right-To-Left in mind, // so the layout is forced to Left-To-Right - setLayoutDirection(Qt::LeftToRight); + setLayoutDirection( Qt::LeftToRight ); // The offsets are not yet calculated. // Do not calculate these too often to be more smoothly when resizing @@ -352,52 +353,52 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) // create scroll bar for scrolling output up and down // set the scroll bar's slider to occupy the whole area of the scroll bar initially - _scrollBar = new QScrollBar(this); - setScroll(0,0); + _scrollBar = new QScrollBar( this ); + setScroll( 0, 0 ); _scrollBar->setCursor( Qt::ArrowCursor ); - connect(_scrollBar, &QAbstractSlider::valueChanged, this, - &TerminalDisplay::scrollBarPositionChanged); + connect( _scrollBar, &QAbstractSlider::valueChanged, this, + &TerminalDisplay::scrollBarPositionChanged ); // qtermwidget: we have to hide it here due the _scrollbarLocation==NoScrollBar // check in TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) _scrollBar->hide(); // setup timers for blinking cursor and text - _blinkTimer = new QTimer(this); - connect(_blinkTimer, &QTimer::timeout, this, &TerminalDisplay::blinkEvent); - _blinkCursorTimer = new QTimer(this); - connect(_blinkCursorTimer, &QTimer::timeout, this, &TerminalDisplay::blinkCursorEvent); + _blinkTimer = new QTimer( this ); + connect( _blinkTimer, &QTimer::timeout, this, &TerminalDisplay::blinkEvent ); + _blinkCursorTimer = new QTimer( this ); + connect( _blinkCursorTimer, &QTimer::timeout, this, &TerminalDisplay::blinkCursorEvent ); // KCursor::setAutoHideCursor( this, true ); - setUsesMouse(true); - setColorTable(base_color_table); - setMouseTracking(true); + setUsesMouse( true ); + setColorTable( base_color_table ); + setMouseTracking( true ); // Enable drag and drop - setAcceptDrops(true); // attempt + setAcceptDrops( true ); // attempt dragInfo.state = diNone; setFocusPolicy( Qt::WheelFocus ); // enable input method support - setAttribute(Qt::WA_InputMethodEnabled, true); + setAttribute( Qt::WA_InputMethodEnabled, true ); // this is an important optimization, it tells Qt // that TerminalDisplay will handle repainting its entire area. - setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute( Qt::WA_OpaquePaintEvent ); - _gridLayout = new QGridLayout(this); - _gridLayout->setContentsMargins(0, 0, 0, 0); + _gridLayout = new QGridLayout( this ); + _gridLayout->setContentsMargins( 0, 0, 0, 0 ); setLayout( _gridLayout ); - new AutoScrollHandler(this); + new AutoScrollHandler( this ); } TerminalDisplay::~TerminalDisplay() { - disconnect(_blinkTimer); - disconnect(_blinkCursorTimer); + disconnect( _blinkTimer ); + disconnect( _blinkCursorTimer ); qApp->removeEventFilter( this ); delete[] _image; @@ -434,358 +435,358 @@ where _ = none enum LineEncode { - TopL = (1<<1), - TopC = (1<<2), - TopR = (1<<3), + TopL = ( 1 << 1 ), + TopC = ( 1 << 2 ), + TopR = ( 1 << 3 ), - LeftT = (1<<5), - Int11 = (1<<6), - Int12 = (1<<7), - Int13 = (1<<8), - RightT = (1<<9), + LeftT = ( 1 << 5 ), + Int11 = ( 1 << 6 ), + Int12 = ( 1 << 7 ), + Int13 = ( 1 << 8 ), + RightT = ( 1 << 9 ), - LeftC = (1<<10), - Int21 = (1<<11), - Int22 = (1<<12), - Int23 = (1<<13), - RightC = (1<<14), + LeftC = ( 1 << 10 ), + Int21 = ( 1 << 11 ), + Int22 = ( 1 << 12 ), + Int23 = ( 1 << 13 ), + RightC = ( 1 << 14 ), - LeftB = (1<<15), - Int31 = (1<<16), - Int32 = (1<<17), - Int33 = (1<<18), - RightB = (1<<19), + LeftB = ( 1 << 15 ), + Int31 = ( 1 << 16 ), + Int32 = ( 1 << 17 ), + Int33 = ( 1 << 18 ), + RightB = ( 1 << 19 ), - BotL = (1<<21), - BotC = (1<<22), - BotR = (1<<23) + BotL = ( 1 << 21 ), + BotC = ( 1 << 22 ), + BotR = ( 1 << 23 ) }; #include "LineFont.h" -static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code) +static void drawLineChar( QPainter &paint, int x, int y, int w, int h, uchar code ) { - //Calculate cell midpoints, end points. - int cx = x + w/2; - int cy = y + h/2; - int ex = x + w - 1; - int ey = y + h - 1; + //Calculate cell midpoints, end points. + int cx = x + w / 2; + int cy = y + h / 2; + int ex = x + w - 1; + int ey = y + h - 1; - quint32 toDraw = LineChars[code]; + quint32 toDraw = LineChars[code]; - //Top _lines: - if (toDraw & TopL) - paint.drawLine(cx-1, y, cx-1, cy-2); - if (toDraw & TopC) - paint.drawLine(cx, y, cx, cy-2); - if (toDraw & TopR) - paint.drawLine(cx+1, y, cx+1, cy-2); + //Top _lines: + if ( toDraw & TopL ) + paint.drawLine( cx - 1, y, cx - 1, cy - 2 ); + if ( toDraw & TopC ) + paint.drawLine( cx, y, cx, cy - 2 ); + if ( toDraw & TopR ) + paint.drawLine( cx + 1, y, cx + 1, cy - 2 ); - //Bot _lines: - if (toDraw & BotL) - paint.drawLine(cx-1, cy+2, cx-1, ey); - if (toDraw & BotC) - paint.drawLine(cx, cy+2, cx, ey); - if (toDraw & BotR) - paint.drawLine(cx+1, cy+2, cx+1, ey); + //Bot _lines: + if ( toDraw & BotL ) + paint.drawLine( cx - 1, cy + 2, cx - 1, ey ); + if ( toDraw & BotC ) + paint.drawLine( cx, cy + 2, cx, ey ); + if ( toDraw & BotR ) + paint.drawLine( cx + 1, cy + 2, cx + 1, ey ); - //Left _lines: - if (toDraw & LeftT) - paint.drawLine(x, cy-1, cx-2, cy-1); - if (toDraw & LeftC) - paint.drawLine(x, cy, cx-2, cy); - if (toDraw & LeftB) - paint.drawLine(x, cy+1, cx-2, cy+1); + //Left _lines: + if ( toDraw & LeftT ) + paint.drawLine( x, cy - 1, cx - 2, cy - 1 ); + if ( toDraw & LeftC ) + paint.drawLine( x, cy, cx - 2, cy ); + if ( toDraw & LeftB ) + paint.drawLine( x, cy + 1, cx - 2, cy + 1 ); - //Right _lines: - if (toDraw & RightT) - paint.drawLine(cx+2, cy-1, ex, cy-1); - if (toDraw & RightC) - paint.drawLine(cx+2, cy, ex, cy); - if (toDraw & RightB) - paint.drawLine(cx+2, cy+1, ex, cy+1); + //Right _lines: + if ( toDraw & RightT ) + paint.drawLine( cx + 2, cy - 1, ex, cy - 1 ); + if ( toDraw & RightC ) + paint.drawLine( cx + 2, cy, ex, cy ); + if ( toDraw & RightB ) + paint.drawLine( cx + 2, cy + 1, ex, cy + 1 ); - //Intersection points. - if (toDraw & Int11) - paint.drawPoint(cx-1, cy-1); - if (toDraw & Int12) - paint.drawPoint(cx, cy-1); - if (toDraw & Int13) - paint.drawPoint(cx+1, cy-1); + //Intersection points. + if ( toDraw & Int11 ) + paint.drawPoint( cx - 1, cy - 1 ); + if ( toDraw & Int12 ) + paint.drawPoint( cx, cy - 1 ); + if ( toDraw & Int13 ) + paint.drawPoint( cx + 1, cy - 1 ); - if (toDraw & Int21) - paint.drawPoint(cx-1, cy); - if (toDraw & Int22) - paint.drawPoint(cx, cy); - if (toDraw & Int23) - paint.drawPoint(cx+1, cy); + if ( toDraw & Int21 ) + paint.drawPoint( cx - 1, cy ); + if ( toDraw & Int22 ) + paint.drawPoint( cx, cy ); + if ( toDraw & Int23 ) + paint.drawPoint( cx + 1, cy ); - if (toDraw & Int31) - paint.drawPoint(cx-1, cy+1); - if (toDraw & Int32) - paint.drawPoint(cx, cy+1); - if (toDraw & Int33) - paint.drawPoint(cx+1, cy+1); + if ( toDraw & Int31 ) + paint.drawPoint( cx - 1, cy + 1 ); + if ( toDraw & Int32 ) + paint.drawPoint( cx, cy + 1 ); + if ( toDraw & Int33 ) + paint.drawPoint( cx + 1, cy + 1 ); } -void TerminalDisplay::drawLineCharString( QPainter& painter, int x, int y, const QString& str, - const Character* attributes) +void TerminalDisplay::drawLineCharString( QPainter &painter, int x, int y, const QString &str, + const Character *attributes ) { - const QPen& currentPen = painter.pen(); + const QPen ¤tPen = painter.pen(); - if ( (attributes->rendition & RE_BOLD) && _boldIntense ) - { - QPen boldPen(currentPen); - boldPen.setWidth(3); - painter.setPen( boldPen ); - } + if ( ( attributes->rendition & RE_BOLD ) && _boldIntense ) + { + QPen boldPen( currentPen ); + boldPen.setWidth( 3 ); + painter.setPen( boldPen ); + } - for (int i=0 ; i < str.length(); i++) - { - uchar code = str[i].cell(); - if (LineChars[code]) - drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code); - } + for ( int i = 0 ; i < str.length(); i++ ) + { + uchar code = str[i].cell(); + if ( LineChars[code] ) + drawLineChar( painter, x + ( _fontWidth * i ), y, _fontWidth, _fontHeight, code ); + } - painter.setPen( currentPen ); + painter.setPen( currentPen ); } -void TerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape) +void TerminalDisplay::setKeyboardCursorShape( KeyboardCursorShape shape ) { - _cursorShape = shape; + _cursorShape = shape; } TerminalDisplay::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() const { - return _cursorShape; + return _cursorShape; } -void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color) +void TerminalDisplay::setKeyboardCursorColor( bool useForegroundColor, const QColor &color ) { - if (useForegroundColor) - _cursorColor = QColor(); // an invalid color means that - // the foreground color of the - // current character should - // be used + if ( useForegroundColor ) + _cursorColor = QColor(); // an invalid color means that + // the foreground color of the + // current character should + // be used - else - _cursorColor = color; + else + _cursorColor = color; } QColor TerminalDisplay::keyboardCursorColor() const { - return _cursorColor; + return _cursorColor; } -void TerminalDisplay::setOpacity(qreal opacity) +void TerminalDisplay::setOpacity( qreal opacity ) { - QColor color(_blendColor); - color.setAlphaF(opacity); + QColor color( _blendColor ); + color.setAlphaF( opacity ); - // enable automatic background filling to prevent the display - // flickering if there is no transparency - /*if ( color.alpha() == 255 ) - { - setAutoFillBackground(true); - } - else - { - setAutoFillBackground(false); - }*/ + // enable automatic background filling to prevent the display + // flickering if there is no transparency + /*if ( color.alpha() == 255 ) + { + setAutoFillBackground(true); + } + else + { + setAutoFillBackground(false); + }*/ - _blendColor = color.rgba(); + _blendColor = color.rgba(); } -void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting ) +void TerminalDisplay::drawBackground( QPainter &painter, const QRect &rect, const QColor &backgroundColor, bool useOpacitySetting ) { - // the area of the widget showing the contents of the terminal display is drawn - // using the background color from the color scheme set with setColorTable() - // - // the area of the widget behind the scroll-bar is drawn using the background - // brush from the scroll-bar's palette, to give the effect of the scroll-bar - // being outside of the terminal display and visual consistency with other KDE - // applications. - // - QRect scrollBarArea = _scrollBar->isVisible() ? - rect.intersected(_scrollBar->geometry()) : - QRect(); - QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea); - QRect contentsRect = contentsRegion.boundingRect(); + // the area of the widget showing the contents of the terminal display is drawn + // using the background color from the color scheme set with setColorTable() + // + // the area of the widget behind the scroll-bar is drawn using the background + // brush from the scroll-bar's palette, to give the effect of the scroll-bar + // being outside of the terminal display and visual consistency with other KDE + // applications. + // + QRect scrollBarArea = _scrollBar->isVisible() ? + rect.intersected( _scrollBar->geometry() ) : + QRect(); + QRegion contentsRegion = QRegion( rect ).subtracted( scrollBarArea ); + QRect contentsRect = contentsRegion.boundingRect(); - if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting ) - { - QColor color(backgroundColor); - color.setAlpha(qAlpha(_blendColor)); + if ( HAVE_TRANSPARENCY && qAlpha( _blendColor ) < 0xff && useOpacitySetting ) + { + QColor color( backgroundColor ); + color.setAlpha( qAlpha( _blendColor ) ); - painter.save(); - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.fillRect(contentsRect, color); - painter.restore(); - } - else - painter.fillRect(contentsRect, backgroundColor); - - painter.fillRect(scrollBarArea,_scrollBar->palette().background()); -} - -void TerminalDisplay::drawCursor(QPainter& painter, - const QRect& rect, - const QColor& foregroundColor, - const QColor& /*backgroundColor*/, - bool& invertCharacterColor) -{ - QRect cursorRect = rect; - cursorRect.setHeight(_fontHeight - _lineSpacing - 1); - - if (!_cursorBlinking) - { - if ( _cursorColor.isValid() ) - painter.setPen(_cursorColor); - else - painter.setPen(foregroundColor); - - if ( _cursorShape == BlockCursor ) - { - // draw the cursor outline, adjusting the area so that - // it is draw entirely inside 'rect' - int penWidth = qMax(1,painter.pen().width()); - - painter.drawRect(cursorRect.adjusted(penWidth/2, - penWidth/2, - - penWidth/2 - penWidth%2, - - penWidth/2 - penWidth%2)); - if ( hasFocus() ) - { - painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor); - - if ( !_cursorColor.isValid() ) - { - // invert the colour used to draw the text to ensure that the character at - // the cursor position is readable - invertCharacterColor = true; - } - } - } - else if ( _cursorShape == UnderlineCursor ) - painter.drawLine(cursorRect.left(), - cursorRect.bottom(), - cursorRect.right(), - cursorRect.bottom()); - else if ( _cursorShape == IBeamCursor ) - painter.drawLine(cursorRect.left(), - cursorRect.top(), - cursorRect.left(), - cursorRect.bottom()); - - } -} - -void TerminalDisplay::drawCharacters(QPainter& painter, - const QRect& rect, - const QString& text, - const Character* style, - bool invertCharacterColor) -{ - // don't draw text which is currently blinking - if ( _blinking && (style->rendition & RE_BLINK) ) - return; - - // setup bold and underline - bool useBold; - ColorEntry::FontWeight weight = style->fontWeight(_colorTable); - if (weight == ColorEntry::UseCurrentFormat) - useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); - else - useBold = (weight == ColorEntry::Bold) ? true : false; - bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); - - QFont font = painter.font(); - if ( font.bold() != useBold - || font.underline() != useUnderline ) - { - font.setBold(useBold); - font.setUnderline(useUnderline); - painter.setFont(font); - } - - // setup pen - const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor ); - const QColor color = textColor.color(_colorTable); - QPen pen = painter.pen(); - if ( pen.color() != color ) - { - pen.setColor(color); - painter.setPen(color); - } - - // draw text - if ( isLineCharString(text) ) - drawLineCharString(painter,rect.x(),rect.y(),text,style); - else - { - // the drawText(rect,flags,string) overload is used here with null flags - // instead of drawText(rect,string) because the (rect,string) overload causes - // the application's default layout direction to be used instead of - // the widget-specific layout direction, which should always be - // Qt::LeftToRight for this widget - // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2 - if (_bidiEnabled) - painter.drawText(rect,0,text); - else -#if QT_VERSION >= 0x040800 - painter.drawText(rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text); -#else - painter.drawText(rect, 0, LTR_OVERRIDE_CHAR + text); -#endif - } -} - -void TerminalDisplay::drawTextFragment(QPainter& painter , - const QRect& rect, - const QString& text, - const Character* style) -{ painter.save(); - - // setup painter - const QColor foregroundColor = style->foregroundColor.color(_colorTable); - const QColor backgroundColor = style->backgroundColor.color(_colorTable); - - // draw background if different from the display's background color - if ( backgroundColor != palette().background().color() ) - drawBackground(painter,rect,backgroundColor, - false /* do not use transparency */); - - // draw cursor shape if the current character is the cursor - // this may alter the foreground and background colors - bool invertCharacterColor = false; - if ( style->rendition & RE_CURSOR ) - drawCursor(painter,rect,foregroundColor,backgroundColor,invertCharacterColor); - - // draw text - drawCharacters(painter,rect,text,style,invertCharacterColor); - + painter.setCompositionMode( QPainter::CompositionMode_Source ); + painter.fillRect( contentsRect, color ); painter.restore(); + } + else + painter.fillRect( contentsRect, backgroundColor ); + + painter.fillRect( scrollBarArea, _scrollBar->palette().background() ); } -void TerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; } +void TerminalDisplay::drawCursor( QPainter &painter, + const QRect &rect, + const QColor &foregroundColor, + const QColor & /*backgroundColor*/, + bool &invertCharacterColor ) +{ + QRect cursorRect = rect; + cursorRect.setHeight( _fontHeight - _lineSpacing - 1 ); + + if ( !_cursorBlinking ) + { + if ( _cursorColor.isValid() ) + painter.setPen( _cursorColor ); + else + painter.setPen( foregroundColor ); + + if ( _cursorShape == BlockCursor ) + { + // draw the cursor outline, adjusting the area so that + // it is draw entirely inside 'rect' + int penWidth = qMax( 1, painter.pen().width() ); + + painter.drawRect( cursorRect.adjusted( penWidth / 2, + penWidth / 2, + - penWidth / 2 - penWidth % 2, + - penWidth / 2 - penWidth % 2 ) ); + if ( hasFocus() ) + { + painter.fillRect( cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor ); + + if ( !_cursorColor.isValid() ) + { + // invert the colour used to draw the text to ensure that the character at + // the cursor position is readable + invertCharacterColor = true; + } + } + } + else if ( _cursorShape == UnderlineCursor ) + painter.drawLine( cursorRect.left(), + cursorRect.bottom(), + cursorRect.right(), + cursorRect.bottom() ); + else if ( _cursorShape == IBeamCursor ) + painter.drawLine( cursorRect.left(), + cursorRect.top(), + cursorRect.left(), + cursorRect.bottom() ); + + } +} + +void TerminalDisplay::drawCharacters( QPainter &painter, + const QRect &rect, + const QString &text, + const Character *style, + bool invertCharacterColor ) +{ + // don't draw text which is currently blinking + if ( _blinking && ( style->rendition & RE_BLINK ) ) + return; + + // setup bold and underline + bool useBold; + ColorEntry::FontWeight weight = style->fontWeight( _colorTable ); + if ( weight == ColorEntry::UseCurrentFormat ) + useBold = ( ( style->rendition & RE_BOLD ) && _boldIntense ) || font().bold(); + else + useBold = ( weight == ColorEntry::Bold ) ? true : false; + bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + + QFont font = painter.font(); + if ( font.bold() != useBold + || font.underline() != useUnderline ) + { + font.setBold( useBold ); + font.setUnderline( useUnderline ); + painter.setFont( font ); + } + + // setup pen + const CharacterColor &textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor ); + const QColor color = textColor.color( _colorTable ); + QPen pen = painter.pen(); + if ( pen.color() != color ) + { + pen.setColor( color ); + painter.setPen( color ); + } + + // draw text + if ( isLineCharString( text ) ) + drawLineCharString( painter, rect.x(), rect.y(), text, style ); + else + { + // the drawText(rect,flags,string) overload is used here with null flags + // instead of drawText(rect,string) because the (rect,string) overload causes + // the application's default layout direction to be used instead of + // the widget-specific layout direction, which should always be + // Qt::LeftToRight for this widget + // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2 + if ( _bidiEnabled ) + painter.drawText( rect, 0, text ); + else +#if QT_VERSION >= 0x040800 + painter.drawText( rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text ); +#else + painter.drawText( rect, 0, LTR_OVERRIDE_CHAR + text ); +#endif + } +} + +void TerminalDisplay::drawTextFragment( QPainter &painter, + const QRect &rect, + const QString &text, + const Character *style ) +{ + painter.save(); + + // setup painter + const QColor foregroundColor = style->foregroundColor.color( _colorTable ); + const QColor backgroundColor = style->backgroundColor.color( _colorTable ); + + // draw background if different from the display's background color + if ( backgroundColor != palette().background().color() ) + drawBackground( painter, rect, backgroundColor, + false /* do not use transparency */ ); + + // draw cursor shape if the current character is the cursor + // this may alter the foreground and background colors + bool invertCharacterColor = false; + if ( style->rendition & RE_CURSOR ) + drawCursor( painter, rect, foregroundColor, backgroundColor, invertCharacterColor ); + + // draw text + drawCharacters( painter, rect, text, style, invertCharacterColor ); + + painter.restore(); +} + +void TerminalDisplay::setRandomSeed( uint randomSeed ) { _randomSeed = randomSeed; } uint TerminalDisplay::randomSeed() const { return _randomSeed; } #if 0 /*! Set XIM Position */ -void TerminalDisplay::setCursorPos(const int curx, const int cury) +void TerminalDisplay::setCursorPos( const int curx, const int cury ) { - QPoint tL = contentsRect().topLeft(); - int tLx = tL.x(); - int tLy = tL.y(); + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); - int xpos, ypos; - ypos = _topMargin + tLy + _fontHeight*(cury-1) + _fontAscent; - xpos = _leftMargin + tLx + _fontWidth*curx; - //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ??? - // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos); - _cursorLine = cury; - _cursorCol = curx; + int xpos, ypos; + ypos = _topMargin + tLy + _fontHeight * ( cury - 1 ) + _fontAscent; + xpos = _leftMargin + tLx + _fontWidth * curx; + //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ??? + // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos); + _cursorLine = cury; + _cursorCol = curx; } #endif @@ -797,188 +798,193 @@ void TerminalDisplay::setCursorPos(const int curx, const int cury) // display is much cheaper than re-rendering all the text for the // part of the image which has moved up or down. // Instead only new lines have to be drawn -void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) +void TerminalDisplay::scrollImage( int lines, const QRect &screenWindowRegion ) { - // if the flow control warning is enabled this will interfere with the - // scrolling optimizations and cause artifacts. the simple solution here - // is to just disable the optimization whilst it is visible - if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) - return; + // if the flow control warning is enabled this will interfere with the + // scrolling optimizations and cause artifacts. the simple solution here + // is to just disable the optimization whilst it is visible + if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) + return; - // constrain the region to the display - // the bottom of the region is capped to the number of lines in the display's - // internal image - 2, so that the height of 'region' is strictly less - // than the height of the internal image. - QRect region = screenWindowRegion; - region.setBottom( qMin(region.bottom(),this->_lines-2) ); + // constrain the region to the display + // the bottom of the region is capped to the number of lines in the display's + // internal image - 2, so that the height of 'region' is strictly less + // than the height of the internal image. + QRect region = screenWindowRegion; + region.setBottom( qMin( region.bottom(), this->_lines - 2 ) ); - // return if there is nothing to do - if ( lines == 0 - || _image == 0 - || !region.isValid() - || (region.top() + abs(lines)) >= region.bottom() - || this->_lines <= region.height() ) return; + // return if there is nothing to do + if ( lines == 0 + || _image == 0 + || !region.isValid() + || ( region.top() + abs( lines ) ) >= region.bottom() + || this->_lines <= region.height() ) return; - // hide terminal size label to prevent it being scrolled - if (_resizeWidget && _resizeWidget->isVisible()) - _resizeWidget->hide(); + // hide terminal size label to prevent it being scrolled + if ( _resizeWidget && _resizeWidget->isVisible() ) + _resizeWidget->hide(); - // Note: With Qt 4.4 the left edge of the scrolled area must be at 0 - // to get the correct (newly exposed) part of the widget repainted. - // - // The right edge must be before the left edge of the scroll bar to - // avoid triggering a repaint of the entire widget, the distance is - // given by SCROLLBAR_CONTENT_GAP - // - // Set the QT_FLUSH_PAINT environment variable to '1' before starting the - // application to monitor repainting. - // - int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width(); - const int SCROLLBAR_CONTENT_GAP = 1; - QRect scrollRect; - if ( _scrollbarLocation == ScrollBarLeft ) - { - scrollRect.setLeft(scrollBarWidth+SCROLLBAR_CONTENT_GAP); - scrollRect.setRight(width()); - } - else - { - scrollRect.setLeft(0); - scrollRect.setRight(width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP); - } - void* firstCharPos = &_image[ region.top() * this->_columns ]; - void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ]; + // Note: With Qt 4.4 the left edge of the scrolled area must be at 0 + // to get the correct (newly exposed) part of the widget repainted. + // + // The right edge must be before the left edge of the scroll bar to + // avoid triggering a repaint of the entire widget, the distance is + // given by SCROLLBAR_CONTENT_GAP + // + // Set the QT_FLUSH_PAINT environment variable to '1' before starting the + // application to monitor repainting. + // + int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width(); + const int SCROLLBAR_CONTENT_GAP = 1; + QRect scrollRect; + if ( _scrollbarLocation == ScrollBarLeft ) + { + scrollRect.setLeft( scrollBarWidth + SCROLLBAR_CONTENT_GAP ); + scrollRect.setRight( width() ); + } + else + { + scrollRect.setLeft( 0 ); + scrollRect.setRight( width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP ); + } + void *firstCharPos = &_image[ region.top() * this->_columns ]; + void *lastCharPos = &_image[( region.top() + abs( lines ) ) * this->_columns ]; - int top = _topMargin + (region.top() * _fontHeight); - int linesToMove = region.height() - abs(lines); - int bytesToMove = linesToMove * - this->_columns * - sizeof(Character); + int top = _topMargin + ( region.top() * _fontHeight ); + int linesToMove = region.height() - abs( lines ); + int bytesToMove = linesToMove * + this->_columns * + sizeof( Character ); - Q_ASSERT( linesToMove > 0 ); - Q_ASSERT( bytesToMove > 0 ); + Q_ASSERT( linesToMove > 0 ); + Q_ASSERT( bytesToMove > 0 ); - //scroll internal image - if ( lines > 0 ) - { - // check that the memory areas that we are going to move are valid - Q_ASSERT( (char*)lastCharPos + bytesToMove < - (char*)(_image + (this->_lines * this->_columns)) ); + //scroll internal image + if ( lines > 0 ) + { + // check that the memory areas that we are going to move are valid + Q_ASSERT( ( char * )lastCharPos + bytesToMove < + ( char * )( _image + ( this->_lines * this->_columns ) ) ); - Q_ASSERT( (lines*this->_columns) < _imageSize ); + Q_ASSERT( ( lines * this->_columns ) < _imageSize ); - //scroll internal image down - memmove( firstCharPos , lastCharPos , bytesToMove ); + //scroll internal image down + memmove( firstCharPos, lastCharPos, bytesToMove ); - //set region of display to scroll - scrollRect.setTop(top); - } - else - { - // check that the memory areas that we are going to move are valid - Q_ASSERT( (char*)firstCharPos + bytesToMove < - (char*)(_image + (this->_lines * this->_columns)) ); + //set region of display to scroll + scrollRect.setTop( top ); + } + else + { + // check that the memory areas that we are going to move are valid + Q_ASSERT( ( char * )firstCharPos + bytesToMove < + ( char * )( _image + ( this->_lines * this->_columns ) ) ); - //scroll internal image up - memmove( lastCharPos , firstCharPos , bytesToMove ); + //scroll internal image up + memmove( lastCharPos, firstCharPos, bytesToMove ); - //set region of the display to scroll - scrollRect.setTop(top + abs(lines) * _fontHeight); - } - scrollRect.setHeight(linesToMove * _fontHeight ); + //set region of the display to scroll + scrollRect.setTop( top + abs( lines ) * _fontHeight ); + } + scrollRect.setHeight( linesToMove * _fontHeight ); - Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty()); + Q_ASSERT( scrollRect.isValid() && !scrollRect.isEmpty() ); - //scroll the display vertically to match internal _image - scroll( 0 , _fontHeight * (-lines) , scrollRect ); + //scroll the display vertically to match internal _image + scroll( 0, _fontHeight * ( -lines ), scrollRect ); } QRegion TerminalDisplay::hotSpotRegion() const { - QRegion region; - foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() ) + QRegion region; + foreach ( Filter::HotSpot *hotSpot, _filterChain->hotSpots() ) + { + QRect r; + if ( hotSpot->startLine() == hotSpot->endLine() ) { - QRect r; - if (hotSpot->startLine()==hotSpot->endLine()) { - r.setLeft(hotSpot->startColumn()); - r.setTop(hotSpot->startLine()); - r.setRight(hotSpot->endColumn()); - r.setBottom(hotSpot->endLine()); - region |= imageToWidget(r); - } else { - r.setLeft(hotSpot->startColumn()); - r.setTop(hotSpot->startLine()); - r.setRight(_columns); - r.setBottom(hotSpot->startLine()); - region |= imageToWidget(r); - for ( int line = hotSpot->startLine()+1 ; line < hotSpot->endLine() ; line++ ) { - r.setLeft(0); - r.setTop(line); - r.setRight(_columns); - r.setBottom(line); - region |= imageToWidget(r); - } - r.setLeft(0); - r.setTop(hotSpot->endLine()); - r.setRight(hotSpot->endColumn()); - r.setBottom(hotSpot->endLine()); - region |= imageToWidget(r); - } + r.setLeft( hotSpot->startColumn() ); + r.setTop( hotSpot->startLine() ); + r.setRight( hotSpot->endColumn() ); + r.setBottom( hotSpot->endLine() ); + region |= imageToWidget( r ); } - return region; + else + { + r.setLeft( hotSpot->startColumn() ); + r.setTop( hotSpot->startLine() ); + r.setRight( _columns ); + r.setBottom( hotSpot->startLine() ); + region |= imageToWidget( r ); + for ( int line = hotSpot->startLine() + 1 ; line < hotSpot->endLine() ; line++ ) + { + r.setLeft( 0 ); + r.setTop( line ); + r.setRight( _columns ); + r.setBottom( line ); + region |= imageToWidget( r ); + } + r.setLeft( 0 ); + r.setTop( hotSpot->endLine() ); + r.setRight( hotSpot->endColumn() ); + r.setBottom( hotSpot->endLine() ); + region |= imageToWidget( r ); + } + } + return region; } void TerminalDisplay::processFilters() { - if (!_screenWindow) - return; + if ( !_screenWindow ) + return; - QRegion preUpdateHotSpots = hotSpotRegion(); + QRegion preUpdateHotSpots = hotSpotRegion(); - // use _screenWindow->getImage() here rather than _image because - // other classes may call processFilters() when this display's - // ScreenWindow emits a scrolled() signal - which will happen before - // updateImage() is called on the display and therefore _image is - // out of date at this point - _filterChain->setImage( _screenWindow->getImage(), - _screenWindow->windowLines(), - _screenWindow->windowColumns(), - _screenWindow->getLineProperties() ); - _filterChain->process(); + // use _screenWindow->getImage() here rather than _image because + // other classes may call processFilters() when this display's + // ScreenWindow emits a scrolled() signal - which will happen before + // updateImage() is called on the display and therefore _image is + // out of date at this point + _filterChain->setImage( _screenWindow->getImage(), + _screenWindow->windowLines(), + _screenWindow->windowColumns(), + _screenWindow->getLineProperties() ); + _filterChain->process(); - QRegion postUpdateHotSpots = hotSpotRegion(); + QRegion postUpdateHotSpots = hotSpotRegion(); - update( preUpdateHotSpots | postUpdateHotSpots ); + update( preUpdateHotSpots | postUpdateHotSpots ); } void TerminalDisplay::updateImage() { if ( !_screenWindow ) - return; + return; // optimization - scroll the existing image where possible and // avoid expensive text drawing for parts of the image that // can simply be moved up or down - scrollImage( _screenWindow->scrollCount() , + scrollImage( _screenWindow->scrollCount(), _screenWindow->scrollRegion() ); _screenWindow->resetScrollCount(); - if (!_image) { - // Create _image. - // The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first. - updateImageSize(); + if ( !_image ) + { + // Create _image. + // The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first. + updateImageSize(); } - Character* const newimg = _screenWindow->getImage(); + Character *const newimg = _screenWindow->getImage(); int lines = _screenWindow->windowLines(); int columns = _screenWindow->windowColumns(); - setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() ); + setScroll( _screenWindow->currentLine(), _screenWindow->lineCount() ); Q_ASSERT( this->_usedLines <= this->_lines ); Q_ASSERT( this->_usedColumns <= this->_columns ); - int y,x,len; + int y, x, len; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); @@ -989,11 +995,11 @@ void TerminalDisplay::updateImage() CharacterColor _clipboard; // undefined int cr = -1; // undefined - const int linesToUpdate = qMin(this->_lines, qMax(0,lines )); - const int columnsToUpdate = qMin(this->_columns,qMax(0,columns)); + const int linesToUpdate = qMin( this->_lines, qMax( 0, lines ) ); + const int columnsToUpdate = qMin( this->_columns, qMax( 0, columns ) ); QChar *disstrU = new QChar[columnsToUpdate]; - char *dirtyMask = new char[columnsToUpdate+2]; + char *dirtyMask = new char[columnsToUpdate + 2]; QRegion dirtyRegion; // debugging variable, this records the number of lines that are found to @@ -1001,138 +1007,138 @@ void TerminalDisplay::updateImage() // which therefore need to be repainted int dirtyLineCount = 0; - for (y = 0; y < linesToUpdate; ++y) + for ( y = 0; y < linesToUpdate; ++y ) { - const Character* currentLine = &_image[y*this->_columns]; - const Character* const newLine = &newimg[y*columns]; + const Character *currentLine = &_image[y * this->_columns]; + const Character *const newLine = &newimg[y * columns]; bool updateLine = false; // The dirty mask indicates which characters need repainting. We also // mark surrounding neighbours dirty, in case the character exceeds // its cell boundaries - memset(dirtyMask, 0, columnsToUpdate+2); + memset( dirtyMask, 0, columnsToUpdate + 2 ); - for( x = 0 ; x < columnsToUpdate ; ++x) + for ( x = 0 ; x < columnsToUpdate ; ++x ) { - if ( newLine[x] != currentLine[x] ) - { - dirtyMask[x] = true; - } - } - - if (!_resizing) // not while _resizing, we're expecting a paintEvent - for (x = 0; x < columnsToUpdate; ++x) - { - _hasBlinker |= (newLine[x].rendition & RE_BLINK); - - // Start drawing if this character or the next one differs. - // We also take the next one into account to handle the situation - // where characters exceed their cell width. - if (dirtyMask[x]) + if ( newLine[x] != currentLine[x] ) { - quint16 c = newLine[x+0].character; - if ( !c ) - continue; - int p = 0; - disstrU[p++] = c; //fontMap(c); - bool lineDraw = isLineChar(c); - bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0); - cr = newLine[x].rendition; - _clipboard = newLine[x].backgroundColor; - if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor; - int lln = columnsToUpdate - x; - for (len = 1; len < lln; ++len) + dirtyMask[x] = true; + } + } + + if ( !_resizing ) // not while _resizing, we're expecting a paintEvent + for ( x = 0; x < columnsToUpdate; ++x ) + { + _hasBlinker |= ( newLine[x].rendition & RE_BLINK ); + + // Start drawing if this character or the next one differs. + // We also take the next one into account to handle the situation + // where characters exceed their cell width. + if ( dirtyMask[x] ) { - const Character& ch = newLine[x+len]; - - if (!ch.character) - continue; // Skip trailing part of multi-col chars. - - bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0); - - if ( ch.foregroundColor != cf || - ch.backgroundColor != _clipboard || - ch.rendition != cr || - !dirtyMask[x+len] || - isLineChar(c) != lineDraw || - nextIsDoubleWidth != doubleWidth ) - break; - + quint16 c = newLine[x + 0].character; + if ( !c ) + continue; + int p = 0; disstrU[p++] = c; //fontMap(c); + bool lineDraw = isLineChar( c ); + bool doubleWidth = ( x + 1 == columnsToUpdate ) ? false : ( newLine[x + 1].character == 0 ); + cr = newLine[x].rendition; + _clipboard = newLine[x].backgroundColor; + if ( newLine[x].foregroundColor != cf ) cf = newLine[x].foregroundColor; + int lln = columnsToUpdate - x; + for ( len = 1; len < lln; ++len ) + { + const Character &ch = newLine[x + len]; + + if ( !ch.character ) + continue; // Skip trailing part of multi-col chars. + + bool nextIsDoubleWidth = ( x + len + 1 == columnsToUpdate ) ? false : ( newLine[x + len + 1].character == 0 ); + + if ( ch.foregroundColor != cf || + ch.backgroundColor != _clipboard || + ch.rendition != cr || + !dirtyMask[x + len] || + isLineChar( c ) != lineDraw || + nextIsDoubleWidth != doubleWidth ) + break; + + disstrU[p++] = c; //fontMap(c); + } + + QString unistr( disstrU, p ); + + bool saveFixedFont = _fixedFont; + if ( lineDraw ) + _fixedFont = false; + if ( doubleWidth ) + _fixedFont = false; + + updateLine = true; + + _fixedFont = saveFixedFont; + x += len - 1; } - QString unistr(disstrU, p); - - bool saveFixedFont = _fixedFont; - if (lineDraw) - _fixedFont = false; - if (doubleWidth) - _fixedFont = false; - - updateLine = true; - - _fixedFont = saveFixedFont; - x += len - 1; } - } - //both the top and bottom halves of double height _lines must always be redrawn //although both top and bottom halves contain the same characters, only //the top one is actually //drawn. - if (_lineProperties.count() > y) - updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT); + if ( _lineProperties.count() > y ) + updateLine |= ( _lineProperties[y] & LINE_DOUBLEHEIGHT ); // if the characters on the line are different in the old and the new _image // then this line must be repainted. - if (updateLine) + if ( updateLine ) { - dirtyLineCount++; + dirtyLineCount++; - // add the area occupied by this line to the region which needs to be - // repainted - QRect dirtyRect = QRect( _leftMargin+tLx , - _topMargin+tLy+_fontHeight*y , - _fontWidth * columnsToUpdate , - _fontHeight ); + // add the area occupied by this line to the region which needs to be + // repainted + QRect dirtyRect = QRect( _leftMargin + tLx, + _topMargin + tLy + _fontHeight * y, + _fontWidth * columnsToUpdate, + _fontHeight ); - dirtyRegion |= dirtyRect; + dirtyRegion |= dirtyRect; } // replace the line of characters in the old _image with the // current line of the new _image - memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character)); + memcpy( ( void * )currentLine, ( const void * )newLine, columnsToUpdate * sizeof( Character ) ); } // if the new _image is smaller than the previous _image, then ensure that the area // outside the new _image is cleared if ( linesToUpdate < _usedLines ) { - dirtyRegion |= QRect( _leftMargin+tLx , - _topMargin+tLy+_fontHeight*linesToUpdate , - _fontWidth * this->_columns , - _fontHeight * (_usedLines-linesToUpdate) ); + dirtyRegion |= QRect( _leftMargin + tLx, + _topMargin + tLy + _fontHeight * linesToUpdate, + _fontWidth * this->_columns, + _fontHeight * ( _usedLines - linesToUpdate ) ); } _usedLines = linesToUpdate; if ( columnsToUpdate < _usedColumns ) { - dirtyRegion |= QRect( _leftMargin+tLx+columnsToUpdate*_fontWidth , - _topMargin+tLy , - _fontWidth * (_usedColumns-columnsToUpdate) , - _fontHeight * this->_lines ); + dirtyRegion |= QRect( _leftMargin + tLx + columnsToUpdate * _fontWidth, + _topMargin + tLy, + _fontWidth * ( _usedColumns - columnsToUpdate ), + _fontHeight * this->_lines ); } _usedColumns = columnsToUpdate; dirtyRegion |= _inputMethodData.previousPreeditRect; // update the parts of the display which have changed - update(dirtyRegion); + update( dirtyRegion ); - if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( TEXT_BLINK_DELAY ); - if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; } + if ( _hasBlinker && !_blinkTimer->isActive() ) _blinkTimer->start( TEXT_BLINK_DELAY ); + if ( !_hasBlinker && _blinkTimer->isActive() ) { _blinkTimer->stop(); _blinking = false; } delete[] dirtyMask; delete[] disstrU; @@ -1140,425 +1146,434 @@ void TerminalDisplay::updateImage() void TerminalDisplay::showResizeNotification() { - if (_terminalSizeHint && isVisible()) + if ( _terminalSizeHint && isVisible() ) { - if (_terminalSizeStartup) { - _terminalSizeStartup=false; - return; - } - if (!_resizeWidget) - { - _resizeWidget = new QLabel(QStringLiteral("Size: XXX x XXX"), this); - _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(QStringLiteral("Size: XXX x XXX"))); - _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); - _resizeWidget->setAlignment(Qt::AlignCenter); + if ( _terminalSizeStartup ) + { + _terminalSizeStartup = false; + return; + } + if ( !_resizeWidget ) + { + _resizeWidget = new QLabel( QStringLiteral( "Size: XXX x XXX" ), this ); + _resizeWidget->setMinimumWidth( _resizeWidget->fontMetrics().width( QStringLiteral( "Size: XXX x XXX" ) ) ); + _resizeWidget->setMinimumHeight( _resizeWidget->sizeHint().height() ); + _resizeWidget->setAlignment( Qt::AlignCenter ); - _resizeWidget->setStyleSheet(QStringLiteral("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)")); + _resizeWidget->setStyleSheet( QStringLiteral( "background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)" ) ); - _resizeTimer = new QTimer(this); - _resizeTimer->setSingleShot(true); - connect(_resizeTimer, &QTimer::timeout, _resizeWidget, &QWidget::hide); - } - QString sizeStr = QStringLiteral("Size: %1 x %2").arg(_columns).arg(_lines); - _resizeWidget->setText(sizeStr); - _resizeWidget->move((width()-_resizeWidget->width())/2, - (height()-_resizeWidget->height())/2+20); - _resizeWidget->show(); - _resizeTimer->start(1000); + _resizeTimer = new QTimer( this ); + _resizeTimer->setSingleShot( true ); + connect( _resizeTimer, &QTimer::timeout, _resizeWidget, &QWidget::hide ); + } + QString sizeStr = QStringLiteral( "Size: %1 x %2" ).arg( _columns ).arg( _lines ); + _resizeWidget->setText( sizeStr ); + _resizeWidget->move( ( width() - _resizeWidget->width() ) / 2, + ( height() - _resizeWidget->height() ) / 2 + 20 ); + _resizeWidget->show(); + _resizeTimer->start( 1000 ); } } -void TerminalDisplay::setBlinkingCursor(bool blink) +void TerminalDisplay::setBlinkingCursor( bool blink ) { - _hasBlinkingCursor=blink; + _hasBlinkingCursor = blink; - if (blink && !_blinkCursorTimer->isActive()) - _blinkCursorTimer->start(QApplication::cursorFlashTime() / 2); + if ( blink && !_blinkCursorTimer->isActive() ) + _blinkCursorTimer->start( QApplication::cursorFlashTime() / 2 ); - if (!blink && _blinkCursorTimer->isActive()) + if ( !blink && _blinkCursorTimer->isActive() ) { _blinkCursorTimer->stop(); - if (_cursorBlinking) + if ( _cursorBlinking ) blinkCursorEvent(); else _cursorBlinking = false; } } -void TerminalDisplay::setBlinkingTextEnabled(bool blink) +void TerminalDisplay::setBlinkingTextEnabled( bool blink ) { - _allowBlinkingText = blink; + _allowBlinkingText = blink; - if (blink && !_blinkTimer->isActive()) - _blinkTimer->start(TEXT_BLINK_DELAY); + if ( blink && !_blinkTimer->isActive() ) + _blinkTimer->start( TEXT_BLINK_DELAY ); - if (!blink && _blinkTimer->isActive()) - { - _blinkTimer->stop(); - _blinking = false; - } -} - -void TerminalDisplay::focusOutEvent(QFocusEvent*) -{ - emit termLostFocus(); - // trigger a repaint of the cursor so that it is both visible (in case - // it was hidden during blinking) - // and drawn in a focused out state - _cursorBlinking = false; - updateCursor(); - - _blinkCursorTimer->stop(); - if (_blinking) - blinkEvent(); - - _blinkTimer->stop(); -} -void TerminalDisplay::focusInEvent(QFocusEvent*) -{ - emit termGetFocus(); - if (_hasBlinkingCursor) - { - _blinkCursorTimer->start(); - } - updateCursor(); - - if (_hasBlinker) - _blinkTimer->start(); -} - -void TerminalDisplay::paintEvent( QPaintEvent* pe ) -{ - QPainter paint(this); - - foreach (const QRect &rect, (pe->region() & contentsRect()).rects()) + if ( !blink && _blinkTimer->isActive() ) { - drawBackground(paint,rect,palette().background().color(), - true /* use opacity setting */); - drawContents(paint, rect); + _blinkTimer->stop(); + _blinking = false; } - drawInputMethodPreeditString(paint,preeditRect()); - paintFilters(paint); +} + +void TerminalDisplay::focusOutEvent( QFocusEvent * ) +{ + emit termLostFocus(); + // trigger a repaint of the cursor so that it is both visible (in case + // it was hidden during blinking) + // and drawn in a focused out state + _cursorBlinking = false; + updateCursor(); + + _blinkCursorTimer->stop(); + if ( _blinking ) + blinkEvent(); + + _blinkTimer->stop(); +} +void TerminalDisplay::focusInEvent( QFocusEvent * ) +{ + emit termGetFocus(); + if ( _hasBlinkingCursor ) + { + _blinkCursorTimer->start(); + } + updateCursor(); + + if ( _hasBlinker ) + _blinkTimer->start(); +} + +void TerminalDisplay::paintEvent( QPaintEvent *pe ) +{ + QPainter paint( this ); + + foreach ( const QRect &rect, ( pe->region() & contentsRect() ).rects() ) + { + drawBackground( paint, rect, palette().background().color(), + true /* use opacity setting */ ); + drawContents( paint, rect ); + } + drawInputMethodPreeditString( paint, preeditRect() ); + paintFilters( paint ); } QPoint TerminalDisplay::cursorPosition() const { - if (_screenWindow) - return _screenWindow->cursorPosition(); - else - return QPoint(0,0); + if ( _screenWindow ) + return _screenWindow->cursorPosition(); + else + return QPoint( 0, 0 ); } QRect TerminalDisplay::preeditRect() const { - const int preeditLength = string_width(_inputMethodData.preeditString); + const int preeditLength = string_width( _inputMethodData.preeditString ); - if ( preeditLength == 0 ) - return QRect(); + if ( preeditLength == 0 ) + return QRect(); - return QRect(_leftMargin + _fontWidth*cursorPosition().x(), - _topMargin + _fontHeight*cursorPosition().y(), - _fontWidth*preeditLength, - _fontHeight); + return QRect( _leftMargin + _fontWidth * cursorPosition().x(), + _topMargin + _fontHeight * cursorPosition().y(), + _fontWidth * preeditLength, + _fontHeight ); } -void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect) +void TerminalDisplay::drawInputMethodPreeditString( QPainter &painter, const QRect &rect ) { - if ( _inputMethodData.preeditString.isEmpty() ) - return; + if ( _inputMethodData.preeditString.isEmpty() ) + return; - const QPoint cursorPos = cursorPosition(); + const QPoint cursorPos = cursorPosition(); - bool invertColors = false; - const QColor background = _colorTable[DEFAULT_BACK_COLOR].color; - const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color; - const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())]; + bool invertColors = false; + const QColor background = _colorTable[DEFAULT_BACK_COLOR].color; + const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color; + const Character *style = &_image[loc( cursorPos.x(), cursorPos.y() )]; - drawBackground(painter,rect,background,true); - drawCursor(painter,rect,foreground,background,invertColors); - drawCharacters(painter,rect,_inputMethodData.preeditString,style,invertColors); + drawBackground( painter, rect, background, true ); + drawCursor( painter, rect, foreground, background, invertColors ); + drawCharacters( painter, rect, _inputMethodData.preeditString, style, invertColors ); - _inputMethodData.previousPreeditRect = rect; + _inputMethodData.previousPreeditRect = rect; } -FilterChain* TerminalDisplay::filterChain() const +FilterChain *TerminalDisplay::filterChain() const { - return _filterChain; + return _filterChain; } -void TerminalDisplay::paintFilters(QPainter& painter) +void TerminalDisplay::paintFilters( QPainter &painter ) { - // get color of character under mouse and use it to draw - // lines for filters - QPoint cursorPos = mapFromGlobal(QCursor::pos()); - int cursorLine; - int cursorColumn; - int scrollBarWidth = (_scrollbarLocation == ScrollBarLeft) ? _scrollBar->width() : 0; + // get color of character under mouse and use it to draw + // lines for filters + QPoint cursorPos = mapFromGlobal( QCursor::pos() ); + int cursorLine; + int cursorColumn; + int scrollBarWidth = ( _scrollbarLocation == ScrollBarLeft ) ? _scrollBar->width() : 0; - getCharacterPosition( cursorPos , cursorLine , cursorColumn ); - Character cursorCharacter = _image[loc(cursorColumn,cursorLine)]; + getCharacterPosition( cursorPos, cursorLine, cursorColumn ); + Character cursorCharacter = _image[loc( cursorColumn, cursorLine )]; - painter.setPen( QPen(cursorCharacter.foregroundColor.color(colorTable())) ); + painter.setPen( QPen( cursorCharacter.foregroundColor.color( colorTable() ) ) ); - // iterate over hotspots identified by the display's currently active filters - // and draw appropriate visuals to indicate the presence of the hotspot + // iterate over hotspots identified by the display's currently active filters + // and draw appropriate visuals to indicate the presence of the hotspot - QList spots = _filterChain->hotSpots(); - QListIterator iter(spots); - while (iter.hasNext()) + QList spots = _filterChain->hotSpots(); + QListIterator iter( spots ); + while ( iter.hasNext() ) + { + Filter::HotSpot *spot = iter.next(); + + QRegion region; + if ( spot->type() == Filter::HotSpot::Link ) { - Filter::HotSpot* spot = iter.next(); - - QRegion region; - if ( spot->type() == Filter::HotSpot::Link ) { - QRect r; - if (spot->startLine()==spot->endLine()) { - r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, - spot->startLine()*_fontHeight + 1, - (spot->endColumn()-1)*_fontWidth - 1 + scrollBarWidth, - (spot->endLine()+1)*_fontHeight - 1 ); - region |= r; - } else { - r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, - spot->startLine()*_fontHeight + 1, - (_columns-1)*_fontWidth - 1 + scrollBarWidth, - (spot->startLine()+1)*_fontHeight - 1 ); - region |= r; - for ( int line = spot->startLine()+1 ; line < spot->endLine() ; line++ ) { - r.setCoords( 0*_fontWidth + 1 + scrollBarWidth, - line*_fontHeight + 1, - (_columns-1)*_fontWidth - 1 + scrollBarWidth, - (line+1)*_fontHeight - 1 ); - region |= r; - } - r.setCoords( 0*_fontWidth + 1 + scrollBarWidth, - spot->endLine()*_fontHeight + 1, - (spot->endColumn()-1)*_fontWidth - 1 + scrollBarWidth, - (spot->endLine()+1)*_fontHeight - 1 ); - region |= r; - } - } - - for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ ) + QRect r; + if ( spot->startLine() == spot->endLine() ) + { + r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, + spot->startLine()*_fontHeight + 1, + ( spot->endColumn() - 1 )*_fontWidth - 1 + scrollBarWidth, + ( spot->endLine() + 1 )*_fontHeight - 1 ); + region |= r; + } + else + { + r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, + spot->startLine()*_fontHeight + 1, + ( _columns - 1 )*_fontWidth - 1 + scrollBarWidth, + ( spot->startLine() + 1 )*_fontHeight - 1 ); + region |= r; + for ( int line = spot->startLine() + 1 ; line < spot->endLine() ; line++ ) { - int startColumn = 0; - int endColumn = _columns-1; // TODO use number of _columns which are actually - // occupied on this line rather than the width of the - // display in _columns - - // ignore whitespace at the end of the lines - while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 ) - endColumn--; - - // increment here because the column which we want to set 'endColumn' to - // is the first whitespace character at the end of the line - endColumn++; - - if ( line == spot->startLine() ) - startColumn = spot->startColumn(); - if ( line == spot->endLine() ) - endColumn = spot->endColumn(); - - // subtract one pixel from - // the right and bottom so that - // we do not overdraw adjacent - // hotspots - // - // subtracting one pixel from all sides also prevents an edge case where - // moving the mouse outside a link could still leave it underlined - // because the check below for the position of the cursor - // finds it on the border of the target area - QRect r; - r.setCoords( startColumn*_fontWidth + 1 + scrollBarWidth, - line*_fontHeight + 1, - endColumn*_fontWidth - 1 + scrollBarWidth, - (line+1)*_fontHeight - 1 ); - // Underline link hotspots - if ( spot->type() == Filter::HotSpot::Link ) - { - QFontMetrics metrics(font()); - - // find the baseline (which is the invisible line that the characters in the font sit on, - // with some having tails dangling below) - int baseline = r.bottom() - metrics.descent(); - // find the position of the underline below that - int underlinePos = baseline + metrics.underlinePos(); - if ( region.contains( mapFromGlobal(QCursor::pos()) ) ){ - painter.drawLine( r.left() , underlinePos , - r.right() , underlinePos ); - } - } - // Marker hotspots simply have a transparent rectanglular shape - // drawn on top of them - else if ( spot->type() == Filter::HotSpot::Marker ) - { - //TODO - Do not use a hardcoded colour for this - painter.fillRect(r,QBrush(QColor(255,0,0,120))); - } + r.setCoords( 0 * _fontWidth + 1 + scrollBarWidth, + line * _fontHeight + 1, + ( _columns - 1 )*_fontWidth - 1 + scrollBarWidth, + ( line + 1 )*_fontHeight - 1 ); + region |= r; } + r.setCoords( 0 * _fontWidth + 1 + scrollBarWidth, + spot->endLine()*_fontHeight + 1, + ( spot->endColumn() - 1 )*_fontWidth - 1 + scrollBarWidth, + ( spot->endLine() + 1 )*_fontHeight - 1 ); + region |= r; + } } + + for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ ) + { + int startColumn = 0; + int endColumn = _columns - 1; // TODO use number of _columns which are actually + // occupied on this line rather than the width of the + // display in _columns + + // ignore whitespace at the end of the lines + while ( QChar( _image[loc( endColumn, line )].character ).isSpace() && endColumn > 0 ) + endColumn--; + + // increment here because the column which we want to set 'endColumn' to + // is the first whitespace character at the end of the line + endColumn++; + + if ( line == spot->startLine() ) + startColumn = spot->startColumn(); + if ( line == spot->endLine() ) + endColumn = spot->endColumn(); + + // subtract one pixel from + // the right and bottom so that + // we do not overdraw adjacent + // hotspots + // + // subtracting one pixel from all sides also prevents an edge case where + // moving the mouse outside a link could still leave it underlined + // because the check below for the position of the cursor + // finds it on the border of the target area + QRect r; + r.setCoords( startColumn * _fontWidth + 1 + scrollBarWidth, + line * _fontHeight + 1, + endColumn * _fontWidth - 1 + scrollBarWidth, + ( line + 1 )*_fontHeight - 1 ); + // Underline link hotspots + if ( spot->type() == Filter::HotSpot::Link ) + { + QFontMetrics metrics( font() ); + + // find the baseline (which is the invisible line that the characters in the font sit on, + // with some having tails dangling below) + int baseline = r.bottom() - metrics.descent(); + // find the position of the underline below that + int underlinePos = baseline + metrics.underlinePos(); + if ( region.contains( mapFromGlobal( QCursor::pos() ) ) ) + { + painter.drawLine( r.left(), underlinePos, + r.right(), underlinePos ); + } + } + // Marker hotspots simply have a transparent rectanglular shape + // drawn on top of them + else if ( spot->type() == Filter::HotSpot::Marker ) + { + //TODO - Do not use a hardcoded colour for this + painter.fillRect( r, QBrush( QColor( 255, 0, 0, 120 ) ) ); + } + } + } } -int TerminalDisplay::textWidth(const int startColumn, const int length, const int line) const +int TerminalDisplay::textWidth( const int startColumn, const int length, const int line ) const { - QFontMetrics fm(font()); + QFontMetrics fm( font() ); int result = 0; - for (int column = 0; column < length; column++) { - result += fm.width(_image[loc(startColumn + column, line)].character); + for ( int column = 0; column < length; column++ ) + { + result += fm.width( _image[loc( startColumn + column, line )].character ); } return result; } -QRect TerminalDisplay::calculateTextArea(int topLeftX, int topLeftY, int startColumn, int line, int length) { - int left = _fixedFont ? _fontWidth * startColumn : textWidth(0, startColumn, line); +QRect TerminalDisplay::calculateTextArea( int topLeftX, int topLeftY, int startColumn, int line, int length ) +{ + int left = _fixedFont ? _fontWidth * startColumn : textWidth( 0, startColumn, line ); int top = _fontHeight * line; - int width = _fixedFont ? _fontWidth * length : textWidth(startColumn, length, line); - return QRect(_leftMargin + topLeftX + left, - _topMargin + topLeftY + top, - width, - _fontHeight); + int width = _fixedFont ? _fontWidth * length : textWidth( startColumn, length, line ); + return QRect( _leftMargin + topLeftX + left, + _topMargin + topLeftY + top, + width, + _fontHeight ); } -void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) +void TerminalDisplay::drawContents( QPainter &paint, const QRect &rect ) { QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); - int lux = qMin(_usedColumns-1, qMax(0,(rect.left() - tLx - _leftMargin ) / _fontWidth)); - int luy = qMin(_usedLines-1, qMax(0,(rect.top() - tLy - _topMargin ) / _fontHeight)); - int rlx = qMin(_usedColumns-1, qMax(0,(rect.right() - tLx - _leftMargin ) / _fontWidth)); - int rly = qMin(_usedLines-1, qMax(0,(rect.bottom() - tLy - _topMargin ) / _fontHeight)); + int lux = qMin( _usedColumns - 1, qMax( 0, ( rect.left() - tLx - _leftMargin ) / _fontWidth ) ); + int luy = qMin( _usedLines - 1, qMax( 0, ( rect.top() - tLy - _topMargin ) / _fontHeight ) ); + int rlx = qMin( _usedColumns - 1, qMax( 0, ( rect.right() - tLx - _leftMargin ) / _fontWidth ) ); + int rly = qMin( _usedLines - 1, qMax( 0, ( rect.bottom() - tLy - _topMargin ) / _fontHeight ) ); const int bufferSize = _usedColumns; QString unistr; - unistr.reserve(bufferSize); - for (int y = luy; y <= rly; y++) + unistr.reserve( bufferSize ); + for ( int y = luy; y <= rly; y++ ) { - quint16 c = _image[loc(lux,y)].character; + quint16 c = _image[loc( lux, y )].character; int x = lux; - if(!c && x) + if ( !c && x ) x--; // Search for start of multi-column character - for (; x <= rlx; x++) + for ( ; x <= rlx; x++ ) { int len = 1; int p = 0; // reset our buffer to the maximal size - unistr.resize(bufferSize); + unistr.resize( bufferSize ); QChar *disstrU = unistr.data(); // is this a single character or a sequence of characters ? - if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR ) + if ( _image[loc( x, y )].rendition & RE_EXTENDED_CHAR ) { // sequence of characters ushort extendedCharLength = 0; - ushort* chars = ExtendedCharTable::instance - .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength); + ushort *chars = ExtendedCharTable::instance + .lookupExtendedChar( _image[loc( x, y )].charSequence, extendedCharLength ); for ( int index = 0 ; index < extendedCharLength ; index++ ) { - Q_ASSERT( p < bufferSize ); - disstrU[p++] = chars[index]; + Q_ASSERT( p < bufferSize ); + disstrU[p++] = chars[index]; } } else { // single character - c = _image[loc(x,y)].character; - if (c) + c = _image[loc( x, y )].character; + if ( c ) { - Q_ASSERT( p < bufferSize ); - disstrU[p++] = c; //fontMap(c); + Q_ASSERT( p < bufferSize ); + disstrU[p++] = c; //fontMap(c); } } - bool lineDraw = isLineChar(c); - bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0); - CharacterColor currentForeground = _image[loc(x,y)].foregroundColor; - CharacterColor currentBackground = _image[loc(x,y)].backgroundColor; - quint8 currentRendition = _image[loc(x,y)].rendition; + bool lineDraw = isLineChar( c ); + bool doubleWidth = ( _image[ qMin( loc( x, y ) + 1, _imageSize ) ].character == 0 ); + CharacterColor currentForeground = _image[loc( x, y )].foregroundColor; + CharacterColor currentBackground = _image[loc( x, y )].backgroundColor; + quint8 currentRendition = _image[loc( x, y )].rendition; - while (x+len <= rlx && - _image[loc(x+len,y)].foregroundColor == currentForeground && - _image[loc(x+len,y)].backgroundColor == currentBackground && - _image[loc(x+len,y)].rendition == currentRendition && - (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth && - isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment! + while ( x + len <= rlx && + _image[loc( x + len, y )].foregroundColor == currentForeground && + _image[loc( x + len, y )].backgroundColor == currentBackground && + _image[loc( x + len, y )].rendition == currentRendition && + ( _image[ qMin( loc( x + len, y ) + 1, _imageSize ) ].character == 0 ) == doubleWidth && + isLineChar( c = _image[loc( x + len, y )].character ) == lineDraw ) // Assignment! { - if (c) + if ( c ) disstrU[p++] = c; //fontMap(c); - if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition + if ( doubleWidth ) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition len++; // Skip trailing part of multi-column character len++; } - if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character)) + if ( ( x + len < _usedColumns ) && ( !_image[loc( x + len, y )].character ) ) len++; // Adjust for trailing part of multi-column character - bool save__fixedFont = _fixedFont; - if (lineDraw) - _fixedFont = false; - if (doubleWidth) - _fixedFont = false; - unistr.resize(p); + bool save__fixedFont = _fixedFont; + if ( lineDraw ) + _fixedFont = false; + if ( doubleWidth ) + _fixedFont = false; + unistr.resize( p ); - // Create a text scaling matrix for double width and double height lines. - QMatrix textScale; + // Create a text scaling matrix for double width and double height lines. + QMatrix textScale; - if (y < _lineProperties.size()) - { - if (_lineProperties[y] & LINE_DOUBLEWIDTH) - textScale.scale(2,1); + if ( y < _lineProperties.size() ) + { + if ( _lineProperties[y] & LINE_DOUBLEWIDTH ) + textScale.scale( 2, 1 ); - if (_lineProperties[y] & LINE_DOUBLEHEIGHT) - textScale.scale(1,2); - } + if ( _lineProperties[y] & LINE_DOUBLEHEIGHT ) + textScale.scale( 1, 2 ); + } - //Apply text scaling matrix. - paint.setWorldMatrix(textScale, true); + //Apply text scaling matrix. + paint.setWorldMatrix( textScale, true ); - //calculate the area in which the text will be drawn - QRect textArea = calculateTextArea(tLx, tLy, x, y, len); + //calculate the area in which the text will be drawn + QRect textArea = calculateTextArea( tLx, tLy, x, y, len ); - //move the calculated area to take account of scaling applied to the painter. - //the position of the area from the origin (0,0) is scaled - //by the opposite of whatever - //transformation has been applied to the painter. this ensures that - //painting does actually start from textArea.topLeft() - //(instead of textArea.topLeft() * painter-scale) - textArea.moveTopLeft( textScale.inverted().map(textArea.topLeft()) ); + //move the calculated area to take account of scaling applied to the painter. + //the position of the area from the origin (0,0) is scaled + //by the opposite of whatever + //transformation has been applied to the painter. this ensures that + //painting does actually start from textArea.topLeft() + //(instead of textArea.topLeft() * painter-scale) + textArea.moveTopLeft( textScale.inverted().map( textArea.topLeft() ) ); - //paint text fragment - drawTextFragment( paint, - textArea, - unistr, - &_image[loc(x,y)] ); //, - //0, - //!_isPrinting ); + //paint text fragment + drawTextFragment( paint, + textArea, + unistr, + &_image[loc( x, y )] ); //, + //0, + //!_isPrinting ); - _fixedFont = save__fixedFont; + _fixedFont = save__fixedFont; - //reset back to single-width, single-height _lines - paint.setWorldMatrix(textScale.inverted(), true); + //reset back to single-width, single-height _lines + paint.setWorldMatrix( textScale.inverted(), true ); - if (y < _lineProperties.size()-1) - { - //double-height _lines are represented by two adjacent _lines - //containing the same characters - //both _lines will have the LINE_DOUBLEHEIGHT attribute. - //If the current line has the LINE_DOUBLEHEIGHT attribute, - //we can therefore skip the next line - if (_lineProperties[y] & LINE_DOUBLEHEIGHT) - y++; - } + if ( y < _lineProperties.size() - 1 ) + { + //double-height _lines are represented by two adjacent _lines + //containing the same characters + //both _lines will have the LINE_DOUBLEHEIGHT attribute. + //If the current line has the LINE_DOUBLEHEIGHT attribute, + //we can therefore skip the next line + if ( _lineProperties[y] & LINE_DOUBLEHEIGHT ) + y++; + } - x += len - 1; + x += len - 1; } } } void TerminalDisplay::blinkEvent() { - if (!_allowBlinkingText) return; + if ( !_allowBlinkingText ) return; _blinking = !_blinking; @@ -1568,21 +1583,21 @@ void TerminalDisplay::blinkEvent() update(); } -QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const +QRect TerminalDisplay::imageToWidget( const QRect &imageArea ) const { - QRect result; - result.setLeft( _leftMargin + _fontWidth * imageArea.left() ); - result.setTop( _topMargin + _fontHeight * imageArea.top() ); - result.setWidth( _fontWidth * imageArea.width() ); - result.setHeight( _fontHeight * imageArea.height() ); + QRect result; + result.setLeft( _leftMargin + _fontWidth * imageArea.left() ); + result.setTop( _topMargin + _fontHeight * imageArea.top() ); + result.setWidth( _fontWidth * imageArea.width() ); + result.setHeight( _fontHeight * imageArea.height() ); - return result; + return result; } void TerminalDisplay::updateCursor() { - QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) ); - update(cursorRect); + QRect cursorRect = imageToWidget( QRect( cursorPosition(), QSize( 1, 1 ) ) ); + update( cursorRect ); } void TerminalDisplay::blinkCursorEvent() @@ -1597,7 +1612,7 @@ void TerminalDisplay::blinkCursorEvent() /* */ /* ------------------------------------------------------------------------- */ -void TerminalDisplay::resizeEvent(QResizeEvent*) +void TerminalDisplay::resizeEvent( QResizeEvent * ) { updateImageSize(); processFilters(); @@ -1605,49 +1620,49 @@ void TerminalDisplay::resizeEvent(QResizeEvent*) void TerminalDisplay::propagateSize() { - if (_isFixedSize) + if ( _isFixedSize ) { - setSize(_columns, _lines); - QWidget::setFixedSize(sizeHint()); - parentWidget()->adjustSize(); - parentWidget()->setFixedSize(parentWidget()->sizeHint()); - return; + setSize( _columns, _lines ); + QWidget::setFixedSize( sizeHint() ); + parentWidget()->adjustSize(); + parentWidget()->setFixedSize( parentWidget()->sizeHint() ); + return; } - if (_image) - updateImageSize(); + if ( _image ) + updateImageSize(); } void TerminalDisplay::updateImageSize() { - Character* oldimg = _image; + Character *oldimg = _image; int oldlin = _lines; int oldcol = _columns; makeImage(); // copy the old image to reduce flicker - int lines = qMin(oldlin,_lines); - int columns = qMin(oldcol,_columns); + int lines = qMin( oldlin, _lines ); + int columns = qMin( oldcol, _columns ); - if (oldimg) + if ( oldimg ) { - for (int line = 0; line < lines; line++) + for ( int line = 0; line < lines; line++ ) { - memcpy((void*)&_image[_columns*line], - (void*)&oldimg[oldcol*line],columns*sizeof(Character)); + memcpy( ( void * )&_image[_columns * line], + ( void * )&oldimg[oldcol * line], columns * sizeof( Character ) ); } delete[] oldimg; } - if (_screenWindow) - _screenWindow->setWindowLines(_lines); + if ( _screenWindow ) + _screenWindow->setWindowLines( _lines ); - _resizing = (oldlin!=_lines) || (oldcol!=_columns); + _resizing = ( oldlin != _lines ) || ( oldcol != _columns ); if ( _resizing ) { - showResizeNotification(); - emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent + showResizeNotification(); + emit changedContentSizeSignal( _contentHeight, _contentWidth ); // expose resizeEvent } _resizing = false; @@ -1658,13 +1673,13 @@ void TerminalDisplay::updateImageSize() // //TODO: Perhaps it would be better to have separate signals for show and hide instead of using //the same signal as the one for a content size change -void TerminalDisplay::showEvent(QShowEvent*) +void TerminalDisplay::showEvent( QShowEvent * ) { - emit changedContentSizeSignal(_contentHeight,_contentWidth); + emit changedContentSizeSignal( _contentHeight, _contentWidth ); } -void TerminalDisplay::hideEvent(QHideEvent*) +void TerminalDisplay::hideEvent( QHideEvent * ) { - emit changedContentSizeSignal(_contentHeight,_contentWidth); + emit changedContentSizeSignal( _contentHeight, _contentWidth ); } /* ------------------------------------------------------------------------- */ @@ -1673,10 +1688,10 @@ void TerminalDisplay::hideEvent(QHideEvent*) /* */ /* ------------------------------------------------------------------------- */ -void TerminalDisplay::scrollBarPositionChanged(int) +void TerminalDisplay::scrollBarPositionChanged( int ) { if ( !_screenWindow ) - return; + return; _screenWindow->scrollTo( _scrollBar->value() ); @@ -1684,13 +1699,13 @@ void TerminalDisplay::scrollBarPositionChanged(int) // the display to automatically track new output, // that is, scroll down automatically // to how new _lines as they are added - const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); + const bool atEndOfOutput = ( _scrollBar->value() == _scrollBar->maximum() ); _screenWindow->setTrackOutput( atEndOfOutput ); updateImage(); } -void TerminalDisplay::setScroll(int cursor, int slines) +void TerminalDisplay::setScroll( int cursor, int slines ) { // update _scrollBar if the range or value has changed, // otherwise return @@ -1698,39 +1713,39 @@ void TerminalDisplay::setScroll(int cursor, int slines) // setting the range or value of a _scrollBar will always trigger // a repaint, so it should be avoided if it is not necessary if ( _scrollBar->minimum() == 0 && - _scrollBar->maximum() == (slines - _lines) && + _scrollBar->maximum() == ( slines - _lines ) && _scrollBar->value() == cursor ) { - return; + return; } - disconnect(_scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged); - _scrollBar->setRange(0,slines - _lines); - _scrollBar->setSingleStep(1); - _scrollBar->setPageStep(_lines); - _scrollBar->setValue(cursor); - connect(_scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged); + disconnect( _scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged ); + _scrollBar->setRange( 0, slines - _lines ); + _scrollBar->setSingleStep( 1 ); + _scrollBar->setPageStep( _lines ); + _scrollBar->setValue( cursor ); + connect( _scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged ); } void TerminalDisplay::scrollToEnd() { - disconnect(_scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged); + disconnect( _scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged ); _scrollBar->setValue( _scrollBar->maximum() ); - connect(_scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged); + connect( _scrollBar, &QAbstractSlider::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged ); _screenWindow->scrollTo( _scrollBar->value() + 1 ); _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() ); } -void TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) +void TerminalDisplay::setScrollBarPosition( ScrollBarPosition position ) { - if (_scrollbarLocation == position) - return; + if ( _scrollbarLocation == position ) + return; if ( position == NoScrollBar ) - _scrollBar->hide(); + _scrollBar->hide(); else - _scrollBar->show(); + _scrollBar->show(); _topMargin = _leftMargin = 1; _scrollbarLocation = position; @@ -1739,28 +1754,29 @@ void TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) update(); } -void TerminalDisplay::mousePressEvent(QMouseEvent* ev) +void TerminalDisplay::mousePressEvent( QMouseEvent *ev ) { - if ( _possibleTripleClick && (ev->button()==Qt::LeftButton) ) { - mouseTripleClickEvent(ev); + if ( _possibleTripleClick && ( ev->button() == Qt::LeftButton ) ) + { + mouseTripleClickEvent( ev ); return; } - if ( !contentsRect().contains(ev->pos()) ) return; + if ( !contentsRect().contains( ev->pos() ) ) return; if ( !_screenWindow ) return; int charLine; int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); - QPoint pos = QPoint(charColumn,charLine); + getCharacterPosition( ev->pos(), charLine, charColumn ); + QPoint pos = QPoint( charColumn, charLine ); - if ( ev->button() == Qt::LeftButton) + if ( ev->button() == Qt::LeftButton ) { _lineSelectionMode = false; _wordSelectionMode = false; - emit isBusySelecting(true); // Keep it steady... + emit isBusySelecting( true ); // Keep it steady... // Drag only when the Control key is hold bool selected = false; @@ -1768,23 +1784,25 @@ void TerminalDisplay::mousePressEvent(QMouseEvent* ev) // 'selected' accordingly. //emit testIsSelected(pos.x(), pos.y(), selected); - selected = _screenWindow->isSelected(pos.x(),pos.y()); + selected = _screenWindow->isSelected( pos.x(), pos.y() ); - if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected ) { + if ( ( !_ctrlDrag || ev->modifiers() & Qt::ControlModifier ) && selected ) + { // The user clicked inside selected text dragInfo.state = diPending; dragInfo.start = ev->pos(); } - else { + else + { // No reason to ever start a drag event dragInfo.state = diNone; - _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !(ev->modifiers() & Qt::AltModifier) ); - _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier); + _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !( ev->modifiers() & Qt::AltModifier ) ); + _columnSelectionMode = ( ev->modifiers() & Qt::AltModifier ) && ( ev->modifiers() & Qt::ControlModifier ); - if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) + if ( _mouseMarks || ( ev->modifiers() & Qt::ShiftModifier ) ) { - _screenWindow->clearSelection(); + _screenWindow->clearSelection(); //emit clearSelectionSignal(); pos.ry() += _scrollBar->value(); @@ -1794,162 +1812,166 @@ void TerminalDisplay::mousePressEvent(QMouseEvent* ev) } else { - emit mouseSignal( 0, charColumn + 1, charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); + emit mouseSignal( 0, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 0 ); } - Filter::HotSpot *spot = _filterChain->hotSpotAt(charLine, charColumn); - if (spot && spot->type() == Filter::HotSpot::Link) - spot->activate(QStringLiteral("open-action")); + Filter::HotSpot *spot = _filterChain->hotSpotAt( charLine, charColumn ); + if ( spot && spot->type() == Filter::HotSpot::Link ) + spot->activate( QStringLiteral( "open-action" ) ); } } else if ( ev->button() == Qt::MidButton ) { - if ( _mouseMarks || (!_mouseMarks && (ev->modifiers() & Qt::ShiftModifier)) ) - emitSelection(true,ev->modifiers() & Qt::ControlModifier); + if ( _mouseMarks || ( !_mouseMarks && ( ev->modifiers() & Qt::ShiftModifier ) ) ) + emitSelection( true, ev->modifiers() & Qt::ControlModifier ); else - emit mouseSignal( 1, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); + emit mouseSignal( 1, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 0 ); } else if ( ev->button() == Qt::RightButton ) { - if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) - emit configureRequest(ev->pos()); + if ( _mouseMarks || ( ev->modifiers() & Qt::ShiftModifier ) ) + emit configureRequest( ev->pos() ); else - emit mouseSignal( 2, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); + emit mouseSignal( 2, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 0 ); } } -QList TerminalDisplay::filterActions(const QPoint& position) +QList TerminalDisplay::filterActions( const QPoint &position ) { int charLine, charColumn; - getCharacterPosition(position,charLine,charColumn); + getCharacterPosition( position, charLine, charColumn ); - Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); + Filter::HotSpot *spot = _filterChain->hotSpotAt( charLine, charColumn ); - return spot ? spot->actions() : QList(); + return spot ? spot->actions() : QList(); } -void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev) +void TerminalDisplay::mouseMoveEvent( QMouseEvent *ev ) { int charLine = 0; int charColumn = 0; - int scrollBarWidth = (_scrollbarLocation == ScrollBarLeft) ? _scrollBar->width() : 0; + int scrollBarWidth = ( _scrollbarLocation == ScrollBarLeft ) ? _scrollBar->width() : 0; - getCharacterPosition(ev->pos(),charLine,charColumn); + getCharacterPosition( ev->pos(), charLine, charColumn ); // handle filters // change link hot-spot appearance on mouse-over - Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); - if ( spot && spot->type() == Filter::HotSpot::Link) + Filter::HotSpot *spot = _filterChain->hotSpotAt( charLine, charColumn ); + if ( spot && spot->type() == Filter::HotSpot::Link ) { QRegion previousHotspotArea = _mouseOverHotspotArea; _mouseOverHotspotArea = QRegion(); QRect r; - if (spot->startLine()==spot->endLine()) { - r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, - spot->startLine()*_fontHeight, - spot->endColumn()*_fontWidth + scrollBarWidth, - (spot->endLine()+1)*_fontHeight - 1 ); - _mouseOverHotspotArea |= r; - } else { - r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, - spot->startLine()*_fontHeight, - _columns*_fontWidth - 1 + scrollBarWidth, - (spot->startLine()+1)*_fontHeight ); - _mouseOverHotspotArea |= r; - for ( int line = spot->startLine()+1 ; line < spot->endLine() ; line++ ) { - r.setCoords( 0*_fontWidth + scrollBarWidth, - line*_fontHeight, - _columns*_fontWidth + scrollBarWidth, - (line+1)*_fontHeight ); - _mouseOverHotspotArea |= r; - } - r.setCoords( 0*_fontWidth + scrollBarWidth, - spot->endLine()*_fontHeight, - spot->endColumn()*_fontWidth + scrollBarWidth, - (spot->endLine()+1)*_fontHeight ); + if ( spot->startLine() == spot->endLine() ) + { + r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, + spot->startLine()*_fontHeight, + spot->endColumn()*_fontWidth + scrollBarWidth, + ( spot->endLine() + 1 )*_fontHeight - 1 ); + _mouseOverHotspotArea |= r; + } + else + { + r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, + spot->startLine()*_fontHeight, + _columns * _fontWidth - 1 + scrollBarWidth, + ( spot->startLine() + 1 )*_fontHeight ); + _mouseOverHotspotArea |= r; + for ( int line = spot->startLine() + 1 ; line < spot->endLine() ; line++ ) + { + r.setCoords( 0 * _fontWidth + scrollBarWidth, + line * _fontHeight, + _columns * _fontWidth + scrollBarWidth, + ( line + 1 )*_fontHeight ); _mouseOverHotspotArea |= r; + } + r.setCoords( 0 * _fontWidth + scrollBarWidth, + spot->endLine()*_fontHeight, + spot->endColumn()*_fontWidth + scrollBarWidth, + ( spot->endLine() + 1 )*_fontHeight ); + _mouseOverHotspotArea |= r; } // display tooltips when mousing over links // TODO: Extend this to work with filter types other than links - const QString& tooltip = spot->tooltip(); + const QString &tooltip = spot->tooltip(); if ( !tooltip.isEmpty() ) { - QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea.boundingRect() ); + QToolTip::showText( mapToGlobal( ev->pos() ), tooltip, this, _mouseOverHotspotArea.boundingRect() ); } update( _mouseOverHotspotArea | previousHotspotArea ); } else if ( !_mouseOverHotspotArea.isEmpty() ) { - update( _mouseOverHotspotArea ); - // set hotspot area to an invalid rectangle - _mouseOverHotspotArea = QRegion(); + update( _mouseOverHotspotArea ); + // set hotspot area to an invalid rectangle + _mouseOverHotspotArea = QRegion(); } // for auto-hiding the cursor, we need mouseTracking - if (ev->buttons() == Qt::NoButton ) return; + if ( ev->buttons() == Qt::NoButton ) return; // if the terminal is interested in mouse movements // then emit a mouse movement signal, unless the shift // key is being held down, which overrides this. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + if ( !_mouseMarks && !( ev->modifiers() & Qt::ShiftModifier ) ) { int button = 3; - if (ev->buttons() & Qt::LeftButton) - button = 0; - if (ev->buttons() & Qt::MidButton) - button = 1; - if (ev->buttons() & Qt::RightButton) - button = 2; + if ( ev->buttons() & Qt::LeftButton ) + button = 0; + if ( ev->buttons() & Qt::MidButton ) + button = 1; + if ( ev->buttons() & Qt::RightButton ) + button = 2; - emit mouseSignal( button, - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum(), - 1 ); + emit mouseSignal( button, + charColumn + 1, + charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), + 1 ); return; } - if (dragInfo.state == diPending) + if ( dragInfo.state == diPending ) { // we had a mouse down, but haven't confirmed a drag yet // if the mouse has moved sufficiently, we will confirm // int distance = KGlobalSettings::dndEventDelay(); - int distance = QApplication::startDragDistance(); - if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance || - ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance) - { + int distance = QApplication::startDragDistance(); + if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance || + ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance ) + { // we've left the drag square, we can start a real drag operation now - emit isBusySelecting(false); // OK.. we can breath again. + emit isBusySelecting( false ); // OK.. we can breath again. - _screenWindow->clearSelection(); + _screenWindow->clearSelection(); doDrag(); } return; } - else if (dragInfo.state == diDragging) + else if ( dragInfo.state == diDragging ) { // this isn't technically needed because mouseMoveEvent is suppressed during // Qt drag operations, replaced by dragMoveEvent return; } - if (_actSel == 0) return; + if ( _actSel == 0 ) return; - // don't extend selection while pasting - if (ev->buttons() & Qt::MidButton) return; +// don't extend selection while pasting + if ( ev->buttons() & Qt::MidButton ) return; extendSelection( ev->pos() ); } -void TerminalDisplay::extendSelection( const QPoint& position ) +void TerminalDisplay::extendSelection( const QPoint &position ) { QPoint pos = position; if ( !_screenWindow ) - return; + return; //if ( !contentsRect().contains(ev->pos()) ) return; QPoint tL = contentsRect().topLeft(); @@ -1963,33 +1985,33 @@ void TerminalDisplay::extendSelection( const QPoint& position ) int linesBeyondWidget = 0; - QRect textBounds(tLx + _leftMargin, - tLy + _topMargin, - _usedColumns*_fontWidth-1, - _usedLines*_fontHeight-1); + QRect textBounds( tLx + _leftMargin, + tLy + _topMargin, + _usedColumns * _fontWidth - 1, + _usedLines * _fontHeight - 1 ); // Adjust position within text area bounds. QPoint oldpos = pos; - pos.setX( qBound(textBounds.left(),pos.x(),textBounds.right()) ); - pos.setY( qBound(textBounds.top(),pos.y(),textBounds.bottom()) ); + pos.setX( qBound( textBounds.left(), pos.x(), textBounds.right() ) ); + pos.setY( qBound( textBounds.top(), pos.y(), textBounds.bottom() ) ); if ( oldpos.y() > textBounds.bottom() ) { - linesBeyondWidget = (oldpos.y()-textBounds.bottom()) / _fontHeight; - _scrollBar->setValue(_scrollBar->value()+linesBeyondWidget+1); // scrollforward + linesBeyondWidget = ( oldpos.y() - textBounds.bottom() ) / _fontHeight; + _scrollBar->setValue( _scrollBar->value() + linesBeyondWidget + 1 ); // scrollforward } if ( oldpos.y() < textBounds.top() ) { - linesBeyondWidget = (textBounds.top()-oldpos.y()) / _fontHeight; - _scrollBar->setValue(_scrollBar->value()-linesBeyondWidget-1); // history + linesBeyondWidget = ( textBounds.top() - oldpos.y() ) / _fontHeight; + _scrollBar->setValue( _scrollBar->value() - linesBeyondWidget - 1 ); // history } int charColumn = 0; int charLine = 0; - getCharacterPosition(pos,charLine,charColumn); + getCharacterPosition( pos, charLine, charColumn ); - QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight); + QPoint here = QPoint( charColumn, charLine ); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight); QPoint ohere; QPoint _iPntSelCorr = _iPntSel; _iPntSelCorr.ry() -= _scrollBar->value(); @@ -2004,29 +2026,31 @@ void TerminalDisplay::extendSelection( const QPoint& position ) QChar selClass; bool left_not_right = ( here.y() < _iPntSelCorr.y() || - ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); + ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || - ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); + ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); swapping = left_not_right != old_left_not_right; // Find left (left_not_right ? from here : from start) QPoint left = left_not_right ? here : _iPntSelCorr; - i = loc(left.x(),left.y()); - if (i>=0 && i<=_imageSize) { - selClass = charClass(_image[i].character); - while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) )) - && charClass(_image[i-1].character) == selClass ) - { i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} } + i = loc( left.x(), left.y() ); + if ( i >= 0 && i <= _imageSize ) + { + selClass = charClass( _image[i].character ); + while ( ( ( left.x() > 0 ) || ( left.y() > 0 && ( _lineProperties[left.y() - 1] & LINE_WRAPPED ) ) ) + && charClass( _image[i - 1].character ) == selClass ) + { i--; if ( left.x() > 0 ) left.rx()--; else {left.rx() = _usedColumns - 1; left.ry()--;} } } // Find left (left_not_right ? from start : from here) QPoint right = left_not_right ? _iPntSelCorr : here; - i = loc(right.x(),right.y()); - if (i>=0 && i<=_imageSize) { - selClass = charClass(_image[i].character); - while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) )) - && charClass(_image[i+1].character) == selClass ) - { i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } } + i = loc( right.x(), right.y() ); + if ( i >= 0 && i <= _imageSize ) + { + selClass = charClass( _image[i].character ); + while ( ( ( right.x() < _usedColumns - 1 ) || ( right.y() < _usedLines - 1 && ( _lineProperties[right.y()] & LINE_WRAPPED ) ) ) + && charClass( _image[i + 1].character ) == selClass ) + { i++; if ( right.x() < _usedColumns - 1 ) right.rx()++; else {right.rx() = 0; right.ry()++; } } } // Pick which is start (ohere) and which is extension (here) @@ -2049,13 +2073,13 @@ void TerminalDisplay::extendSelection( const QPoint& position ) QPoint above = above_not_below ? here : _iPntSelCorr; QPoint below = above_not_below ? _iPntSelCorr : here; - while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) ) + while ( above.y() > 0 && ( _lineProperties[above.y() - 1] & LINE_WRAPPED ) ) above.ry()--; - while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) ) + while ( below.y() < _usedLines - 1 && ( _lineProperties[below.y()] & LINE_WRAPPED ) ) below.ry()++; - above.setX(0); - below.setX(_usedColumns-1); + above.setX( 0 ); + below.setX( _usedColumns - 1 ); // Pick which is start (ohere) and which is extension (here) if ( above_not_below ) @@ -2068,7 +2092,7 @@ void TerminalDisplay::extendSelection( const QPoint& position ) } QPoint newSelBegin = QPoint( ohere.x(), ohere.y() ); - swapping = !(_tripleSelBegin==newSelBegin); + swapping = !( _tripleSelBegin == newSelBegin ); _tripleSelBegin = newSelBegin; ohere.rx()++; @@ -2081,9 +2105,9 @@ void TerminalDisplay::extendSelection( const QPoint& position ) QChar selClass; bool left_not_right = ( here.y() < _iPntSelCorr.y() || - ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); + ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || - ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); + ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); swapping = left_not_right != old_left_not_right; // Find left (left_not_right ? from here : from start) @@ -2093,19 +2117,20 @@ void TerminalDisplay::extendSelection( const QPoint& position ) QPoint right = left_not_right ? _iPntSelCorr : here; if ( right.x() > 0 && !_columnSelectionMode ) { - i = loc(right.x(),right.y()); - if (i>=0 && i<=_imageSize) { - selClass = charClass(_image[i-1].character); - /* if (selClass == ' ') - { - while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) && - !(_lineProperties[right.y()] & LINE_WRAPPED)) - { i++; right.rx()++; } - if (right.x() < _usedColumns-1) - right = left_not_right ? _iPntSelCorr : here; - else - right.rx()++; // will be balanced later because of offset=-1; - }*/ + i = loc( right.x(), right.y() ); + if ( i >= 0 && i <= _imageSize ) + { + selClass = charClass( _image[i - 1].character ); + /* if (selClass == ' ') + { + while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) && + !(_lineProperties[right.y()] & LINE_WRAPPED)) + { i++; right.rx()++; } + if (right.x() < _usedColumns-1) + right = left_not_right ? _iPntSelCorr : here; + else + right.rx()++; // will be balanced later because of offset=-1; + }*/ } } @@ -2120,19 +2145,19 @@ void TerminalDisplay::extendSelection( const QPoint& position ) } } - if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved + if ( ( here == _pntSelCorr ) && ( scroll == _scrollBar->value() ) ) return; // not moved - if (here == ohere) return; // It's not left, it's not right. + if ( here == ohere ) return; // It's not left, it's not right. if ( _actSel < 2 || swapping ) { if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) { - _screenWindow->setSelectionStart( ohere.x() , ohere.y() , true ); + _screenWindow->setSelectionStart( ohere.x(), ohere.y(), true ); } else { - _screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false ); + _screenWindow->setSelectionStart( ohere.x() - 1 - offset, ohere.y(), false ); } } @@ -2143,38 +2168,38 @@ void TerminalDisplay::extendSelection( const QPoint& position ) if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) { - _screenWindow->setSelectionEnd( here.x() , here.y() ); + _screenWindow->setSelectionEnd( here.x(), here.y() ); } else { - _screenWindow->setSelectionEnd( here.x()+offset , here.y() ); + _screenWindow->setSelectionEnd( here.x() + offset, here.y() ); } } -void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev) +void TerminalDisplay::mouseReleaseEvent( QMouseEvent *ev ) { - if ( !_screenWindow ) - return; + if ( !_screenWindow ) + return; - int charLine; - int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); + int charLine; + int charColumn; + getCharacterPosition( ev->pos(), charLine, charColumn ); - if ( ev->button() == Qt::LeftButton) + if ( ev->button() == Qt::LeftButton ) { - emit isBusySelecting(false); - if(dragInfo.state == diPending) + emit isBusySelecting( false ); + if ( dragInfo.state == diPending ) { // We had a drag event pending but never confirmed. Kill selection - _screenWindow->clearSelection(); + _screenWindow->clearSelection(); //emit clearSelectionSignal(); } else { if ( _actSel > 1 ) { - setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); + setSelection( _screenWindow->selectedText( _preserveLineBreaks ) ); } _actSel = 0; @@ -2183,94 +2208,94 @@ void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev) // outside the range. The procedure used in `mouseMoveEvent' // applies here, too. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + if ( !_mouseMarks && !( ev->modifiers() & Qt::ShiftModifier ) ) emit mouseSignal( 3, // release - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); + charColumn + 1, + charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 0 ); } dragInfo.state = diNone; } if ( !_mouseMarks && - ((ev->button() == Qt::RightButton && !(ev->modifiers() & Qt::ShiftModifier)) - || ev->button() == Qt::MidButton) ) + ( ( ev->button() == Qt::RightButton && !( ev->modifiers() & Qt::ShiftModifier ) ) + || ev->button() == Qt::MidButton ) ) { emit mouseSignal( 3, charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , - 0); + charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), + 0 ); } } -void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const +void TerminalDisplay::getCharacterPosition( const QPoint &widgetPoint, int &line, int &column ) const { - line = (widgetPoint.y()-contentsRect().top()-_topMargin) / _fontHeight; + line = ( widgetPoint.y() - contentsRect().top() - _topMargin ) / _fontHeight; - if ( _fixedFont ) - column = (widgetPoint.x() + _fontWidth/2 -contentsRect().left()-_leftMargin) / _fontWidth; - else - { - int x = contentsRect().left() + widgetPoint.x() - _fontWidth/2; - column = 0; + if ( _fixedFont ) + column = ( widgetPoint.x() + _fontWidth / 2 - contentsRect().left() - _leftMargin ) / _fontWidth; + else + { + int x = contentsRect().left() + widgetPoint.x() - _fontWidth / 2; + column = 0; - while(x > textWidth(0, column, line)) - column++; - } + while ( x > textWidth( 0, column, line ) ) + column++; + } - if ( line < 0 ) - line = 0; - if ( column < 0 ) - column = 0; + if ( line < 0 ) + line = 0; + if ( column < 0 ) + column = 0; - if ( line >= _usedLines ) - line = _usedLines-1; + if ( line >= _usedLines ) + line = _usedLines - 1; - // the column value returned can be equal to _usedColumns, which - // is the position just after the last character displayed in a line. - // - // this is required so that the user can select characters in the right-most - // column (or left-most for right-to-left input) - if ( column > _usedColumns ) - column = _usedColumns; + // the column value returned can be equal to _usedColumns, which + // is the position just after the last character displayed in a line. + // + // this is required so that the user can select characters in the right-most + // column (or left-most for right-to-left input) + if ( column > _usedColumns ) + column = _usedColumns; } void TerminalDisplay::updateFilters() { - if ( !_screenWindow ) - return; + if ( !_screenWindow ) + return; - processFilters(); + processFilters(); } void TerminalDisplay::updateLineProperties() { - if ( !_screenWindow ) - return; + if ( !_screenWindow ) + return; - _lineProperties = _screenWindow->getLineProperties(); + _lineProperties = _screenWindow->getLineProperties(); } -void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev) +void TerminalDisplay::mouseDoubleClickEvent( QMouseEvent *ev ) { - if ( ev->button() != Qt::LeftButton) return; + if ( ev->button() != Qt::LeftButton ) return; if ( !_screenWindow ) return; int charLine = 0; int charColumn = 0; - getCharacterPosition(ev->pos(),charLine,charColumn); + getCharacterPosition( ev->pos(), charLine, charColumn ); - QPoint pos(charColumn,charLine); + QPoint pos( charColumn, charLine ); // pass on double-click as two clicks. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + if ( !_mouseMarks && !( ev->modifiers() & Qt::ShiftModifier ) ) { // Send just _ONE_ click event, since the first click of the double-click // was already sent by the click handler emit mouseSignal( 0, - pos.x()+1, - pos.y()+1 +_scrollBar->value() -_scrollBar->maximum(), + pos.x() + 1, + pos.y() + 1 + _scrollBar->value() - _scrollBar->maximum(), 0 ); // left button return; } @@ -2278,72 +2303,72 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev) _screenWindow->clearSelection(); QPoint bgnSel = pos; QPoint endSel = pos; - int i = loc(bgnSel.x(),bgnSel.y()); + int i = loc( bgnSel.x(), bgnSel.y() ); _iPntSel = bgnSel; _iPntSel.ry() += _scrollBar->value(); _wordSelectionMode = true; // find word boundaries... - QChar selClass = charClass(_image[i].character); + QChar selClass = charClass( _image[i].character ); { - // find the start of the word - int x = bgnSel.x(); - while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) )) - && charClass(_image[i-1].character) == selClass ) - { - i--; - if (x>0) - x--; - else - { - x=_usedColumns-1; - bgnSel.ry()--; - } - } + // find the start of the word + int x = bgnSel.x(); + while ( ( ( x > 0 ) || ( bgnSel.y() > 0 && ( _lineProperties[bgnSel.y() - 1] & LINE_WRAPPED ) ) ) + && charClass( _image[i - 1].character ) == selClass ) + { + i--; + if ( x > 0 ) + x--; + else + { + x = _usedColumns - 1; + bgnSel.ry()--; + } + } - bgnSel.setX(x); - _screenWindow->setSelectionStart( bgnSel.x() , bgnSel.y() , false ); + bgnSel.setX( x ); + _screenWindow->setSelectionStart( bgnSel.x(), bgnSel.y(), false ); - // find the end of the word - i = loc( endSel.x(), endSel.y() ); - x = endSel.x(); - while( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) )) - && charClass(_image[i+1].character) == selClass ) - { - i++; - if (x<_usedColumns-1) - x++; - else - { - x=0; - endSel.ry()++; - } - } + // find the end of the word + i = loc( endSel.x(), endSel.y() ); + x = endSel.x(); + while ( ( ( x < _usedColumns - 1 ) || ( endSel.y() < _usedLines - 1 && ( _lineProperties[endSel.y()] & LINE_WRAPPED ) ) ) + && charClass( _image[i + 1].character ) == selClass ) + { + i++; + if ( x < _usedColumns - 1 ) + x++; + else + { + x = 0; + endSel.ry()++; + } + } - endSel.setX(x); + endSel.setX( x ); - // In word selection mode don't select @ (64) if at end of word. - if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) ) - endSel.setX( x - 1 ); + // In word selection mode don't select @ (64) if at end of word. + if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) ) + endSel.setX( x - 1 ); - _actSel = 2; // within selection + _actSel = 2; // within selection - _screenWindow->setSelectionEnd( endSel.x() , endSel.y() ); + _screenWindow->setSelectionEnd( endSel.x(), endSel.y() ); - setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); - } + setSelection( _screenWindow->selectedText( _preserveLineBreaks ) ); + } - _possibleTripleClick=true; + _possibleTripleClick = true; - QTimer::singleShot(QApplication::doubleClickInterval(),this, - SLOT(tripleClickTimeout())); + QTimer::singleShot( QApplication::doubleClickInterval(), this, + SLOT( tripleClickTimeout() ) ); } -void TerminalDisplay::wheelEvent( QWheelEvent* ev ) +void TerminalDisplay::wheelEvent( QWheelEvent *ev ) { - if (ev->orientation() != Qt::Vertical) + if ( ev->orientation() != Qt::Vertical ) return; // if the terminal program is not interested mouse events @@ -2353,26 +2378,26 @@ void TerminalDisplay::wheelEvent( QWheelEvent* ev ) if ( _mouseMarks ) { bool canScroll = _scrollBar->maximum() > 0; - if (canScroll) - _scrollBar->event(ev); + if ( canScroll ) + _scrollBar->event( ev ); else { - // assume that each Up / Down key event will cause the terminal application - // to scroll by one line. - // - // to get a reasonable scrolling speed, scroll by one line for every 5 degrees - // of mouse wheel rotation. Mouse wheels typically move in steps of 15 degrees, - // giving a scroll of 3 lines - int key = ev->delta() > 0 ? Qt::Key_Up : Qt::Key_Down; + // assume that each Up / Down key event will cause the terminal application + // to scroll by one line. + // + // to get a reasonable scrolling speed, scroll by one line for every 5 degrees + // of mouse wheel rotation. Mouse wheels typically move in steps of 15 degrees, + // giving a scroll of 3 lines + int key = ev->delta() > 0 ? Qt::Key_Up : Qt::Key_Down; - // QWheelEvent::delta() gives rotation in eighths of a degree - int wheelDegrees = ev->delta() / 8; - int linesToScroll = abs(wheelDegrees) / 5; + // QWheelEvent::delta() gives rotation in eighths of a degree + int wheelDegrees = ev->delta() / 8; + int linesToScroll = abs( wheelDegrees ) / 5; - QKeyEvent keyScrollEvent(QEvent::KeyPress,key,Qt::NoModifier); + QKeyEvent keyScrollEvent( QEvent::KeyPress, key, Qt::NoModifier ); - for (int i=0;ipos() , charLine , charColumn ); + getCharacterPosition( ev->pos(), charLine, charColumn ); emit mouseSignal( ev->delta() > 0 ? 4 : 5, charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , - 0); + charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), + 0 ); } } void TerminalDisplay::tripleClickTimeout() { - _possibleTripleClick=false; + _possibleTripleClick = false; } -void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) +void TerminalDisplay::mouseTripleClickEvent( QMouseEvent *ev ) { if ( !_screenWindow ) return; int charLine; int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); - _iPntSel = QPoint(charColumn,charLine); + getCharacterPosition( ev->pos(), charLine, charColumn ); + _iPntSel = QPoint( charColumn, charLine ); _screenWindow->clearSelection(); @@ -2410,46 +2435,48 @@ void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) _wordSelectionMode = false; _actSel = 2; // within selection - emit isBusySelecting(true); // Keep it steady... + emit isBusySelecting( true ); // Keep it steady... - while (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) + while ( _iPntSel.y() > 0 && ( _lineProperties[_iPntSel.y() - 1] & LINE_WRAPPED ) ) _iPntSel.ry()--; - if (_tripleClickMode == SelectForwardsFromCursor) { + if ( _tripleClickMode == SelectForwardsFromCursor ) + { // find word boundary start - int i = loc(_iPntSel.x(),_iPntSel.y()); - QChar selClass = charClass(_image[i].character); + int i = loc( _iPntSel.x(), _iPntSel.y() ); + QChar selClass = charClass( _image[i].character ); int x = _iPntSel.x(); - while ( ((x>0) || - (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) + while ( ( ( x > 0 ) || + ( _iPntSel.y() > 0 && ( _lineProperties[_iPntSel.y() - 1] & LINE_WRAPPED ) ) ) - && charClass(_image[i-1].character) == selClass ) + && charClass( _image[i - 1].character ) == selClass ) { - i--; - if (x>0) - x--; - else - { - x=_columns-1; - _iPntSel.ry()--; - } + i--; + if ( x > 0 ) + x--; + else + { + x = _columns - 1; + _iPntSel.ry()--; + } } - _screenWindow->setSelectionStart( x , _iPntSel.y() , false ); + _screenWindow->setSelectionStart( x, _iPntSel.y(), false ); _tripleSelBegin = QPoint( x, _iPntSel.y() ); } - else if (_tripleClickMode == SelectWholeLine) { - _screenWindow->setSelectionStart( 0 , _iPntSel.y() , false ); + else if ( _tripleClickMode == SelectWholeLine ) + { + _screenWindow->setSelectionStart( 0, _iPntSel.y(), false ); _tripleSelBegin = QPoint( 0, _iPntSel.y() ); } - while (_iPntSel.y()<_lines-1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED) ) + while ( _iPntSel.y() < _lines - 1 && ( _lineProperties[_iPntSel.y()] & LINE_WRAPPED ) ) _iPntSel.ry()++; - _screenWindow->setSelectionEnd( _columns - 1 , _iPntSel.y() ); + _screenWindow->setSelectionEnd( _columns - 1, _iPntSel.y() ); - setSelection(_screenWindow->selectedText(_preserveLineBreaks)); + setSelection( _screenWindow->selectedText( _preserveLineBreaks ) ); _iPntSel.ry() += _scrollBar->value(); } @@ -2457,39 +2484,40 @@ void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) bool TerminalDisplay::focusNextPrevChild( bool next ) { - if (next) + if ( next ) return false; // This disables changing the active part in konqueror - // when pressing Tab + // when pressing Tab return QWidget::focusNextPrevChild( next ); } -QChar TerminalDisplay::charClass(QChar qch) const +QChar TerminalDisplay::charClass( QChar qch ) const { - if ( qch.isSpace() ) return ' '; + if ( qch.isSpace() ) return ' '; - if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) ) + if ( qch.isLetterOrNumber() || _wordCharacters.contains( qch, Qt::CaseInsensitive ) ) return 'a'; - return qch; + return qch; } -void TerminalDisplay::setWordCharacters(const QString& wc) +void TerminalDisplay::setWordCharacters( const QString &wc ) { - _wordCharacters = wc; + _wordCharacters = wc; } -void TerminalDisplay::setUsesMouse(bool on) +void TerminalDisplay::setUsesMouse( bool on ) { - if (_mouseMarks != on) { - _mouseMarks = on; - setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor ); - emit usesMouseChanged(); - } + if ( _mouseMarks != on ) + { + _mouseMarks = on; + setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor ); + emit usesMouseChanged(); + } } bool TerminalDisplay::usesMouse() const { - return _mouseMarks; + return _mouseMarks; } /* ------------------------------------------------------------------------- */ @@ -2500,49 +2528,49 @@ bool TerminalDisplay::usesMouse() const #undef KeyPress -void TerminalDisplay::emitSelection(bool useXselection,bool appendReturn) +void TerminalDisplay::emitSelection( bool useXselection, bool appendReturn ) { if ( !_screenWindow ) - return; + return; // Paste Clipboard by simulating keypress events - QString text = QApplication::clipboard()->text(useXselection ? QClipboard::Selection : - QClipboard::Clipboard); - if(appendReturn) - text.append("\r"); + QString text = QApplication::clipboard()->text( useXselection ? QClipboard::Selection : + QClipboard::Clipboard ); + if ( appendReturn ) + text.append( "\r" ); if ( ! text.isEmpty() ) { - text.replace('\n', '\r'); - QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); - emit keyPressedSignal(&e); // expose as a big fat keypress event + text.replace( '\n', '\r' ); + QKeyEvent e( QEvent::KeyPress, 0, Qt::NoModifier, text ); + emit keyPressedSignal( &e ); // expose as a big fat keypress event _screenWindow->clearSelection(); } } -void TerminalDisplay::setSelection(const QString& t) +void TerminalDisplay::setSelection( const QString &t ) { - QApplication::clipboard()->setText(t, QClipboard::Selection); + QApplication::clipboard()->setText( t, QClipboard::Selection ); } void TerminalDisplay::copyClipboard() { if ( !_screenWindow ) - return; + return; - QString text = _screenWindow->selectedText(_preserveLineBreaks); - if (!text.isEmpty()) - QApplication::clipboard()->setText(text); + QString text = _screenWindow->selectedText( _preserveLineBreaks ); + if ( !text.isEmpty() ) + QApplication::clipboard()->setText( text ); } void TerminalDisplay::pasteClipboard() { - emitSelection(false,false); + emitSelection( false, false ); } void TerminalDisplay::pasteSelection() { - emitSelection(true,false); + emitSelection( true, false ); } /* ------------------------------------------------------------------------- */ @@ -2553,241 +2581,241 @@ void TerminalDisplay::pasteSelection() void TerminalDisplay::setFlowControlWarningEnabled( bool enable ) { - _flowControlWarningEnabled = enable; + _flowControlWarningEnabled = enable; - // if the dialog is currently visible and the flow control warning has - // been disabled then hide the dialog - if (!enable) - outputSuspended(false); + // if the dialog is currently visible and the flow control warning has + // been disabled then hide the dialog + if ( !enable ) + outputSuspended( false ); } -void TerminalDisplay::setMotionAfterPasting(MotionAfterPasting action) +void TerminalDisplay::setMotionAfterPasting( MotionAfterPasting action ) { - mMotionAfterPasting = action; + mMotionAfterPasting = action; } int TerminalDisplay::motionAfterPasting() { - return mMotionAfterPasting; + return mMotionAfterPasting; } -void TerminalDisplay::keyPressEvent( QKeyEvent* event ) +void TerminalDisplay::keyPressEvent( QKeyEvent *event ) { - bool emitKeyPressSignal = true; + bool emitKeyPressSignal = true; - // Keyboard-based navigation - if ( event->modifiers() == Qt::ShiftModifier ) + // Keyboard-based navigation + if ( event->modifiers() == Qt::ShiftModifier ) + { + bool update = true; + + if ( event->key() == Qt::Key_PageUp ) { - bool update = true; - - if ( event->key() == Qt::Key_PageUp ) - { - _screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 ); - } - else if ( event->key() == Qt::Key_PageDown ) - { - _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 ); - } - else if ( event->key() == Qt::Key_Up ) - { - _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 ); - } - else if ( event->key() == Qt::Key_Down ) - { - _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 ); - } - else if ( event->key() == Qt::Key_End) - { - scrollToEnd(); - } - else if ( event->key() == Qt::Key_Home) - { - _screenWindow->scrollTo(0); - } - else - update = false; - - if ( update ) - { - _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() ); - - updateLineProperties(); - updateImage(); - - // do not send key press to terminal - emitKeyPressSignal = false; - } + _screenWindow->scrollBy( ScreenWindow::ScrollPages, -1 ); } - - _actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't - // know where the current selection is. - - if (_hasBlinkingCursor) + else if ( event->key() == Qt::Key_PageDown ) { - _blinkCursorTimer->start(QApplication::cursorFlashTime() / 2); - if (_cursorBlinking) - blinkCursorEvent(); - else - _cursorBlinking = false; + _screenWindow->scrollBy( ScreenWindow::ScrollPages, 1 ); } - - if ( emitKeyPressSignal ) + else if ( event->key() == Qt::Key_Up ) { - emit keyPressedSignal(event); - - if(event->modifiers().testFlag(Qt::ShiftModifier) - || event->modifiers().testFlag(Qt::ControlModifier) - || event->modifiers().testFlag(Qt::AltModifier)) - { - switch(mMotionAfterPasting) - { - case MoveStartScreenWindow: - _screenWindow->scrollTo(0); - break; - case MoveEndScreenWindow: - scrollToEnd(); - break; - case NoMoveScreenWindow: - break; - } - } - else - { - scrollToEnd(); - } + _screenWindow->scrollBy( ScreenWindow::ScrollLines, -1 ); } + else if ( event->key() == Qt::Key_Down ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollLines, 1 ); + } + else if ( event->key() == Qt::Key_End ) + { + scrollToEnd(); + } + else if ( event->key() == Qt::Key_Home ) + { + _screenWindow->scrollTo( 0 ); + } + else + update = false; - event->accept(); + if ( update ) + { + _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() ); + + updateLineProperties(); + updateImage(); + + // do not send key press to terminal + emitKeyPressSignal = false; + } + } + + _actSel = 0; // Key stroke implies a screen update, so TerminalDisplay won't + // know where the current selection is. + + if ( _hasBlinkingCursor ) + { + _blinkCursorTimer->start( QApplication::cursorFlashTime() / 2 ); + if ( _cursorBlinking ) + blinkCursorEvent(); + else + _cursorBlinking = false; + } + + if ( emitKeyPressSignal ) + { + emit keyPressedSignal( event ); + + if ( event->modifiers().testFlag( Qt::ShiftModifier ) + || event->modifiers().testFlag( Qt::ControlModifier ) + || event->modifiers().testFlag( Qt::AltModifier ) ) + { + switch ( mMotionAfterPasting ) + { + case MoveStartScreenWindow: + _screenWindow->scrollTo( 0 ); + break; + case MoveEndScreenWindow: + scrollToEnd(); + break; + case NoMoveScreenWindow: + break; + } + } + else + { + scrollToEnd(); + } + } + + event->accept(); } -void TerminalDisplay::inputMethodEvent( QInputMethodEvent* event ) +void TerminalDisplay::inputMethodEvent( QInputMethodEvent *event ) { - QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString()); - emit keyPressedSignal(&keyEvent); + QKeyEvent keyEvent( QEvent::KeyPress, 0, Qt::NoModifier, event->commitString() ); + emit keyPressedSignal( &keyEvent ); - _inputMethodData.preeditString = event->preeditString(); - update(preeditRect() | _inputMethodData.previousPreeditRect); + _inputMethodData.preeditString = event->preeditString(); + update( preeditRect() | _inputMethodData.previousPreeditRect ); - event->accept(); + event->accept(); } QVariant TerminalDisplay::inputMethodQuery( Qt::InputMethodQuery query ) const { - const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0); - switch ( query ) + const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint( 0, 0 ); + switch ( query ) + { + case Qt::ImMicroFocus: + return imageToWidget( QRect( cursorPos.x(), cursorPos.y(), 1, 1 ) ); + break; + case Qt::ImFont: + return font(); + break; + case Qt::ImCursorPosition: + // return the cursor position within the current line + return cursorPos.x(); + break; + case Qt::ImSurroundingText: { - case Qt::ImMicroFocus: - return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1)); - break; - case Qt::ImFont: - return font(); - break; - case Qt::ImCursorPosition: - // return the cursor position within the current line - return cursorPos.x(); - break; - case Qt::ImSurroundingText: - { - // return the text from the current line - QString lineText; - QTextStream stream(&lineText); - PlainTextDecoder decoder; - decoder.begin(&stream); - decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]); - decoder.end(); - return lineText; - } - break; - case Qt::ImCurrentSelection: - return QString(); - break; - default: - break; + // return the text from the current line + QString lineText; + QTextStream stream( &lineText ); + PlainTextDecoder decoder; + decoder.begin( &stream ); + decoder.decodeLine( &_image[loc( 0, cursorPos.y() )], _usedColumns, _lineProperties[cursorPos.y()] ); + decoder.end(); + return lineText; } + break; + case Qt::ImCurrentSelection: + return QString(); + break; + default: + break; + } - return QVariant(); + return QVariant(); } -bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent) +bool TerminalDisplay::handleShortcutOverrideEvent( QKeyEvent *keyEvent ) { - int modifiers = keyEvent->modifiers(); + int modifiers = keyEvent->modifiers(); - // When a possible shortcut combination is pressed, - // emit the overrideShortcutCheck() signal to allow the host - // to decide whether the terminal should override it or not. - if (modifiers != Qt::NoModifier) + // When a possible shortcut combination is pressed, + // emit the overrideShortcutCheck() signal to allow the host + // to decide whether the terminal should override it or not. + if ( modifiers != Qt::NoModifier ) + { + int modifierCount = 0; + unsigned int currentModifier = Qt::ShiftModifier; + + while ( currentModifier <= Qt::KeypadModifier ) { - int modifierCount = 0; - unsigned int currentModifier = Qt::ShiftModifier; - - while (currentModifier <= Qt::KeypadModifier) - { - if (modifiers & currentModifier) - modifierCount++; - currentModifier <<= 1; - } - if (modifierCount < 2) - { - bool over_ride = false; - emit overrideShortcutCheck(keyEvent,over_ride); - if (over_ride) - { - keyEvent->accept(); - return true; - } - } + if ( modifiers & currentModifier ) + modifierCount++; + currentModifier <<= 1; } - - // Override any of the following shortcuts because - // they are needed by the terminal - int keyCode = keyEvent->key() | modifiers; - switch ( keyCode ) + if ( modifierCount < 2 ) { - // list is taken from the QLineEdit::event() code - case Qt::Key_Tab: - case Qt::Key_Delete: - case Qt::Key_Home: - case Qt::Key_End: - case Qt::Key_Backspace: - case Qt::Key_Left: - case Qt::Key_Right: - case Qt::Key_Escape: + bool over_ride = false; + emit overrideShortcutCheck( keyEvent, over_ride ); + if ( over_ride ) + { keyEvent->accept(); return true; + } } - return false; + } + + // Override any of the following shortcuts because + // they are needed by the terminal + int keyCode = keyEvent->key() | modifiers; + switch ( keyCode ) + { + // list is taken from the QLineEdit::event() code + case Qt::Key_Tab: + case Qt::Key_Delete: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Backspace: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Escape: + keyEvent->accept(); + return true; + } + return false; } -bool TerminalDisplay::event(QEvent* event) +bool TerminalDisplay::event( QEvent *event ) { bool eventHandled = false; - switch (event->type()) + switch ( event->type() ) { case QEvent::ShortcutOverride: - eventHandled = handleShortcutOverrideEvent((QKeyEvent*)event); - break; + eventHandled = handleShortcutOverrideEvent( ( QKeyEvent * )event ); + break; case QEvent::PaletteChange: case QEvent::ApplicationPaletteChange: - _scrollBar->setPalette( QApplication::palette() ); - break; + _scrollBar->setPalette( QApplication::palette() ); + break; default: - break; + break; } - return eventHandled ? true : QWidget::event(event); + return eventHandled ? true : QWidget::event( event ); } -void TerminalDisplay::setBellMode(int mode) +void TerminalDisplay::setBellMode( int mode ) { - _bellMode=mode; + _bellMode = mode; } void TerminalDisplay::enableBell() { - _allowBell = true; + _allowBell = true; } -void TerminalDisplay::bell(const QString& message) +void TerminalDisplay::bell( const QString &message ) { - if (_bellMode==NoBell) return; + if ( _bellMode == NoBell ) return; //limit the rate at which bells can occur //...mainly for sound effects where rapid bells in sequence @@ -2795,34 +2823,34 @@ void TerminalDisplay::bell(const QString& message) if ( _allowBell ) { _allowBell = false; - QTimer::singleShot(500,this,SLOT(enableBell())); + QTimer::singleShot( 500, this, SLOT( enableBell() ) ); - if (_bellMode==SystemBeepBell) + if ( _bellMode == SystemBeepBell ) { - QApplication::beep(); + QApplication::beep(); } - else if (_bellMode==NotifyBell) + else if ( _bellMode == NotifyBell ) { - emit notifyBell(message); + emit notifyBell( message ); } - else if (_bellMode==VisualBell) + else if ( _bellMode == VisualBell ) { - swapColorTable(); - QTimer::singleShot(200,this,SLOT(swapColorTable())); + swapColorTable(); + QTimer::singleShot( 200, this, SLOT( swapColorTable() ) ); } } } void TerminalDisplay::selectionChanged() { - emit copyAvailable(_screenWindow->selectedText(false).isEmpty() == false); + emit copyAvailable( _screenWindow->selectedText( false ).isEmpty() == false ); } void TerminalDisplay::swapColorTable() { ColorEntry color = _colorTable[1]; - _colorTable[1]=_colorTable[0]; - _colorTable[0]= color; + _colorTable[1] = _colorTable[0]; + _colorTable[0] = color; _colorsInverted = !_colorsInverted; update(); } @@ -2830,50 +2858,50 @@ void TerminalDisplay::swapColorTable() void TerminalDisplay::clearImage() { // We initialize _image[_imageSize] too. See makeImage() - for (int i = 0; i <= _imageSize; i++) + for ( int i = 0; i <= _imageSize; i++ ) { _image[i].character = ' '; - _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT, - DEFAULT_FORE_COLOR); - _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT, - DEFAULT_BACK_COLOR); + _image[i].foregroundColor = CharacterColor( COLOR_SPACE_DEFAULT, + DEFAULT_FORE_COLOR ); + _image[i].backgroundColor = CharacterColor( COLOR_SPACE_DEFAULT, + DEFAULT_BACK_COLOR ); _image[i].rendition = DEFAULT_RENDITION; } } void TerminalDisplay::calcGeometry() { - _scrollBar->resize(_scrollBar->sizeHint().width(), contentsRect().height()); - switch(_scrollbarLocation) + _scrollBar->resize( _scrollBar->sizeHint().width(), contentsRect().height() ); + switch ( _scrollbarLocation ) { case NoScrollBar : - _leftMargin = DEFAULT_LEFT_MARGIN; - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN; - break; + _leftMargin = DEFAULT_LEFT_MARGIN; + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN; + break; case ScrollBarLeft : - _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width(); - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); - _scrollBar->move(contentsRect().topLeft()); - break; + _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width(); + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); + _scrollBar->move( contentsRect().topLeft() ); + break; case ScrollBarRight: - _leftMargin = DEFAULT_LEFT_MARGIN; - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); - _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width()-1,0)); - break; + _leftMargin = DEFAULT_LEFT_MARGIN; + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); + _scrollBar->move( contentsRect().topRight() - QPoint( _scrollBar->width() - 1, 0 ) ); + break; } _topMargin = DEFAULT_TOP_MARGIN; _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1; - if (!_isFixedSize) + if ( !_isFixedSize ) { - // ensure that display is always at least one column wide - _columns = qMax(1,_contentWidth / _fontWidth); - _usedColumns = qMin(_usedColumns,_columns); + // ensure that display is always at least one column wide + _columns = qMax( 1, _contentWidth / _fontWidth ); + _usedColumns = qMin( _usedColumns, _columns ); - // ensure that display is always at least one line high - _lines = qMax(1,_contentHeight / _fontHeight); - _usedLines = qMin(_usedLines,_lines); + // ensure that display is always at least one line high + _lines = qMax( 1, _contentHeight / _fontHeight ); + _usedLines = qMin( _usedLines, _lines ); } } @@ -2886,24 +2914,24 @@ void TerminalDisplay::makeImage() Q_ASSERT( _lines > 0 && _columns > 0 ); Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns ); - _imageSize=_lines*_columns; + _imageSize = _lines * _columns; // We over-commit one character so that we can be more relaxed in dealing with // certain boundary conditions: _image[_imageSize] is a valid but unused position - _image = new Character[_imageSize+1]; + _image = new Character[_imageSize + 1]; clearImage(); } // calculate the needed size, this must be synced with calcGeometry() -void TerminalDisplay::setSize(int columns, int lines) +void TerminalDisplay::setSize( int columns, int lines ) { int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->sizeHint().width(); int horizontalMargin = 2 * DEFAULT_LEFT_MARGIN; int verticalMargin = 2 * DEFAULT_TOP_MARGIN; - QSize newSize = QSize( horizontalMargin + scrollBarWidth + (columns * _fontWidth) , - verticalMargin + (lines * _fontHeight) ); + QSize newSize = QSize( horizontalMargin + scrollBarWidth + ( columns * _fontWidth ), + verticalMargin + ( lines * _fontHeight ) ); if ( newSize != size() ) { @@ -2912,23 +2940,23 @@ void TerminalDisplay::setSize(int columns, int lines) } } -void TerminalDisplay::setFixedSize(int cols, int lins) +void TerminalDisplay::setFixedSize( int cols, int lins ) { _isFixedSize = true; //ensure that display is at least one line by one column in size - _columns = qMax(1,cols); - _lines = qMax(1,lins); - _usedColumns = qMin(_usedColumns,_columns); - _usedLines = qMin(_usedLines,_lines); + _columns = qMax( 1, cols ); + _lines = qMax( 1, lins ); + _usedColumns = qMin( _usedColumns, _columns ); + _usedLines = qMin( _usedLines, _lines ); - if (_image) + if ( _image ) { - delete[] _image; - makeImage(); + delete[] _image; + makeImage(); } - setSize(cols, lins); - QWidget::setFixedSize(_size); + setSize( cols, lins ); + QWidget::setFixedSize( _size ); } QSize TerminalDisplay::sizeHint() const @@ -2943,44 +2971,44 @@ QSize TerminalDisplay::sizeHint() const /* */ /* --------------------------------------------------------------------- */ -void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event) +void TerminalDisplay::dragEnterEvent( QDragEnterEvent *event ) { - if (event->mimeData()->hasFormat(QStringLiteral("text/plain"))) - event->acceptProposedAction(); - if (event->mimeData()->urls().count()) - event->acceptProposedAction(); + if ( event->mimeData()->hasFormat( QStringLiteral( "text/plain" ) ) ) + event->acceptProposedAction(); + if ( event->mimeData()->urls().count() ) + event->acceptProposedAction(); } -void TerminalDisplay::dropEvent(QDropEvent* event) +void TerminalDisplay::dropEvent( QDropEvent *event ) { //KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); QList urls = event->mimeData()->urls(); QString dropText; - if (!urls.isEmpty()) + if ( !urls.isEmpty() ) { - // TODO/FIXME: escape or quote pasted things if neccessary... - qDebug() << "TerminalDisplay: handling urls. It can be broken. Report any errors, please"; + // TODO/FIXME: escape or quote pasted things if neccessary... + qDebug() << "TerminalDisplay: handling urls. It can be broken. Report any errors, please"; for ( int i = 0 ; i < urls.count() ; i++ ) { - //KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); - QUrl url = urls[i]; + //KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); + QUrl url = urls[i]; - QString urlText; + QString urlText; - if (url.isLocalFile()) - urlText = url.path(); - else - urlText = url.toString(); + if ( url.isLocalFile() ) + urlText = url.path(); + else + urlText = url.toString(); - // in future it may be useful to be able to insert file names with drag-and-drop - // without quoting them (this only affects paths with spaces in) - //urlText = KShell::quoteArg(urlText); + // in future it may be useful to be able to insert file names with drag-and-drop + // without quoting them (this only affects paths with spaces in) + //urlText = KShell::quoteArg(urlText); - dropText += urlText; + dropText += urlText; - if ( i != urls.count()-1 ) - dropText += ' '; + if ( i != urls.count() - 1 ) + dropText += ' '; } } else @@ -2988,58 +3016,58 @@ void TerminalDisplay::dropEvent(QDropEvent* event) dropText = event->mimeData()->text(); } - emit sendStringToEmu(dropText.toLocal8Bit()); + emit sendStringToEmu( dropText.toLocal8Bit() ); } void TerminalDisplay::doDrag() { dragInfo.state = diDragging; - dragInfo.dragObject = new QDrag(this); + dragInfo.dragObject = new QDrag( this ); QMimeData *mimeData = new QMimeData; - mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); - dragInfo.dragObject->setMimeData(mimeData); - dragInfo.dragObject->start(Qt::CopyAction); + mimeData->setText( QApplication::clipboard()->text( QClipboard::Selection ) ); + dragInfo.dragObject->setMimeData( mimeData ); + dragInfo.dragObject->start( Qt::CopyAction ); // Don't delete the QTextDrag object. Qt will delete it when it's done with it. } -void TerminalDisplay::outputSuspended(bool suspended) +void TerminalDisplay::outputSuspended( bool suspended ) { - //create the label when this function is first called - if (!_outputSuspendedLabel) - { - //This label includes a link to an English language website - //describing the 'flow control' (Xon/Xoff) feature found in almost - //all terminal emulators. - //If there isn't a suitable article available in the target language the link - //can simply be removed. - _outputSuspendedLabel = new QLabel( tr("Output has been " - "suspended" - " by pressing Ctrl+S." - " Press Ctrl+Q to resume."), - this ); + //create the label when this function is first called + if ( !_outputSuspendedLabel ) + { + //This label includes a link to an English language website + //describing the 'flow control' (Xon/Xoff) feature found in almost + //all terminal emulators. + //If there isn't a suitable article available in the target language the link + //can simply be removed. + _outputSuspendedLabel = new QLabel( tr( "Output has been " + "suspended" + " by pressing Ctrl+S." + " Press Ctrl+Q to resume." ), + this ); - QPalette palette(_outputSuspendedLabel->palette()); - //KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); - _outputSuspendedLabel->setPalette(palette); - _outputSuspendedLabel->setAutoFillBackground(true); - _outputSuspendedLabel->setBackgroundRole(QPalette::Base); - _outputSuspendedLabel->setFont(QApplication::font()); - _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); + QPalette palette( _outputSuspendedLabel->palette() ); + //KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); + _outputSuspendedLabel->setPalette( palette ); + _outputSuspendedLabel->setAutoFillBackground( true ); + _outputSuspendedLabel->setBackgroundRole( QPalette::Base ); + _outputSuspendedLabel->setFont( QApplication::font() ); + _outputSuspendedLabel->setContentsMargins( 5, 5, 5, 5 ); - //enable activation of "Xon/Xoff" link in label - _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | - Qt::LinksAccessibleByKeyboard); - _outputSuspendedLabel->setOpenExternalLinks(true); - _outputSuspendedLabel->setVisible(false); + //enable activation of "Xon/Xoff" link in label + _outputSuspendedLabel->setTextInteractionFlags( Qt::LinksAccessibleByMouse | + Qt::LinksAccessibleByKeyboard ); + _outputSuspendedLabel->setOpenExternalLinks( true ); + _outputSuspendedLabel->setVisible( false ); - _gridLayout->addWidget(_outputSuspendedLabel); - _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, - QSizePolicy::Expanding), - 1,0); + _gridLayout->addWidget( _outputSuspendedLabel ); + _gridLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, + QSizePolicy::Expanding ), + 1, 0 ); - } + } - _outputSuspendedLabel->setVisible(suspended); + _outputSuspendedLabel->setVisible( suspended ); } uint TerminalDisplay::lineSpacing() const @@ -3047,68 +3075,68 @@ uint TerminalDisplay::lineSpacing() const return _lineSpacing; } -void TerminalDisplay::setLineSpacing(uint i) +void TerminalDisplay::setLineSpacing( uint i ) { _lineSpacing = i; - setVTFont(font()); // Trigger an update. + setVTFont( font() ); // Trigger an update. } -AutoScrollHandler::AutoScrollHandler(QWidget* parent) -: QObject(parent) -, _timerId(0) +AutoScrollHandler::AutoScrollHandler( QWidget *parent ) + : QObject( parent ) + , _timerId( 0 ) { - parent->installEventFilter(this); + parent->installEventFilter( this ); } -void AutoScrollHandler::timerEvent(QTimerEvent* event) +void AutoScrollHandler::timerEvent( QTimerEvent *event ) { - if (event->timerId() != _timerId) - return; + if ( event->timerId() != _timerId ) + return; - QMouseEvent mouseEvent( QEvent::MouseMove, - widget()->mapFromGlobal(QCursor::pos()), - Qt::NoButton, - Qt::LeftButton, - Qt::NoModifier); + QMouseEvent mouseEvent( QEvent::MouseMove, + widget()->mapFromGlobal( QCursor::pos() ), + Qt::NoButton, + Qt::LeftButton, + Qt::NoModifier ); - QApplication::sendEvent(widget(),&mouseEvent); + QApplication::sendEvent( widget(), &mouseEvent ); } -bool AutoScrollHandler::eventFilter(QObject* watched,QEvent* event) +bool AutoScrollHandler::eventFilter( QObject *watched, QEvent *event ) { - Q_ASSERT( watched == parent() ); - Q_UNUSED( watched ); + Q_ASSERT( watched == parent() ); + Q_UNUSED( watched ); - QMouseEvent* mouseEvent = (QMouseEvent*)event; - switch (event->type()) + QMouseEvent *mouseEvent = ( QMouseEvent * )event; + switch ( event->type() ) + { + case QEvent::MouseMove: { - case QEvent::MouseMove: - { - bool mouseInWidget = widget()->rect().contains(mouseEvent->pos()); + bool mouseInWidget = widget()->rect().contains( mouseEvent->pos() ); - if (mouseInWidget) - { - if (_timerId) - killTimer(_timerId); - _timerId = 0; - } - else - { - if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton)) - _timerId = startTimer(100); - } - break; - } - case QEvent::MouseButtonRelease: - if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton)) - { - killTimer(_timerId); - _timerId = 0; - } - break; - default: - break; - }; + if ( mouseInWidget ) + { + if ( _timerId ) + killTimer( _timerId ); + _timerId = 0; + } + else + { + if ( !_timerId && ( mouseEvent->buttons() & Qt::LeftButton ) ) + _timerId = startTimer( 100 ); + } + break; + } + case QEvent::MouseButtonRelease: + if ( _timerId && ( mouseEvent->buttons() & ~Qt::LeftButton ) ) + { + killTimer( _timerId ); + _timerId = 0; + } + break; + default: + break; + }; - return false; + return false; } //#include "TerminalDisplay.moc" diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index a51017e1b4b..8863a854240 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -485,20 +485,20 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi if ( myRasterExtent.yMaximum() < extent.yMaximum() ) { - top = qRound( ( extent.yMaximum() - myRasterExtent.yMaximum() ) / yRes ); + top = std::round( ( extent.yMaximum() - myRasterExtent.yMaximum() ) / yRes ); } if ( myRasterExtent.yMinimum() > extent.yMinimum() ) { - bottom = qRound( ( extent.yMaximum() - myRasterExtent.yMinimum() ) / yRes ) - 1; + bottom = std::round( ( extent.yMaximum() - myRasterExtent.yMinimum() ) / yRes ) - 1; } if ( myRasterExtent.xMinimum() > extent.xMinimum() ) { - left = qRound( ( myRasterExtent.xMinimum() - extent.xMinimum() ) / xRes ); + left = std::round( ( myRasterExtent.xMinimum() - extent.xMinimum() ) / xRes ); } if ( myRasterExtent.xMaximum() < extent.xMaximum() ) { - right = qRound( ( myRasterExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1; + right = std::round( ( myRasterExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1; } #endif QRect subRect = QgsRasterBlock::subRect( extent, pixelWidth, pixelHeight, myRasterExtent ); @@ -585,11 +585,11 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi if ( xRes > srcXRes ) { - tmpWidth = static_cast( qRound( srcWidth * srcXRes / xRes ) ); + tmpWidth = static_cast( std::round( srcWidth * srcXRes / xRes ) ); } if ( yRes > fabs( srcYRes ) ) { - tmpHeight = static_cast( qRound( -1.*srcHeight * srcYRes / yRes ) ); + tmpHeight = static_cast( std::round( -1.*srcHeight * srcYRes / yRes ) ); } double tmpXMin = mExtent.xMinimum() + srcLeft * srcXRes; diff --git a/tests/src/analysis/testqgsalignraster.cpp b/tests/src/analysis/testqgsalignraster.cpp index 6ad8e7b9f72..a10b9cea88a 100644 --- a/tests/src/analysis/testqgsalignraster.cpp +++ b/tests/src/analysis/testqgsalignraster.cpp @@ -19,6 +19,7 @@ #include "qgsapplication.h" #include "qgscoordinatereferencesystem.h" #include "qgsrectangle.h" +#include "qgstestutils.h" #include @@ -227,11 +228,11 @@ class TestAlignRaster : public QObject QgsCoordinateReferenceSystem outCRS( out.crs() ); QCOMPARE( outCRS, destCRS ); QCOMPARE( out.rasterSize(), QSize( 4, 4 ) ); - // let's stick to integers to keep the test more robust - QCOMPARE( qRound( out.cellSize().width() ), 22293 ); // ~ 22293.256065 - QCOMPARE( qRound( out.cellSize().height() ), 22293 ); // ~ 22293.256065 - QCOMPARE( qRound( out.gridOffset().x() ), 4327 ); // ~ 4327.168434 - QCOMPARE( qRound( out.gridOffset().y() ), 637 ); // ~ 637.007990 + // tolerance of 1 to keep the test more robust + QGSCOMPARENEAR( out.cellSize().width(), 22293, 1 ); // ~ 22293.256065 + QGSCOMPARENEAR( out.cellSize().height(), 22293, 1 ); // ~ 22293.256065 + QGSCOMPARENEAR( out.gridOffset().x(), 4327, 1 ); // ~ 4327.168434 + QGSCOMPARENEAR( out.gridOffset().y(), 637, 1 ); // ~ 637.007990 QCOMPARE( out.identify( 1308405, -746611 ), 10. ); } diff --git a/tests/src/app/testqgisappclipboard.cpp b/tests/src/app/testqgisappclipboard.cpp index be741d080b8..120c354549f 100644 --- a/tests/src/app/testqgisappclipboard.cpp +++ b/tests/src/app/testqgisappclipboard.cpp @@ -194,8 +194,8 @@ void TestQgisAppClipboard::copyToText() QStringList list = regex.capturedTexts(); QCOMPARE( list.count(), 3 ); - int x = qRound( list.at( 1 ).toDouble() ); - int y = qRound( list.at( 2 ).toDouble() ); + int x = std::round( list.at( 1 ).toDouble() ); + int y = std::round( list.at( 2 ).toDouble() ); QCOMPARE( x, 145 ); QCOMPARE( y, -38 ); diff --git a/tests/src/app/testqgsnodetool.cpp b/tests/src/app/testqgsnodetool.cpp index a2382763bda..3860e08b06a 100644 --- a/tests/src/app/testqgsnodetool.cpp +++ b/tests/src/app/testqgsnodetool.cpp @@ -68,7 +68,7 @@ class TestQgsNodeTool : public QObject QPoint mapToScreen( double mapX, double mapY ) { QgsPointXY pt = mCanvas->mapSettings().mapToPixel().transform( mapX, mapY ); - return QPoint( qRound( pt.x() ), qRound( pt.y() ) ); + return QPoint( std::round( pt.x() ), std::round( pt.y() ) ); } void mouseMove( double mapX, double mapY ) diff --git a/tests/src/core/testqgis.cpp b/tests/src/core/testqgis.cpp index 24dc7dd4b34..91180a73299 100644 --- a/tests/src/core/testqgis.cpp +++ b/tests/src/core/testqgis.cpp @@ -38,7 +38,6 @@ class TestQgis : public QObject void permissiveToDouble(); void permissiveToInt(); void doubleToString(); - void qgsround(); void signalBlocker(); void qVariantCompare_data(); void qVariantCompare(); @@ -142,20 +141,6 @@ void TestQgis::doubleToString() QCOMPARE( qgsDoubleToString( 12345, -1 ), QString( "12345" ) ); } -void TestQgis::qgsround() -{ - QCOMPARE( qgsRound( 3.141592653589793 ), 3. ); - QCOMPARE( qgsRound( 2.718281828459045 ), 3. ); - QCOMPARE( qgsRound( -3.141592653589793 ), -3. ); - QCOMPARE( qgsRound( -2.718281828459045 ), -3. ); - QCOMPARE( qgsRound( 314159265358979.3 ), 314159265358979. ); - QCOMPARE( qgsRound( 2718281828459.045 ), 2718281828459. ); - QCOMPARE( qgsRound( -314159265358979.3 ), -314159265358979. ); - QCOMPARE( qgsRound( -2718281828459.045 ), -2718281828459. ); - QCOMPARE( qgsRound( 1.5 ), 2. ); - QCOMPARE( qgsRound( -1.5 ), -2. ); -} - void TestQgis::signalBlocker() { std::unique_ptr< QCheckBox > checkbox( new QCheckBox() ); diff --git a/tests/src/core/testqgstaskmanager.cpp b/tests/src/core/testqgstaskmanager.cpp index 950cea39b7e..d914413c47e 100644 --- a/tests/src/core/testqgstaskmanager.cpp +++ b/tests/src/core/testqgstaskmanager.cpp @@ -489,14 +489,14 @@ void TestQgsTaskManager::subTask() // test progress calculation QSignalSpy spy( parent, &QgsTask::progressChanged ); parent->emitProgressChanged( 50 ); - QCOMPARE( qRound( parent->progress() ), 17 ); + QCOMPARE( std::round( parent->progress() ), 17 ); //QCOMPARE( spy.count(), 1 ); - QCOMPARE( qRound( spy.last().at( 0 ).toDouble() ), 17 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 17 ); subTask->emitProgressChanged( 100 ); - QCOMPARE( qRound( parent->progress() ), 50 ); + QCOMPARE( std::round( parent->progress() ), 50 ); //QCOMPARE( spy.count(), 2 ); - QCOMPARE( qRound( spy.last().at( 0 ).toDouble() ), 50 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 50 ); subTask2->finish(); while ( subTask2->status() != QgsTask::Complete ) @@ -504,14 +504,14 @@ void TestQgsTaskManager::subTask() QCoreApplication::processEvents(); } flushEvents(); - QCOMPARE( qRound( parent->progress() ), 83 ); + QCOMPARE( std::round( parent->progress() ), 83 ); //QCOMPARE( spy.count(), 3 ); - QCOMPARE( qRound( spy.last().at( 0 ).toDouble() ), 83 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 83 ); parent->emitProgressChanged( 100 ); - QCOMPARE( qRound( parent->progress() ), 100 ); + QCOMPARE( std::round( parent->progress() ), 100 ); //QCOMPARE( spy.count(), 4 ); - QCOMPARE( qRound( spy.last().at( 0 ).toDouble() ), 100 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 100 ); parent->terminate(); subTask->terminate(); diff --git a/tests/src/core/testqgsvectordataprovider.cpp b/tests/src/core/testqgsvectordataprovider.cpp index 532dfeb29bb..9b662786c21 100644 --- a/tests/src/core/testqgsvectordataprovider.cpp +++ b/tests/src/core/testqgsvectordataprovider.cpp @@ -90,7 +90,7 @@ void TestQgsVectorDataProvider::cleanupTestCase() static double keep6digits( double x ) { - return qRound( x * 1e6 ) / 1e6; + return std::round( x * 1e6 ) / 1e6; } static void checkFid4( QgsFeature &f, bool hasGeometry, bool hasAttrs, int onlyOneAttribute ) diff --git a/tests/src/providers/testqgswcspublicservers.cpp b/tests/src/providers/testqgswcspublicservers.cpp index c5e4e7fb042..8101c74c277 100644 --- a/tests/src/providers/testqgswcspublicservers.cpp +++ b/tests/src/providers/testqgswcspublicservers.cpp @@ -401,7 +401,7 @@ void TestQgsWcsPublicServers::test() int myHeight = 100; if ( myCoverage.hasSize ) { - myHeight = static_cast( qRound( 1.0 * myWidth * myCoverage.height / myCoverage.width ) ); + myHeight = static_cast( std::round( 1.0 * myWidth * myCoverage.height / myCoverage.width ) ); } myLog << QStringLiteral( "hasSize:%1" ).arg( myCoverage.hasSize ); From f2b2c6d2fde485cabc014d300e21404e15729b4a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 24 Aug 2017 16:45:29 +1000 Subject: [PATCH 115/364] Use fabs/abs/labs instead of qAbs Better to stick to standard methods where available instead of less supported Qt methods --- src/analysis/interpolation/MathUtils.cc | 4 +- src/analysis/raster/qgsalignraster.cpp | 8 +- src/analysis/raster/qgsrelief.cpp | 2 +- src/analysis/vector/qgsgeometrysnapper.cpp | 4 +- src/app/qgsdecorationscalebar.cpp | 2 +- src/app/qgslabelinggui.cpp | 2 +- src/app/qgsmaptoolmeasureangle.cpp | 4 +- src/app/qgsundowidget.cpp | 2 +- src/core/composer/qgscomposerarrow.cpp | 2 +- src/core/composer/qgscomposermapgrid.cpp | 4 +- src/core/composer/qgscomposermousehandles.cpp | 4 +- src/core/composer/qgscomposernodesitem.cpp | 6 +- src/core/composer/qgscomposerpicture.cpp | 4 +- src/core/dxf/qgsdxfpaintengine.cpp | 2 +- src/core/effects/qgsimageoperation.cpp | 4 +- src/core/expression/qgsexpressionfunction.cpp | 4 +- src/core/geometry/qgscircle.cpp | 18 +- src/core/geometry/qgscircle.h | 6 +- src/core/geometry/qgscurvepolygon.cpp | 4 +- src/core/geometry/qgsellipse.cpp | 12 +- src/core/geometry/qgsgeometry.cpp | 2 +- .../geometry/qgsinternalgeometryengine.cpp | 4 +- src/core/geometry/qgsregularpolygon.cpp | 8 +- src/core/layout/qgslayoutsnapper.cpp | 2 +- src/core/pal/feature.cpp | 10 +- src/core/pal/pointset.cpp | 4 +- src/core/qgsclipper.h | 4 +- src/core/qgscoordinatereferencesystem.cpp | 2 +- src/core/qgsdistancearea.cpp | 8 +- src/core/qgsmapsettings.cpp | 4 +- src/core/qgspallabeling.cpp | 10 +- src/core/qgspointxy.cpp | 16 +- src/core/qgsrenderchecker.cpp | 12 +- src/core/qgstestutils.h | 4 +- src/core/qgsunittypes.cpp | 26 +- src/core/qgsvectorfilewriter.cpp | 2 +- src/core/qgsvectorlayerlabeling.cpp | 2 +- src/core/raster/qgscolorrampshader.cpp | 2 +- src/core/raster/qgsrasterchecker.cpp | 4 +- src/core/raster/qgsrasterminmaxorigin.cpp | 6 +- src/core/simplify/effectivearea.cpp | 4 +- src/core/symbology/qgsfillsymbollayer.cpp | 14 +- src/core/symbology/qgslinesymbollayer.cpp | 6 +- src/core/symbology/qgssymbollayerutils.cpp | 2 +- src/gui/layout/qgslayoutruler.cpp | 4 +- src/gui/layout/qgslayoutview.cpp | 2 +- src/gui/layout/qgslayoutviewtool.cpp | 2 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 10 +- src/gui/qgscomposerview.cpp | 4 +- src/gui/qgsgradientstopeditor.cpp | 6 +- src/gui/qgsmapcanvas.cpp | 6 +- src/gui/qgsmapcanvasannotationitem.cpp | 10 +- src/gui/qgsmaptoolextent.cpp | 4 +- src/gui/qgsscalecombobox.cpp | 2 +- src/gui/raster/qgsrasterhistogramwidget.cpp | 2 +- .../symbology/qgsgraduatedhistogramwidget.cpp | 6 +- .../checks/qgsgeometryoverlapcheck.cpp | 2 +- .../checks/qgsgeometryoverlapcheck.h | 2 +- .../utils/qgsgeometrycheckerutils.cpp | 2 +- .../georeferencer/qgsgeorefdelegates.cpp | 3 +- .../georeferencer/qgsgeoreftransform.cpp | 10 +- src/plugins/georeferencer/qgsleastsquares.cpp | 4 +- .../georeferencer/qgsmapcoordsdialog.cpp | 2 +- src/plugins/grass/qgsgrassnewmapset.cpp | 2 +- src/plugins/grass/qtermwidget/ColorScheme.cpp | 982 +++++++++--------- src/providers/gdal/qgsgdalprovider.cpp | 6 +- .../grass/qgsgrassrasterprovider.cpp | 2 +- src/providers/wcs/qgswcsprovider.cpp | 8 +- src/providers/wms/qgswmsprovider.cpp | 4 +- tests/src/core/testqgsdistancearea.cpp | 2 +- .../providers/grass/testqgsgrassprovider.cpp | 2 +- 71 files changed, 674 insertions(+), 673 deletions(-) diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index 29033a21c57..8b7b75bbcea 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -210,7 +210,7 @@ double MathUtils::distPointFromLine( QgsPoint *thepoint, QgsPoint *p1, QgsPoint double a = normal.getX(); double b = normal.getY(); double c = -( normal.getX() * p2->x() + normal.getY() * p2->y() ); - double distance = qAbs( ( a * thepoint->x() + b * thepoint->y() + c ) / ( sqrt( a * a + b * b ) ) ); + double distance = fabs( ( a * thepoint->x() + b * thepoint->y() + c ) / ( sqrt( a * a + b * b ) ) ); return distance; } else @@ -471,7 +471,7 @@ double MathUtils::power( double a, int b ) return 1; } double tmp = a; - for ( int i = 2; i <= qAbs( ( double )b ); i++ ) + for ( int i = 2; i <= abs( b ); i++ ) { a *= tmp; diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 8e5f68357d4..8b451109de4 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -30,7 +30,7 @@ static double ceil_with_tolerance( double value ) { - if ( qAbs( value - std::round( value ) ) < 1e-6 ) + if ( fabs( value - std::round( value ) ) < 1e-6 ) return std::round( value ); else return qCeil( value ); @@ -38,7 +38,7 @@ static double ceil_with_tolerance( double value ) static double floor_with_tolerance( double value ) { - if ( qAbs( value - std::round( value ) ) < 1e-6 ) + if ( fabs( value - std::round( value ) ) < 1e-6 ) return std::round( value ); else return qFloor( value ); @@ -538,7 +538,7 @@ bool QgsAlignRaster::suggestedWarpOutput( const QgsAlignRaster::RasterInfo &info if ( eErr != CE_None ) return false; - QSizeF cs( qAbs( adfDstGeoTransform[1] ), qAbs( adfDstGeoTransform[5] ) ); + QSizeF cs( fabs( adfDstGeoTransform[1] ), fabs( adfDstGeoTransform[5] ) ); if ( rect ) *rect = QgsRectangle( extents[0], extents[1], extents[2], extents[3] ); @@ -582,7 +582,7 @@ QgsAlignRaster::RasterInfo::~RasterInfo() QSizeF QgsAlignRaster::RasterInfo::cellSize() const { - return QSizeF( qAbs( mGeoTransform[1] ), qAbs( mGeoTransform[5] ) ); + return QSizeF( fabs( mGeoTransform[1] ), fabs( mGeoTransform[5] ) ); } QPointF QgsAlignRaster::RasterInfo::gridOffset() const diff --git a/src/analysis/raster/qgsrelief.cpp b/src/analysis/raster/qgsrelief.cpp index d54238a841f..b00f7a13ed4 100644 --- a/src/analysis/raster/qgsrelief.cpp +++ b/src/analysis/raster/qgsrelief.cpp @@ -353,7 +353,7 @@ bool QgsRelief::processNineCellWindow( float *x1, float *x2, float *x3, float *x float aspect = mAspectFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 ); if ( hillShadeValue285 != mOutputNodataValue && aspect != mOutputNodataValue ) { - double angle_diff = qAbs( 285 - aspect ); + double angle_diff = fabs( 285 - aspect ); if ( angle_diff > 180 ) { angle_diff -= 180; diff --git a/src/analysis/vector/qgsgeometrysnapper.cpp b/src/analysis/vector/qgsgeometrysnapper.cpp index 16cdb2b0b68..5ab47eccdd1 100644 --- a/src/analysis/vector/qgsgeometrysnapper.cpp +++ b/src/analysis/vector/qgsgeometrysnapper.cpp @@ -110,8 +110,8 @@ class Raytracer // See http://playtechs.blogspot.ch/2007/03/raytracing-on-grid.html public: Raytracer( float x0, float y0, float x1, float y1 ) - : m_dx( qAbs( x1 - x0 ) ) - , m_dy( qAbs( y1 - y0 ) ) + : m_dx( fabs( x1 - x0 ) ) + , m_dy( fabs( y1 - y0 ) ) , m_x( qFloor( x0 ) ) , m_y( qFloor( y0 ) ) , m_n( 1 ) diff --git a/src/app/qgsdecorationscalebar.cpp b/src/app/qgsdecorationscalebar.cpp index dae366accf3..0804e07fa07 100644 --- a/src/app/qgsdecorationscalebar.cpp +++ b/src/app/qgsdecorationscalebar.cpp @@ -113,7 +113,7 @@ void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRender //Get map units per pixel. This can be negative at times (to do with //projections) and that just confuses the rest of the code in this //function, so force to a positive number. - double myMapUnitsPerPixelDouble = qAbs( context.mapToPixel().mapUnitsPerPixel() ); + double myMapUnitsPerPixelDouble = fabs( context.mapToPixel().mapUnitsPerPixel() ); double myActualSize = mPreferredSize; // Exit if the canvas width is 0 or layercount is 0 or QGIS will freeze diff --git a/src/app/qgslabelinggui.cpp b/src/app/qgslabelinggui.cpp index 1d7441c862e..241ca2bca26 100644 --- a/src/app/qgslabelinggui.cpp +++ b/src/app/qgslabelinggui.cpp @@ -237,7 +237,7 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer ) // curved label max character angles mMaxCharAngleInDSpinBox->setValue( lyr.maxCurvedCharAngleIn ); // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI - mMaxCharAngleOutDSpinBox->setValue( qAbs( lyr.maxCurvedCharAngleOut ) ); + mMaxCharAngleOutDSpinBox->setValue( fabs( lyr.maxCurvedCharAngleOut ) ); wrapCharacterEdit->setText( lyr.wrapChar ); mFontMultiLineAlignComboBox->setCurrentIndex( ( unsigned int ) lyr.multilineAlign ); diff --git a/src/app/qgsmaptoolmeasureangle.cpp b/src/app/qgsmaptoolmeasureangle.cpp index f115a972c52..d41d19be49a 100644 --- a/src/app/qgsmaptoolmeasureangle.cpp +++ b/src/app/qgsmaptoolmeasureangle.cpp @@ -64,9 +64,9 @@ void QgsMapToolMeasureAngle::canvasMoveEvent( QgsMapMouseEvent *e ) double azimuthOne = mDa.bearing( mAnglePoints.at( 1 ), mAnglePoints.at( 0 ) ); double azimuthTwo = mDa.bearing( mAnglePoints.at( 1 ), point ); double resultAngle = azimuthTwo - azimuthOne; - QgsDebugMsg( QString::number( qAbs( resultAngle ) ) ); + QgsDebugMsg( QString::number( fabs( resultAngle ) ) ); QgsDebugMsg( QString::number( M_PI ) ); - if ( qAbs( resultAngle ) > M_PI ) + if ( fabs( resultAngle ) > M_PI ) { if ( resultAngle < 0 ) { diff --git a/src/app/qgsundowidget.cpp b/src/app/qgsundowidget.cpp index a90db5bb3c2..41dd32a0057 100644 --- a/src/app/qgsundowidget.cpp +++ b/src/app/qgsundowidget.cpp @@ -78,7 +78,7 @@ void QgsUndoWidget::indexChanged( int curIndx ) canRedo = mUndoStack->canRedo(); curCount = mUndoStack->count(); } - int offset = qAbs( mPreviousIndex - curIndx ); + int offset = abs( mPreviousIndex - curIndx ); // when individually redoing, differentiate between last redo and a new command added to stack bool lastRedo = ( mPreviousIndex == ( mPreviousCount - 1 ) && mPreviousCount == curCount && !canRedo ); diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp index 03971ea9d81..ed55288360a 100644 --- a/src/core/composer/qgscomposerarrow.cpp +++ b/src/core/composer/qgscomposerarrow.cpp @@ -409,7 +409,7 @@ void QgsComposerArrow::adaptItemSceneRect() { //rectangle containing start and end point QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ), - qAbs( mStopPoint.x() - mStartPoint.x() ), qAbs( mStopPoint.y() - mStartPoint.y() ) ); + fabs( mStopPoint.x() - mStartPoint.x() ), fabs( mStopPoint.y() - mStartPoint.y() ) ); double enlarge = computeMarkerMargin(); rect.adjust( -enlarge, -enlarge, enlarge, enlarge ); QgsComposerItem::setSceneRect( rect ); diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index f3aa398c130..2dfc342b318 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -1465,11 +1465,11 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGr if ( geographic ) { //insert degree symbol for geographic coordinates - return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere; + return QString::number( fabs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere; } else { - return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + hemisphere; + return QString::number( fabs( value ), 'f', mGridAnnotationPrecision ) + hemisphere; } } else if ( mGridAnnotationFormat == CustomFormat ) diff --git a/src/core/composer/qgscomposermousehandles.cpp b/src/core/composer/qgscomposermousehandles.cpp index e8f3eeb66a7..3e01aa20027 100644 --- a/src/core/composer/qgscomposermousehandles.cpp +++ b/src/core/composer/qgscomposermousehandles.cpp @@ -593,7 +593,7 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent *event double diffY = mouseMoveStopPoint.y() - mMouseMoveStartPos.y(); //it was only a click - if ( qAbs( diffX ) < std::numeric_limits::min() && qAbs( diffY ) < std::numeric_limits::min() ) + if ( fabs( diffX ) < std::numeric_limits::min() && fabs( diffY ) < std::numeric_limits::min() ) { mIsDragging = false; mIsResizing = false; @@ -824,7 +824,7 @@ void QgsComposerMouseHandles::dragMouseMove( QPointF currentPosition, bool lockM { //constrained (shift) moving should lock to horizontal/vertical movement //reset the smaller of the x/y movements - if ( qAbs( moveRectX ) <= qAbs( moveRectY ) ) + if ( fabs( moveRectX ) <= fabs( moveRectY ) ) { moveRectX = 0; } diff --git a/src/core/composer/qgscomposernodesitem.cpp b/src/core/composer/qgscomposernodesitem.cpp index 8157bbbddb9..e71ca769eec 100644 --- a/src/core/composer/qgscomposernodesitem.cpp +++ b/src/core/composer/qgscomposernodesitem.cpp @@ -77,7 +77,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, double distance = std::numeric_limits::max(); if ( qIsInf( coef ) ) - distance = qAbs( pt1.x() - start.x() ); + distance = fabs( pt1.x() - start.x() ); else { const double coef2 = ( -1 / coef ); @@ -86,7 +86,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, QPointF inter; if ( qIsInf( coef2 ) ) { - distance = qAbs( pt1.y() - start.y() ); + distance = fabs( pt1.y() - start.y() ); inter.setX( start.x() ); inter.setY( pt1.y() ); } @@ -104,7 +104,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, const double length3 = computeDistance( pt1, pt2 ); const double length4 = length1 + length2; - if ( qAbs( length3 - length4 ) < std::numeric_limits::epsilon() ) + if ( fabs( length3 - length4 ) < std::numeric_limits::epsilon() ) distance = computeDistance( inter, start ); } diff --git a/src/core/composer/qgscomposerpicture.cpp b/src/core/composer/qgscomposerpicture.cpp index abe96c4e7b4..e7b3be32c45 100644 --- a/src/core/composer/qgscomposerpicture.cpp +++ b/src/core/composer/qgscomposerpicture.cpp @@ -578,8 +578,8 @@ void QgsComposerPicture::setSceneRect( const QRectF &rectangle ) //if height has changed more than width, then fix width and set height correspondingly //else, do the opposite - if ( qAbs( rect().width() - rectangle.width() ) < - qAbs( rect().height() - rectangle.height() ) ) + if ( fabs( rect().width() - rectangle.width() ) < + fabs( rect().height() - rectangle.height() ) ) { newRect.setHeight( targetImageSize.height() * newRect.width() / targetImageSize.width() ); } diff --git a/src/core/dxf/qgsdxfpaintengine.cpp b/src/core/dxf/qgsdxfpaintengine.cpp index 3123231ca7d..68926fbb3a9 100644 --- a/src/core/dxf/qgsdxfpaintengine.cpp +++ b/src/core/dxf/qgsdxfpaintengine.cpp @@ -262,7 +262,7 @@ double QgsDxfPaintEngine::power( double a, int b ) return 1; double tmp = a; - for ( int i = 2; i <= qAbs( static_cast< double >( b ) ); i++ ) + for ( int i = 2; i <= abs( b ); i++ ) a *= tmp; if ( b > 0 ) diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index 910d4b7816c..846675fe59c 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -889,8 +889,8 @@ QRect QgsImageOperation::nonTransparentImageRect( const QImage &image, QSize min if ( center ) { // recompute min and max to center image - const int dx = qMax( qAbs( xmax - width / 2 ), qAbs( xmin - width / 2 ) ); - const int dy = qMax( qAbs( ymax - height / 2 ), qAbs( ymin - height / 2 ) ); + const int dx = qMax( abs( xmax - width / 2 ), abs( xmin - width / 2 ) ); + const int dy = qMax( abs( ymax - height / 2 ), abs( ymin - height / 2 ) ); xmin = qMax( 0, width / 2 - dx ); xmax = qMin( width, width / 2 + dx ); ymin = qMax( 0, height / 2 - dy ); diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 512c0628e42..268ced0c10a 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -1007,13 +1007,13 @@ static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionCont if ( strhit == lasthit || strhit == -1 ) { //if no new backward delimiter found, try to locate forward - strhit = lines[i].indexOf( rx, strcurrent + qAbs( wrap ) ); + strhit = lines[i].indexOf( rx, strcurrent + labs( wrap ) ); } lasthit = strhit; } else { - strhit = lines[i].indexOf( rx, strcurrent + qAbs( wrap ) ); + strhit = lines[i].indexOf( rx, strcurrent + labs( wrap ) ); } if ( strhit > -1 ) { diff --git a/src/core/geometry/qgscircle.cpp b/src/core/geometry/qgscircle.cpp index 54cdb451350..d098f6c6a74 100644 --- a/src/core/geometry/qgscircle.cpp +++ b/src/core/geometry/qgscircle.cpp @@ -52,24 +52,24 @@ static bool isPerpendicular( const QgsPoint &pt1, const QgsPoint &pt2, const Qgs double yDelta_b = pt3.y() - pt2.y(); double xDelta_b = pt3.x() - pt2.x(); - if ( ( qAbs( xDelta_a ) <= epsilon ) && ( qAbs( yDelta_b ) <= epsilon ) ) + if ( ( fabs( xDelta_a ) <= epsilon ) && ( fabs( yDelta_b ) <= epsilon ) ) { return false; } - if ( qAbs( yDelta_a ) <= epsilon ) + if ( fabs( yDelta_a ) <= epsilon ) { return true; } - else if ( qAbs( yDelta_b ) <= epsilon ) + else if ( fabs( yDelta_b ) <= epsilon ) { return true; } - else if ( qAbs( xDelta_a ) <= epsilon ) + else if ( fabs( xDelta_a ) <= epsilon ) { return true; } - else if ( qAbs( xDelta_b ) <= epsilon ) + else if ( fabs( xDelta_b ) <= epsilon ) { return true; } @@ -138,7 +138,7 @@ QgsCircle QgsCircle::from3Points( const QgsPoint &pt1, const QgsPoint &pt2, cons double aSlope = yDelta_a / xDelta_a; double bSlope = yDelta_b / xDelta_b; - if ( ( qAbs( xDelta_a ) <= epsilon ) && ( qAbs( yDelta_b ) <= epsilon ) ) + if ( ( fabs( xDelta_a ) <= epsilon ) && ( fabs( yDelta_b ) <= epsilon ) ) { center.setX( 0.5 * ( p2.x() + p3.x() ) ); center.setY( 0.5 * ( p1.y() + p2.y() ) ); @@ -147,7 +147,7 @@ QgsCircle QgsCircle::from3Points( const QgsPoint &pt1, const QgsPoint &pt2, cons return QgsCircle( center, radius ); } - if ( qAbs( aSlope - bSlope ) <= epsilon ) + if ( fabs( aSlope - bSlope ) <= epsilon ) { return QgsCircle(); } @@ -191,8 +191,8 @@ QgsCircle QgsCircle::from3Tangents( const QgsPoint &pt1_tg1, const QgsPoint &pt2 QgsCircle QgsCircle::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) { - double delta_x = qAbs( pt1.x() - pt2.x() ); - double delta_y = qAbs( pt1.x() - pt2.y() ); + double delta_x = fabs( pt1.x() - pt2.x() ); + double delta_y = fabs( pt1.x() - pt2.y() ); if ( !qgsDoubleNear( delta_x, delta_y ) ) { return QgsCircle(); diff --git a/src/core/geometry/qgscircle.h b/src/core/geometry/qgscircle.h index d3af47e2fcd..7a233c5a9a1 100644 --- a/src/core/geometry/qgscircle.h +++ b/src/core/geometry/qgscircle.h @@ -133,7 +133,7 @@ class CORE_EXPORT QgsCircle : public QgsEllipse */ void setSemiMajorAxis( const double semiMajorAxis ) override { - mSemiMajorAxis = qAbs( semiMajorAxis ); + mSemiMajorAxis = fabs( semiMajorAxis ); mSemiMinorAxis = mSemiMajorAxis; } @@ -143,7 +143,7 @@ class CORE_EXPORT QgsCircle : public QgsEllipse */ void setSemiMinorAxis( const double semiMinorAxis ) override { - mSemiMajorAxis = qAbs( semiMinorAxis ); + mSemiMajorAxis = fabs( semiMinorAxis ); mSemiMinorAxis = mSemiMajorAxis; } @@ -152,7 +152,7 @@ class CORE_EXPORT QgsCircle : public QgsEllipse //! Set the radius of the circle void setRadius( double radius ) { - mSemiMajorAxis = qAbs( radius ); + mSemiMajorAxis = fabs( radius ); mSemiMinorAxis = mSemiMajorAxis; } diff --git a/src/core/geometry/qgscurvepolygon.cpp b/src/core/geometry/qgscurvepolygon.cpp index 00f26ec8bcc..5e1e6d3a414 100644 --- a/src/core/geometry/qgscurvepolygon.cpp +++ b/src/core/geometry/qgscurvepolygon.cpp @@ -370,7 +370,7 @@ double QgsCurvePolygon::area() const { double area = 0.0; mExteriorRing->sumUpArea( area ); - totalArea += qAbs( area ); + totalArea += fabs( area ); } QList::const_iterator ringIt = mInteriorRings.constBegin(); @@ -380,7 +380,7 @@ double QgsCurvePolygon::area() const if ( ( *ringIt )->isRing() ) { ( *ringIt )->sumUpArea( area ); - totalArea -= qAbs( area ); + totalArea -= fabs( area ); } } return totalArea; diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index 5f1fcc30b4c..3414c6a7969 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -25,8 +25,8 @@ void QgsEllipse::normalizeAxis() { - mSemiMajorAxis = qAbs( mSemiMajorAxis ); - mSemiMinorAxis = qAbs( mSemiMinorAxis ); + mSemiMajorAxis = fabs( mSemiMajorAxis ); + mSemiMinorAxis = fabs( mSemiMinorAxis ); if ( mSemiMajorAxis < mSemiMinorAxis ) { std::swap( mSemiMajorAxis, mSemiMinorAxis ); @@ -72,8 +72,8 @@ QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsEllipse QgsEllipse::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) { QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 ); - double axis_a = qAbs( pt2.x() - pt1.x() ) / 2.0; - double axis_b = qAbs( pt2.y() - pt1.y() ) / 2.0; + double axis_a = fabs( pt2.x() - pt1.x() ) / 2.0; + double axis_b = fabs( pt2.y() - pt1.y() ) / 2.0; double azimuth = 90.0; return QgsEllipse( center, axis_a, axis_b, azimuth ); @@ -81,8 +81,8 @@ QgsEllipse QgsEllipse::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) QgsEllipse QgsEllipse::fromCenterPoint( const QgsPoint ¢er, const QgsPoint &pt1 ) { - double axis_a = qAbs( pt1.x() - center.x() ); - double axis_b = qAbs( pt1.y() - center.y() ); + double axis_a = fabs( pt1.x() - center.x() ); + double axis_b = fabs( pt1.y() - center.y() ); double azimuth = 90.0; return QgsEllipse( center, axis_a, axis_b, azimuth ); diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 26066065742..920fffed53f 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -2558,7 +2558,7 @@ QgsLineString *smoothCurve( const QgsLineString &line, const unsigned int iterat QgsPoint p3 = result->pointN( 1 ); double angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ); - angle = qAbs( M_PI - angle ); + angle = fabs( M_PI - angle ); skipFirst = angle > maxAngleRads; } for ( int i = 0; i < result->numPoints() - 1; i++ ) diff --git a/src/core/geometry/qgsinternalgeometryengine.cpp b/src/core/geometry/qgsinternalgeometryengine.cpp index 0769253dafd..b69991b6e6a 100644 --- a/src/core/geometry/qgsinternalgeometryengine.cpp +++ b/src/core/geometry/qgsinternalgeometryengine.cpp @@ -290,7 +290,7 @@ QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision, bool dotProductWithinAngleTolerance( double dotProduct, double lowerThreshold, double upperThreshold ) { - return lowerThreshold > qAbs( dotProduct ) || qAbs( dotProduct ) > upperThreshold; + return lowerThreshold > fabs( dotProduct ) || fabs( dotProduct ) > upperThreshold; } double normalizedDotProduct( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c ) @@ -341,7 +341,7 @@ double squareness( QgsLineString *ring, double lowerThreshold, double upperThres if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) ) continue; - sum += 2.0 * qMin( qAbs( dotProduct - 1.0 ), qMin( qAbs( dotProduct ), qAbs( dotProduct + 1 ) ) ); + sum += 2.0 * qMin( fabs( dotProduct - 1.0 ), qMin( fabs( dotProduct ), fabs( dotProduct + 1 ) ) ); } a = b; b = c; diff --git a/src/core/geometry/qgsregularpolygon.cpp b/src/core/geometry/qgsregularpolygon.cpp index cc984b899c5..ef2db585517 100644 --- a/src/core/geometry/qgsregularpolygon.cpp +++ b/src/core/geometry/qgsregularpolygon.cpp @@ -46,13 +46,13 @@ QgsRegularPolygon::QgsRegularPolygon( const QgsPoint ¢er, const double radiu { case InscribedCircle: { - mRadius = qAbs( radius ); + mRadius = fabs( radius ); mFirstVertex = mCenter.project( mRadius, azimuth ); break; } case CircumscribedCircle: { - mRadius = apothemToRadius( qAbs( radius ), numSides ); + mRadius = apothemToRadius( fabs( radius ), numSides ); mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 ); break; } @@ -118,7 +118,7 @@ QgsRegularPolygon::QgsRegularPolygon( const QgsPoint &pt1, const QgsPoint &pt2, mCenter = pt1.project( hypothenuse, azimuth + angle ); mFirstVertex = pt1; - mRadius = qAbs( hypothenuse ); + mRadius = fabs( hypothenuse ); } } @@ -152,7 +152,7 @@ void QgsRegularPolygon::setCenter( const QgsPoint ¢er ) void QgsRegularPolygon::setRadius( const double radius ) { - mRadius = qAbs( radius ); + mRadius = fabs( radius ); double azimuth = mCenter.azimuth( mFirstVertex ); // TODO: double inclination = mCenter.inclination(mFirstVertex); mFirstVertex = mCenter.project( mRadius, azimuth ); diff --git a/src/core/layout/qgslayoutsnapper.cpp b/src/core/layout/qgslayoutsnapper.cpp index f7e6da14d84..0e307b66a5d 100644 --- a/src/core/layout/qgslayoutsnapper.cpp +++ b/src/core/layout/qgslayoutsnapper.cpp @@ -128,7 +128,7 @@ double QgsLayoutSnapper::snapPointToGuides( double original, QgsLayoutGuide::Ori Q_FOREACH ( QgsLayoutGuide *guide, mLayout->guides().guides( orientation ) ) { double guidePos = guide->layoutPosition(); - double diff = qAbs( original - guidePos ); + double diff = fabs( original - guidePos ); if ( diff < smallestDiff ) { smallestDiff = diff; diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 93eb746201d..9c2c70e2962 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -720,7 +720,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList 1 (lower for longer segments) - double segmentAngleCost = 1 - qAbs( fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2; // 0 -> 1, lower for more horizontal segments + double segmentAngleCost = 1 - fabs( fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2; // 0 -> 1, lower for more horizontal segments while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment ) { @@ -745,7 +745,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList 1 + double costCenter = 2 * fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment ); // 0 -> 1 cost += costCenter * 0.0005; // < 0, 0.0005 > if ( !closedLine ) @@ -753,7 +753,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList 1 + double costLineCenter = 2 * fabs( labelCenter - middleOfLine ) / totalLineLength; // 0 -> 1 cost += costLineCenter * 0.0005; // < 0, 0.0005 > } @@ -901,7 +901,7 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList & } // penalize positions which are further from the line's midpoint - double costCenter = qAbs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength; // <0, 0.5> + double costCenter = fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength; // <0, 0.5> cost += costCenter / 1000; // < 0, 0.0005 > cost += initialCost; @@ -1241,7 +1241,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos // penalize positions which are further from the line's midpoint double labelCenter = i + getLabelWidth() / 2; - double costCenter = qAbs( total_distance / 2 - labelCenter ) / total_distance; // <0, 0.5> + double costCenter = fabs( total_distance / 2 - labelCenter ) / total_distance; // <0, 0.5> cost += costCenter / 1000; // < 0, 0.0005 > slp->setCost( cost ); diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index d82f3bf0652..27a640fb5e5 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -371,7 +371,7 @@ void PointSet::splitPolygons( QLinkedList &shapes_toProcess, // lookup for the deepest point in the hole for ( i = ips; i != cHull[ihn]; i = ( i + 1 ) % nbp ) { - cp = qAbs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]], + cp = fabs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]], x[cHull[ihn]], y[cHull[ihn]], x[i], y[i] ) ); if ( cp - bestcp > EPSILON ) @@ -444,7 +444,7 @@ void PointSet::splitPolygons( QLinkedList &shapes_toProcess, fx = cx + dx; fy = cy - dy; - if ( seg_length < EPSILON || qAbs( ( b = GeomFunction::cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j + if ( seg_length < EPSILON || fabs( ( b = GeomFunction::cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j { if ( ( ex = GeomFunction::dist_euc2d_sq( x[i], y[i], x[retainedPt], y[retainedPt] ) ) < ( ey = GeomFunction::dist_euc2d_sq( x[j], y[j], x[retainedPt], y[retainedPt] ) ) ) { diff --git a/src/core/qgsclipper.h b/src/core/qgsclipper.h index 433324b137a..e967b376a0b 100644 --- a/src/core/qgsclipper.h +++ b/src/core/qgsclipper.h @@ -395,7 +395,7 @@ inline QgsPointXY QgsClipper::intersect( const double x1, const double y1, QgsPointXY p; - if ( qAbs( r_d ) > SMALL_NUM && qAbs( r_n ) > SMALL_NUM ) + if ( fabs( r_d ) > SMALL_NUM && fabs( r_n ) > SMALL_NUM ) { // they cross double r = r_n / r_d; @@ -405,7 +405,7 @@ inline QgsPointXY QgsClipper::intersect( const double x1, const double y1, { // Should never get here, but if we do for some reason, cause a // clunk because something else is wrong if we do. - Q_ASSERT( qAbs( r_d ) > SMALL_NUM && qAbs( r_n ) > SMALL_NUM ); + Q_ASSERT( fabs( r_d ) > SMALL_NUM && fabs( r_n ) > SMALL_NUM ); } return p; diff --git a/src/core/qgscoordinatereferencesystem.cpp b/src/core/qgscoordinatereferencesystem.cpp index cc765e2df96..653647a1596 100644 --- a/src/core/qgscoordinatereferencesystem.cpp +++ b/src/core/qgscoordinatereferencesystem.cpp @@ -1164,7 +1164,7 @@ void QgsCoordinateReferenceSystem::setMapUnits() static const double FEET_TO_METER = 0.3048; static const double SMALL_NUM = 1e-3; - if ( qAbs( toMeter - FEET_TO_METER ) < SMALL_NUM ) + if ( fabs( toMeter - FEET_TO_METER ) < SMALL_NUM ) unit = QStringLiteral( "Foot" ); if ( qgsDoubleNear( toMeter, 1.0 ) ) //Unit name for meters would be "metre" diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 7b5680d2b22..7bcb21b3b07 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -442,7 +442,7 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( sigma = ( distance / ( b * A ) ) + delta_sigma; i++; } - while ( i < 999 && qAbs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); + while ( i < 999 && fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); lat2 = atan2( ( sin( u1 ) * cos( sigma ) + cos( u1 ) * sin( sigma ) * cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + @@ -571,7 +571,7 @@ double QgsDistanceArea::computeDistanceBearing( double tu2 = 0; int iterLimit = 20; - while ( qAbs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 ) + while ( fabs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 ) { sinLambda = sin( lambda ); cosLambda = cos( lambda ); @@ -741,7 +741,7 @@ double QgsDistanceArea::computePolygonArea( const QList &points ) co dx = x2 - x1; dy = y2 - y1; - if ( qAbs( dy ) > thresh ) + if ( fabs( dy ) > thresh ) { /* account for different latitudes y1, y2 */ area += dx * ( m_Qp - ( Qbar2 - Qbar1 ) / dy ); @@ -797,7 +797,7 @@ double QgsDistanceArea::computePolygonFlatArea( const QList &points } // QgsDebugMsg("Area from point: " + (points[i % size]).toString(2)); area = area / 2.0; - return qAbs( area ); // All areas are positive! + return fabs( area ); // All areas are positive! } QString QgsDistanceArea::formatDistance( double distance, int decimals, QgsUnitTypes::DistanceUnit unit, bool keepBaseUnit ) diff --git a/src/core/qgsmapsettings.cpp b/src/core/qgsmapsettings.cpp index 2c1b4b145dd..3919741956a 100644 --- a/src/core/qgsmapsettings.cpp +++ b/src/core/qgsmapsettings.cpp @@ -143,8 +143,8 @@ void QgsMapSettings::updateDerived() { // Use abs() on the extent to avoid the case where the extent is // symmetrical about 0. - double xMean = ( qAbs( extent.xMinimum() ) + qAbs( extent.xMaximum() ) ) * 0.5; - double yMean = ( qAbs( extent.yMinimum() ) + qAbs( extent.yMaximum() ) ) * 0.5; + double xMean = ( fabs( extent.xMinimum() ) + fabs( extent.xMaximum() ) ) * 0.5; + double yMean = ( fabs( extent.yMinimum() ) + fabs( extent.yMaximum() ) ) * 0.5; double xRange = extent.width() / xMean; double yRange = extent.height() / yMean; diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 989506e0db8..f18b0dad51f 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -1171,8 +1171,8 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, QString t #if 0 // XXX strk QgsPointXY ptSize = xform->toMapCoordinatesF( w, h ); - labelX = qAbs( ptSize.x() - ptZero.x() ); - labelY = qAbs( ptSize.y() - ptZero.y() ); + labelX = fabs( ptSize.x() - ptZero.x() ); + labelY = fabs( ptSize.y() - ptZero.y() ); #else double uPP = xform->mapUnitsPerPixel(); labelX = w * uPP; @@ -1224,7 +1224,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont // scales closer than 1:1 if ( maxScale < 0 ) { - maxScale = 1 / qAbs( maxScale ); + maxScale = 1 / fabs( maxScale ); } if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() < maxScale ) @@ -1239,7 +1239,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont // scales closer than 1:1 if ( minScale < 0 ) { - minScale = 1 / qAbs( minScale ); + minScale = 1 / fabs( minScale ); } if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() > minScale ) @@ -1436,7 +1436,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont } } // make sure maxcharangleout is always negative - maxcharangleout = -( qAbs( maxcharangleout ) ); + maxcharangleout = -( fabs( maxcharangleout ) ); } // data defined centroid whole or clipped? diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index 3f09997f5b7..6a85e7a1dd5 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -84,13 +84,13 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix myWrappedY = myWrappedY + 180.0; } - int myDegreesX = int( qAbs( myWrappedX ) ); - double myFloatMinutesX = double( ( qAbs( myWrappedX ) - myDegreesX ) * 60 ); + int myDegreesX = int( fabs( myWrappedX ) ); + double myFloatMinutesX = double( ( fabs( myWrappedX ) - myDegreesX ) * 60 ); int myIntMinutesX = int( myFloatMinutesX ); double mySecondsX = double( myFloatMinutesX - myIntMinutesX ) * 60; - int myDegreesY = int( qAbs( myWrappedY ) ); - double myFloatMinutesY = double( ( qAbs( myWrappedY ) - myDegreesY ) * 60 ); + int myDegreesY = int( fabs( myWrappedY ) ); + double myFloatMinutesY = double( ( fabs( myWrappedY ) - myDegreesY ) * 60 ); int myIntMinutesY = int( myFloatMinutesY ); double mySecondsY = double( myFloatMinutesY - myIntMinutesY ) * 60; @@ -186,11 +186,11 @@ QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const myWrappedX = myWrappedX + 360.0; } - int myDegreesX = int( qAbs( myWrappedX ) ); - double myFloatMinutesX = double( ( qAbs( myWrappedX ) - myDegreesX ) * 60 ); + int myDegreesX = int( fabs( myWrappedX ) ); + double myFloatMinutesX = double( ( fabs( myWrappedX ) - myDegreesX ) * 60 ); - int myDegreesY = int( qAbs( mY ) ); - double myFloatMinutesY = double( ( qAbs( mY ) - myDegreesY ) * 60 ); + int myDegreesY = int( fabs( mY ) ); + double myFloatMinutesY = double( ( fabs( mY ) - myDegreesY ) * 60 ); //make sure rounding to specified precision doesn't create minutes >= 60 if ( std::round( myFloatMinutesX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) diff --git a/src/core/qgsrenderchecker.cpp b/src/core/qgsrenderchecker.cpp index 6c485080703..93d2a1b47c0 100644 --- a/src/core/qgsrenderchecker.cpp +++ b/src/core/qgsrenderchecker.cpp @@ -381,8 +381,8 @@ bool QgsRenderChecker::compareImages( const QString &testName, { qDebug( "Test image and result image for %s are different dimensions", testName.toLocal8Bit().constData() ); - if ( qAbs( myExpectedImage.width() - myResultImage.width() ) > mMaxSizeDifferenceX || - qAbs( myExpectedImage.height() - myResultImage.height() ) > mMaxSizeDifferenceY ) + if ( abs( myExpectedImage.width() - myResultImage.width() ) > mMaxSizeDifferenceX || + abs( myExpectedImage.height() - myResultImage.height() ) > mMaxSizeDifferenceY ) { mReport += QLatin1String( "" ); mReport += "Expected image and result image for " + testName + " are different dimensions - FAILING!"; @@ -438,10 +438,10 @@ bool QgsRenderChecker::compareImages( const QString &testName, } else { - if ( qAbs( qRed( myExpectedPixel ) - qRed( myActualPixel ) ) > pixelTolerance || - qAbs( qGreen( myExpectedPixel ) - qGreen( myActualPixel ) ) > pixelTolerance || - qAbs( qBlue( myExpectedPixel ) - qBlue( myActualPixel ) ) > pixelTolerance || - qAbs( qAlpha( myExpectedPixel ) - qAlpha( myActualPixel ) ) > pixelTolerance ) + if ( abs( qRed( myExpectedPixel ) - qRed( myActualPixel ) ) > pixelTolerance || + abs( qGreen( myExpectedPixel ) - qGreen( myActualPixel ) ) > pixelTolerance || + abs( qBlue( myExpectedPixel ) - qBlue( myActualPixel ) ) > pixelTolerance || + abs( qAlpha( myExpectedPixel ) - qAlpha( myActualPixel ) ) > pixelTolerance ) { ++mMismatchCount; diffScanline[ x ] = qRgb( 255, 0, 0 ); diff --git a/src/core/qgstestutils.h b/src/core/qgstestutils.h index c1b0d9fb016..1d38a7c576c 100644 --- a/src/core/qgstestutils.h +++ b/src/core/qgstestutils.h @@ -29,7 +29,7 @@ bool _xxxresult = qgsDoubleNear( value, expected, epsilon ); \ if ( !_xxxresult ) \ { \ - qDebug( "Expecting %f got %f (diff %f > %f)", static_cast< double >( expected ), static_cast< double >( value ), qAbs( static_cast< double >( expected ) - value ), static_cast< double >( epsilon ) ); \ + qDebug( "Expecting %f got %f (diff %f > %f)", static_cast< double >( expected ), static_cast< double >( value ), fabs( static_cast< double >( expected ) - value ), static_cast< double >( epsilon ) ); \ } \ QVERIFY( qgsDoubleNear( value, expected, epsilon ) ); \ } @@ -38,7 +38,7 @@ bool _xxxresult = qgsDoubleNear( value, not_expected, epsilon ); \ if ( _xxxresult ) \ { \ - qDebug( "Expecting %f to be differerent from %f (diff %f > %f)", static_cast< double >( value ), static_cast< double >( not_expected ), qAbs( static_cast< double >( not_expected ) - value ), static_cast< double >( epsilon ) ); \ + qDebug( "Expecting %f to be differerent from %f (diff %f > %f)", static_cast< double >( value ), static_cast< double >( not_expected ), fabs( static_cast< double >( not_expected ) - value ), static_cast< double >( epsilon ) ); \ } \ QVERIFY( !qgsDoubleNear( value, not_expected, epsilon ) ); \ } diff --git a/src/core/qgsunittypes.cpp b/src/core/qgsunittypes.cpp index 156c95d66df..471d2f51619 100644 --- a/src/core/qgsunittypes.cpp +++ b/src/core/qgsunittypes.cpp @@ -1383,17 +1383,17 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceMeters; } - else if ( qAbs( distance ) > 1000.0 ) + else if ( fabs( distance ) > 1000.0 ) { result.value = qgsRound( distance / 1000, decimals ); result.unit = QgsUnitTypes::DistanceKilometers; } - else if ( qAbs( distance ) < 0.01 ) + else if ( fabs( distance ) < 0.01 ) { result.value = qgsRound( distance * 1000, decimals ); result.unit = QgsUnitTypes::DistanceMillimeters; } - else if ( qAbs( distance ) < 0.1 ) + else if ( fabs( distance ) < 0.1 ) { result.value = qgsRound( distance * 100, decimals ); @@ -1407,7 +1407,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceKilometers: - if ( keepBaseUnit || qAbs( distance ) >= 1.0 ) + if ( keepBaseUnit || fabs( distance ) >= 1.0 ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceKilometers; @@ -1420,7 +1420,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceFeet: - if ( qAbs( distance ) <= 5280.0 || keepBaseUnit ) + if ( fabs( distance ) <= 5280.0 || keepBaseUnit ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceFeet; @@ -1433,7 +1433,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceYards: - if ( qAbs( distance ) <= 1760.0 || keepBaseUnit ) + if ( fabs( distance ) <= 1760.0 || keepBaseUnit ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceYards; @@ -1446,7 +1446,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceMiles: - if ( qAbs( distance ) >= 1.0 || keepBaseUnit ) + if ( fabs( distance ) >= 1.0 || keepBaseUnit ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceMiles; @@ -1531,12 +1531,12 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaSquareMeters; } - else if ( qAbs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaSquareMeters ) ) + else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaSquareMeters ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMeters, QgsUnitTypes::AreaSquareKilometers ), decimals ); result.unit = QgsUnitTypes::AreaSquareKilometers; } - else if ( qAbs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaHectares, QgsUnitTypes::AreaSquareMeters ) ) + else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaHectares, QgsUnitTypes::AreaSquareMeters ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMeters, QgsUnitTypes::AreaHectares ), decimals ); result.unit = QgsUnitTypes::AreaHectares; @@ -1563,7 +1563,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaSquareFeet; } - else if ( qAbs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareFeet ) ) + else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareFeet ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareFeet, QgsUnitTypes::AreaSquareMiles ), decimals ); result.unit = QgsUnitTypes::AreaSquareMiles; @@ -1583,7 +1583,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaSquareYards; } - else if ( qAbs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareYards ) ) + else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareYards ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareYards, QgsUnitTypes::AreaSquareMiles ), decimals ); result.unit = QgsUnitTypes::AreaSquareMiles; @@ -1610,7 +1610,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaHectares; } - else if ( qAbs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaHectares ) ) + else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaHectares ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaHectares, QgsUnitTypes::AreaSquareKilometers ), decimals ); result.unit = QgsUnitTypes::AreaSquareKilometers; @@ -1630,7 +1630,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaAcres; } - else if ( qAbs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaAcres ) ) + else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaAcres ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaAcres, QgsUnitTypes::AreaSquareMiles ), decimals ); result.unit = QgsUnitTypes::AreaSquareMiles; diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 3817ef69439..1c24ec469e9 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -2472,7 +2472,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer *layer, { QVariant min = layer->minimumValue( i ); QVariant max = layer->maximumValue( i ); - if ( qMax( qAbs( min.toLongLong() ), qAbs( max.toLongLong() ) ) < INT_MAX ) + if ( qMax( llabs( min.toLongLong() ), llabs( max.toLongLong() ) ) < INT_MAX ) { fields[i].setType( QVariant::Int ); } diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp index 5a7a8918bdc..74f2770720f 100644 --- a/src/core/qgsvectorlayerlabeling.cpp +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -465,7 +465,7 @@ void QgsVectorLayerSimpleLabeling::toSld( QDomNode &parent, const QgsStringMap & if ( mSettings->maxCurvedCharAngleIn > 0 || mSettings->maxCurvedCharAngleOut > 0 ) { // SLD has no notion for this, the GeoTools ecosystem can only do a single angle - double angle = qMin( qAbs( mSettings->maxCurvedCharAngleIn ), qAbs( mSettings->maxCurvedCharAngleOut ) ); + double angle = qMin( fabs( mSettings->maxCurvedCharAngleIn ), fabs( mSettings->maxCurvedCharAngleOut ) ); QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "maxAngleDelta" ), qgsDoubleToString( angle ) ); textSymbolizerElement.appendChild( vo ); } diff --git a/src/core/raster/qgscolorrampshader.cpp b/src/core/raster/qgscolorrampshader.cpp index 1202e8369db..99e7a88c213 100644 --- a/src/core/raster/qgscolorrampshader.cpp +++ b/src/core/raster/qgscolorrampshader.cpp @@ -285,7 +285,7 @@ void QgsColorRampShader::classifyColorRamp( const int classes, const int band, c QVector::const_iterator color_it = entryColors.begin(); // calculate a reasonable number of decimals to display - double maxabs = log10( qMax( qAbs( max ), qAbs( min ) ) ); + double maxabs = log10( qMax( fabs( max ), fabs( min ) ) ); int nDecimals = std::round( qMax( 3.0 + maxabs - log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); QList colorRampItems; diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index 5d4fbe6064e..8d47800bb89 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -203,7 +203,7 @@ double QgsRasterChecker::tolerance( double val, int places ) { // float precision is about 7 decimal digits, double about 16 // default places = 6 - return 1. * qPow( 10, std::round( log10( qAbs( val ) ) - places ) ); + return 1. * qPow( 10, std::round( log10( fabs( val ) ) - places ) ); } QString QgsRasterChecker::compareHead() @@ -224,7 +224,7 @@ void QgsRasterChecker::compare( const QString ¶mName, int verifiedVal, int e bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double tolerance ) { // values may be nan - return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( qAbs( verifiedVal - expectedVal ) <= tolerance ); + return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( fabs( verifiedVal - expectedVal ) <= tolerance ); } void QgsRasterChecker::compare( const QString ¶mName, double verifiedVal, double expectedVal, QString &report, bool &ok, double tolerance ) diff --git a/src/core/raster/qgsrasterminmaxorigin.cpp b/src/core/raster/qgsrasterminmaxorigin.cpp index ead3a24f822..8d476a73dc4 100644 --- a/src/core/raster/qgsrasterminmaxorigin.cpp +++ b/src/core/raster/qgsrasterminmaxorigin.cpp @@ -40,9 +40,9 @@ bool QgsRasterMinMaxOrigin::operator ==( const QgsRasterMinMaxOrigin &other ) co return mLimits == other.mLimits && mExtent == other.mExtent && mAccuracy == other.mAccuracy && - qAbs( mCumulativeCutLower - other.mCumulativeCutLower ) < 1e-5 && - qAbs( mCumulativeCutUpper - other.mCumulativeCutUpper ) < 1e-5 && - qAbs( mStdDevFactor - other.mStdDevFactor ) < 1e-5; + fabs( mCumulativeCutLower - other.mCumulativeCutLower ) < 1e-5 && + fabs( mCumulativeCutUpper - other.mCumulativeCutUpper ) < 1e-5 && + fabs( mStdDevFactor - other.mStdDevFactor ) < 1e-5; } QString QgsRasterMinMaxOrigin::limitsString( Limits limits ) diff --git a/src/core/simplify/effectivearea.cpp b/src/core/simplify/effectivearea.cpp index 8a9113c9a83..4863b96ded2 100644 --- a/src/core/simplify/effectivearea.cpp +++ b/src/core/simplify/effectivearea.cpp @@ -43,7 +43,7 @@ static void destroy_minheap( MINHEAP tree ) */ static double triarea2d( const QgsPoint &P1, const QgsPoint &P2, const QgsPoint &P3 ) { - return qAbs( 0.5 * ( ( P1.x() - P2.x() ) * ( P3.y() - P2.y() ) - ( P1.y() - P2.y() ) * ( P3.x() - P2.x() ) ) ); + return fabs( 0.5 * ( ( P1.x() - P2.x() ) * ( P3.y() - P2.y() ) - ( P1.y() - P2.y() ) * ( P3.x() - P2.x() ) ) ); } /** @@ -65,7 +65,7 @@ static double triarea3d( const QgsPoint &P1, const QgsPoint &P2, const QgsPoint cy = az * bx - ax * bz; cz = ax * by - ay * bx; - area = qAbs( 0.5 * ( sqrt( cx * cx + cy * cy + cz * cz ) ) ); + area = fabs( 0.5 * ( sqrt( cx * cx + cy * cy + cz * cz ) ) ); return area; } diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index c69cd237961..01e1539a321 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -395,7 +395,7 @@ QgsSymbolLayer *QgsSimpleFillSymbolLayer::createFromSld( QDomElement &element ) double QgsSimpleFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { double penBleed = context.convertToPainterUnits( mStrokeStyle == Qt::NoPen ? 0 : ( mStrokeWidth / 2.0 ), mStrokeWidthUnit, mStrokeWidthMapUnitScale ); - double offsetBleed = context.convertToPainterUnits( qMax( qAbs( mOffset.x() ), qAbs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return penBleed + offsetBleed; } @@ -916,7 +916,7 @@ QgsGradientFillSymbolLayer *QgsGradientFillSymbolLayer::clone() const double QgsGradientFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - double offsetBleed = context.convertToPainterUnits( qMax( qAbs( mOffset.x() ), qAbs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return offsetBleed; } @@ -1512,7 +1512,7 @@ QgsShapeburstFillSymbolLayer *QgsShapeburstFillSymbolLayer::clone() const double QgsShapeburstFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - double offsetBleed = context.convertToPainterUnits( qMax( qAbs( mOffset.x() ), qAbs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return offsetBleed; } @@ -2600,14 +2600,14 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & lineAngle += 360.; } - height = qAbs( height ); - width = qAbs( width ); + height = abs( height ); + width = abs( width ); outputPixelDist = height * cos( lineAngle * M_PI / 180 ); // Round offset to correspond to one pixel height, otherwise lines may // be shifted on tile border if offset falls close to pixel center - int offsetHeight = std::round( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) ); + int offsetHeight = std::round( fabs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) ); outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 ); } @@ -3749,7 +3749,7 @@ QgsRasterFillSymbolLayer *QgsRasterFillSymbolLayer::clone() const double QgsRasterFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - return context.convertToPainterUnits( qMax( qAbs( mOffset.x() ), qAbs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + return context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); } void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath ) diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 86f947a32a0..1efbac4a39d 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -581,7 +581,7 @@ double QgsSimpleLineSymbolLayer::estimateMaxBleed( const QgsRenderContext &conte else { return context.convertToPainterUnits( ( mWidth / 2.0 ), mWidthUnit, mWidthMapUnitScale ) + - context.convertToPainterUnits( qAbs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); + context.convertToPainterUnits( fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); } } @@ -1236,7 +1236,7 @@ void QgsMarkerLineSymbolLayer::renderOffsetVertexAlongLine( const QPolygonF &poi QPointF previousPoint = points[vertex]; int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 ); int endPoint = distance > 0 ? points.count() - 1 : 0; - double distanceLeft = qAbs( distance ); + double distanceLeft = fabs( distance ); for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement ) { @@ -1604,7 +1604,7 @@ QSet QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext & double QgsMarkerLineSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { return context.convertToPainterUnits( ( mMarker->size() / 2.0 ), mMarker->sizeUnit(), mMarker->sizeMapUnitScale() ) + - context.convertToPainterUnits( qAbs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); + context.convertToPainterUnits( fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); } diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index e2b5baf6f7a..a2fb2792ca3 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3895,7 +3895,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, else { int U = 1; - cell = qMax( qAbs( minimum ), qAbs( maximum ) ); + cell = qMax( fabs( minimum ), fabs( maximum ) ); if ( adjustBias >= 1.5 * h + 0.5 ) { U = 1 + ( 1.0 / ( 1 + h ) ); diff --git a/src/gui/layout/qgslayoutruler.cpp b/src/gui/layout/qgslayoutruler.cpp index b73c78dde40..a74ff53da44 100644 --- a/src/gui/layout/qgslayoutruler.cpp +++ b/src/gui/layout/qgslayoutruler.cpp @@ -376,11 +376,11 @@ QgsLayoutGuide *QgsLayoutRuler::guideAtPoint( QPoint localPoint ) const switch ( mOrientation ) { case Qt::Horizontal: - currentDelta = qAbs( layoutPoint.x() - guide->layoutPosition() ); + currentDelta = fabs( layoutPoint.x() - guide->layoutPosition() ); break; case Qt::Vertical: - currentDelta = qAbs( layoutPoint.y() - guide->layoutPosition() ); + currentDelta = fabs( layoutPoint.y() - guide->layoutPosition() ); break; } if ( currentDelta < minDelta ) diff --git a/src/gui/layout/qgslayoutview.cpp b/src/gui/layout/qgslayoutview.cpp index da8c746da11..2eca4704392 100644 --- a/src/gui/layout/qgslayoutview.cpp +++ b/src/gui/layout/qgslayoutview.cpp @@ -456,7 +456,7 @@ void QgsLayoutView::wheelZoom( QWheelEvent *event ) double zoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble(); // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps - zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * qAbs( event->angleDelta().y() ); + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * fabs( event->angleDelta().y() ); if ( event->modifiers() & Qt::ControlModifier ) { diff --git a/src/gui/layout/qgslayoutviewtool.cpp b/src/gui/layout/qgslayoutviewtool.cpp index a6e39d5c38b..2d981d80c61 100644 --- a/src/gui/layout/qgslayoutviewtool.cpp +++ b/src/gui/layout/qgslayoutviewtool.cpp @@ -31,7 +31,7 @@ bool QgsLayoutViewTool::isClickAndDrag( QPoint startViewPoint, QPoint endViewPoi { int diffX = endViewPoint.x() - startViewPoint.x(); int diffY = endViewPoint.y() - startViewPoint.y(); - if ( qAbs( diffX ) >= 2 || qAbs( diffY ) >= 2 ) + if ( abs( diffX ) >= 2 || abs( diffY ) >= 2 ) { return true; } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 46e7694e9f7..06b51e5a448 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -66,11 +66,11 @@ bool QgsAdvancedDigitizingDockWidget::lineCircleIntersection( const QgsPointXY & const int sgnDy = dy < 0 ? -1 : 1; const double ax = center.x() + ( d * dy + sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); - const double ay = center.y() + ( -d * dx + qAbs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); + const double ay = center.y() + ( -d * dx + fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); const QgsPointXY p1( ax, ay ); const double bx = center.x() + ( d * dy - sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); - const double by = center.y() + ( -d * dx - qAbs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); + const double by = center.y() + ( -d * dx - fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); const QgsPointXY p2( bx, by ); // snap to nearest intersection @@ -656,13 +656,13 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) softAngle -= deltaAngle; } int quo = std::round( softAngle / commonAngle ); - if ( qAbs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES ) + if ( fabs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES ) { // also check the distance in pixel to the line, otherwise it's too sticky at long ranges softAngle = quo * commonAngle ; // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1 - const double dist = qAbs( qCos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) + const double dist = fabs( qCos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) - qSin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) ); if ( dist / mMapCanvas->mapSettings().mapUnitsPerPixel() < SOFT_CONSTRAINT_TOLERANCE_PIXEL ) { @@ -745,7 +745,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) // do not compute intersection if lines are almost parallel // this threshold might be adapted - if ( qAbs( d ) > 0.01 ) + if ( fabs( d ) > 0.01 ) { point.setX( ( ( x3 - x4 ) * ( x1 * y2 - y1 * x2 ) - ( x1 - x2 ) * ( x3 * y4 - y3 * x4 ) ) / d ); point.setY( ( ( y3 - y4 ) * ( x1 * y2 - y1 * x2 ) - ( y1 - y2 ) * ( x3 * y4 - y3 * x4 ) ) / d ); diff --git a/src/gui/qgscomposerview.cpp b/src/gui/qgscomposerview.cpp index 06be24377dc..a7e10e9df9c 100644 --- a/src/gui/qgscomposerview.cpp +++ b/src/gui/qgscomposerview.cpp @@ -753,7 +753,7 @@ void QgsComposerView::mouseReleaseEvent( QMouseEvent *e ) //was this just a click? or a click and drag? bool clickOnly = false; - if ( qAbs( diffX ) < 2 && qAbs( diffY ) < 2 ) + if ( fabs( diffX ) < 2 && fabs( diffY ) < 2 ) { clickOnly = true; } @@ -2015,7 +2015,7 @@ void QgsComposerView::wheelZoom( QWheelEvent *event ) double zoomFactor = mySettings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble(); // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps - zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * qAbs( event->angleDelta().y() ); + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * fabs( event->angleDelta().y() ); if ( event->modifiers() & Qt::ControlModifier ) { diff --git a/src/gui/qgsgradientstopeditor.cpp b/src/gui/qgsgradientstopeditor.cpp index f471a79b6d7..b81692957b5 100644 --- a/src/gui/qgsgradientstopeditor.cpp +++ b/src/gui/qgsgradientstopeditor.cpp @@ -285,7 +285,7 @@ int QgsGradientStopEditor::findClosestStop( int x, int threshold ) const int i = 1; Q_FOREACH ( const QgsGradientStop &stop, mGradient.stops() ) { - currentDiff = qAbs( relativePositionToPoint( stop.offset ) + 1 - x ); + currentDiff = abs( relativePositionToPoint( stop.offset ) + 1 - x ); if ( ( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff ) { closestStop = i; @@ -295,7 +295,7 @@ int QgsGradientStopEditor::findClosestStop( int x, int threshold ) const } //first stop - currentDiff = qAbs( relativePositionToPoint( 0.0 ) + 1 - x ); + currentDiff = abs( relativePositionToPoint( 0.0 ) + 1 - x ); if ( ( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff ) { closestStop = 0; @@ -303,7 +303,7 @@ int QgsGradientStopEditor::findClosestStop( int x, int threshold ) const } //last stop - currentDiff = qAbs( relativePositionToPoint( 1.0 ) + 1 - x ); + currentDiff = abs( relativePositionToPoint( 1.0 ) + 1 - x ); if ( ( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff ) { closestStop = mGradient.count() - 1; diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index fb7b4ba0ac8..fe828b8a55f 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -1091,8 +1091,8 @@ void QgsMapCanvas::keyPressEvent( QKeyEvent *e ) // Don't want to interfer with mouse events QgsRectangle currentExtent = mapSettings().visibleExtent(); - double dx = qAbs( currentExtent.width() / 4 ); - double dy = qAbs( currentExtent.height() / 4 ); + double dx = fabs( currentExtent.width() / 4 ); + double dy = fabs( currentExtent.height() / 4 ); switch ( e->key() ) { @@ -1420,7 +1420,7 @@ void QgsMapCanvas::wheelEvent( QWheelEvent *e ) double zoomFactor = mWheelZoomFactor; // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps - zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * qAbs( e->angleDelta().y() ); + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * fabs( e->angleDelta().y() ); if ( e->modifiers() & Qt::ControlModifier ) { diff --git a/src/gui/qgsmapcanvasannotationitem.cpp b/src/gui/qgsmapcanvasannotationitem.cpp index bc9216057a8..6a7e51ba0e6 100644 --- a/src/gui/qgsmapcanvasannotationitem.cpp +++ b/src/gui/qgsmapcanvasannotationitem.cpp @@ -187,7 +187,7 @@ QgsMapCanvasAnnotationItem::MouseMoveAction QgsMapCanvasAnnotationItem::moveActi int cursorSensitivity = 7; if ( mAnnotation && mAnnotation->hasFixedMapPosition() && - qAbs( itemPos.x() ) < cursorSensitivity && qAbs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin + fabs( itemPos.x() ) < cursorSensitivity && fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin { return MoveMapPosition; } @@ -196,10 +196,10 @@ QgsMapCanvasAnnotationItem::MouseMoveAction QgsMapCanvasAnnotationItem::moveActi QSizeF frameSize = mAnnotation ? mAnnotation->frameSize() : QSizeF( 0, 0 ); bool left, right, up, down; - left = qAbs( itemPos.x() - offset.x() ) < cursorSensitivity; - right = qAbs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity; - up = qAbs( itemPos.y() - offset.y() ) < cursorSensitivity; - down = qAbs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity; + left = fabs( itemPos.x() - offset.x() ) < cursorSensitivity; + right = fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity; + up = fabs( itemPos.y() - offset.y() ) < cursorSensitivity; + down = fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity; if ( left && up ) { diff --git a/src/gui/qgsmaptoolextent.cpp b/src/gui/qgsmaptoolextent.cpp index 118a3e010af..d895883f4aa 100644 --- a/src/gui/qgsmaptoolextent.cpp +++ b/src/gui/qgsmaptoolextent.cpp @@ -47,7 +47,7 @@ void QgsMapToolExtent::canvasMoveEvent( QgsMapMouseEvent *e ) QgsPointXY p = toMapCoordinates( e->pos() ); if ( mRatio.width() > 0 && mRatio.height() > 0 ) { - double width = qAbs( p.x() - mStartPoint.x() ); + double width = fabs( p.x() - mStartPoint.x() ); double height = width * ( mRatio.width() / mRatio.height() ); if ( p.y() - mStartPoint.y() < 0 ) height *= -1; @@ -98,7 +98,7 @@ void QgsMapToolExtent::calculateEndPoint( QgsPointXY &point ) { if ( mRatio.width() > 0 && mRatio.height() > 0 ) { - double width = qAbs( point.x() - mStartPoint.x() ); + double width = fabs( point.x() - mStartPoint.x() ); double height = width * mRatio.height() / mRatio.width(); if ( point.y() - mStartPoint.y() < 0 ) height *= -1; diff --git a/src/gui/qgsscalecombobox.cpp b/src/gui/qgsscalecombobox.cpp index e7817d2a3ce..3bd1145ca5c 100644 --- a/src/gui/qgsscalecombobox.cpp +++ b/src/gui/qgsscalecombobox.cpp @@ -98,7 +98,7 @@ void QgsScaleComboBox::showPopup() { parts = itemText( i ).split( ':' ); nextScale = parts.at( 1 ).toLong( &ok ); - delta = qAbs( currScale - nextScale ); + delta = labs( currScale - nextScale ); if ( delta < min ) { min = delta; diff --git a/src/gui/raster/qgsrasterhistogramwidget.cpp b/src/gui/raster/qgsrasterhistogramwidget.cpp index fcf272f9214..e63e9c0ecdf 100644 --- a/src/gui/raster/qgsrasterhistogramwidget.cpp +++ b/src/gui/raster/qgsrasterhistogramwidget.cpp @@ -1005,7 +1005,7 @@ QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 1 current += diff; if ( current > target ) { - closest = ( qAbs( target - current + diff ) < qAbs( target - current ) ) ? current - diff : current; + closest = ( fabs( target - current + diff ) < fabs( target - current ) ) ? current - diff : current; break; } } diff --git a/src/gui/symbology/qgsgraduatedhistogramwidget.cpp b/src/gui/symbology/qgsgraduatedhistogramwidget.cpp index ffcabc05062..bef17e88ab0 100644 --- a/src/gui/symbology/qgsgraduatedhistogramwidget.cpp +++ b/src/gui/symbology/qgsgraduatedhistogramwidget.cpp @@ -158,11 +158,11 @@ void QgsGraduatedHistogramWidget::findClosestRange( double value, int &closestRa int pressedPixel = mPlot->canvasMap( QwtPlot::xBottom ).transform( value ); for ( int i = 0; i < ranges.count() - 1; ++i ) { - if ( qAbs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance ) + if ( fabs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance ) { closestRangeIndex = i; - minDistance = qAbs( mPressedValue - ranges.at( i ).upperValue() ); - pixelDistance = qAbs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) ); + minDistance = fabs( mPressedValue - ranges.at( i ).upperValue() ); + pixelDistance = fabs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) ); } } } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp index 7749728fb02..a458195fc0f 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp @@ -111,7 +111,7 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart ); - if ( qAbs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() && + if ( fabs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() && QgsGeometryCheckerUtils::pointsFuzzyEqual( part->centroid(), overlapError->location(), QgsGeometryCheckPrecision::reducedTolerance() ) ) { interPart = part; diff --git a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h index 3a9b1212497..34cbb40fe95 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h @@ -38,7 +38,7 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError other->featureId() == featureId() && err->otherId() == otherId() && QgsGeometryCheckerUtils::pointsFuzzyEqual( location(), other->location(), QgsGeometryCheckPrecision::reducedTolerance() ) && - qAbs( value().toDouble() - other->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance(); + fabs( value().toDouble() - other->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance(); } bool closeMatch( QgsGeometryCheckError *other ) const override diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp index 191ebbb908c..29a9f73a1f5 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp @@ -54,7 +54,7 @@ namespace QgsGeometryCheckerUtils static inline double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q ) { - double nom = qAbs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() ); + double nom = fabs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() ); double dx = p2.x() - p1.x(); double dy = p2.y() - p1.y(); return nom / qSqrt( dx * dx + dy * dy ); diff --git a/src/plugins/georeferencer/qgsgeorefdelegates.cpp b/src/plugins/georeferencer/qgsgeorefdelegates.cpp index 0bffc809d15..3004dc3d9d0 100644 --- a/src/plugins/georeferencer/qgsgeorefdelegates.cpp +++ b/src/plugins/georeferencer/qgsgeorefdelegates.cpp @@ -19,6 +19,7 @@ #include "qgsgeorefvalidators.h" #include "qgsgeorefdelegates.h" +#include // ------------------------ QgsNonEditableDelegate ------------------------- // QgsNonEditableDelegate::QgsNonEditableDelegate( QWidget *parent ) @@ -76,7 +77,7 @@ double QgsDmsAndDdDelegate::dmsToDD( const QString &dms ) const { QStringList list = dms.split( ' ' ); QString tmpStr = list.at( 0 ); - double res = qAbs( tmpStr.toDouble() ); + double res = fabs( tmpStr.toDouble() ); tmpStr = list.value( 1 ); if ( !tmpStr.isEmpty() ) diff --git a/src/plugins/georeferencer/qgsgeoreftransform.cpp b/src/plugins/georeferencer/qgsgeoreftransform.cpp index 1ee4a9ebf64..84dbb40d6e9 100644 --- a/src/plugins/georeferencer/qgsgeoreftransform.cpp +++ b/src/plugins/georeferencer/qgsgeoreftransform.cpp @@ -379,8 +379,8 @@ int QgsLinearGeorefTransform::linear_transform( void *pTransformerArg, int bDstT else { // Guard against division by zero - if ( qAbs( t->scaleX ) < std::numeric_limits::epsilon() || - qAbs( t->scaleY ) < std::numeric_limits::epsilon() ) + if ( fabs( t->scaleX ) < std::numeric_limits::epsilon() || + fabs( t->scaleY ) < std::numeric_limits::epsilon() ) { for ( int i = 0; i < nPointCount; ++i ) { @@ -460,7 +460,7 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs else { // Guard against division by zero - if ( qAbs( s ) < std::numeric_limits::epsilon() ) + if ( fabs( s ) < std::numeric_limits::epsilon() ) { for ( int i = 0; i < nPointCount; ++i ) { @@ -602,7 +602,7 @@ bool QgsProjectiveGeorefTransform::updateParametersFromGCPs( const QVector::epsilon() ) + if ( fabs( det ) < 1024.0 * std::numeric_limits::epsilon() ) { mParameters.hasInverse = false; } @@ -654,7 +654,7 @@ int QgsProjectiveGeorefTransform::projective_transform( void *pTransformerArg, i { double Z = x[i] * H[6] + y[i] * H[7] + H[8]; // Projects to infinity? - if ( qAbs( Z ) < 1024.0 * std::numeric_limits::epsilon() ) + if ( fabs( Z ) < 1024.0 * std::numeric_limits::epsilon() ) { panSuccess[i] = false; continue; diff --git a/src/plugins/georeferencer/qgsleastsquares.cpp b/src/plugins/georeferencer/qgsleastsquares.cpp index 2381117c901..ac218fdcd36 100644 --- a/src/plugins/georeferencer/qgsleastsquares.cpp +++ b/src/plugins/georeferencer/qgsleastsquares.cpp @@ -57,8 +57,8 @@ void QgsLeastSquares::linear( const QVector &mapCoords, origin.setX( aX ); origin.setY( aY ); - pixelXSize = qAbs( bX ); - pixelYSize = qAbs( bY ); + pixelXSize = fabs( bX ); + pixelYSize = fabs( bY ); } diff --git a/src/plugins/georeferencer/qgsmapcoordsdialog.cpp b/src/plugins/georeferencer/qgsmapcoordsdialog.cpp index b12976ec40c..5ab6af74d98 100644 --- a/src/plugins/georeferencer/qgsmapcoordsdialog.cpp +++ b/src/plugins/georeferencer/qgsmapcoordsdialog.cpp @@ -137,7 +137,7 @@ double QgsMapCoordsDialog::dmsToDD( const QString &dms ) { QStringList list = dms.split( ' ' ); QString tmpStr = list.at( 0 ); - double res = qAbs( tmpStr.toDouble() ); + double res = fabs( tmpStr.toDouble() ); tmpStr = list.value( 1 ); if ( !tmpStr.isEmpty() ) diff --git a/src/plugins/grass/qgsgrassnewmapset.cpp b/src/plugins/grass/qgsgrassnewmapset.cpp index 6d67ff29e92..f336a69db5d 100644 --- a/src/plugins/grass/qgsgrassnewmapset.cpp +++ b/src/plugins/grass/qgsgrassnewmapset.cpp @@ -1087,7 +1087,7 @@ void QgsGrassNewMapset::drawRegion() double x1 = points[i].x(); double x2 = points[i + 1].x(); - if ( qAbs( x2 - x1 ) > 150 ) + if ( fabs( x2 - x1 ) > 150 ) { if ( x2 < x1 ) { diff --git a/src/plugins/grass/qtermwidget/ColorScheme.cpp b/src/plugins/grass/qtermwidget/ColorScheme.cpp index 46f49ab0031..008399b94fc 100644 --- a/src/plugins/grass/qtermwidget/ColorScheme.cpp +++ b/src/plugins/grass/qtermwidget/ColorScheme.cpp @@ -43,34 +43,34 @@ using namespace Konsole; const ColorEntry ColorScheme::defaultTable[TABLE_COLORS] = - // The following are almost IBM standard color codes, with some slight - // gamma correction for the dim colors to compensate for bright X screens. - // It contains the 8 ansiterm/xterm colors in 2 intensities. +// The following are almost IBM standard color codes, with some slight +// gamma correction for the dim colors to compensate for bright X screens. +// It contains the 8 ansiterm/xterm colors in 2 intensities. { - ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry( -QColor(0xFF,0xFF,0xFF), 1), // Dfore, Dback - ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry( -QColor(0xB2,0x18,0x18), 0), // Black, Red - ColorEntry( QColor(0x18,0xB2,0x18), 0), ColorEntry( -QColor(0xB2,0x68,0x18), 0), // Green, Yellow - ColorEntry( QColor(0x18,0x18,0xB2), 0), ColorEntry( -QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta - ColorEntry( QColor(0x18,0xB2,0xB2), 0), ColorEntry( -QColor(0xB2,0xB2,0xB2), 0), // Cyan, White - // intensive - ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry( -QColor(0xFF,0xFF,0xFF), 1), - ColorEntry( QColor(0x68,0x68,0x68), 0), ColorEntry( -QColor(0xFF,0x54,0x54), 0), - ColorEntry( QColor(0x54,0xFF,0x54), 0), ColorEntry( -QColor(0xFF,0xFF,0x54), 0), - ColorEntry( QColor(0x54,0x54,0xFF), 0), ColorEntry( -QColor(0xFF,0x54,0xFF), 0), - ColorEntry( QColor(0x54,0xFF,0xFF), 0), ColorEntry( -QColor(0xFF,0xFF,0xFF), 0) + ColorEntry( QColor( 0x00, 0x00, 0x00 ), 0 ), ColorEntry( + QColor( 0xFF, 0xFF, 0xFF ), 1 ), // Dfore, Dback + ColorEntry( QColor( 0x00, 0x00, 0x00 ), 0 ), ColorEntry( + QColor( 0xB2, 0x18, 0x18 ), 0 ), // Black, Red + ColorEntry( QColor( 0x18, 0xB2, 0x18 ), 0 ), ColorEntry( + QColor( 0xB2, 0x68, 0x18 ), 0 ), // Green, Yellow + ColorEntry( QColor( 0x18, 0x18, 0xB2 ), 0 ), ColorEntry( + QColor( 0xB2, 0x18, 0xB2 ), 0 ), // Blue, Magenta + ColorEntry( QColor( 0x18, 0xB2, 0xB2 ), 0 ), ColorEntry( + QColor( 0xB2, 0xB2, 0xB2 ), 0 ), // Cyan, White + // intensive + ColorEntry( QColor( 0x00, 0x00, 0x00 ), 0 ), ColorEntry( + QColor( 0xFF, 0xFF, 0xFF ), 1 ), + ColorEntry( QColor( 0x68, 0x68, 0x68 ), 0 ), ColorEntry( + QColor( 0xFF, 0x54, 0x54 ), 0 ), + ColorEntry( QColor( 0x54, 0xFF, 0x54 ), 0 ), ColorEntry( + QColor( 0xFF, 0xFF, 0x54 ), 0 ), + ColorEntry( QColor( 0x54, 0x54, 0xFF ), 0 ), ColorEntry( + QColor( 0xFF, 0x54, 0xFF ), 0 ), + ColorEntry( QColor( 0x54, 0xFF, 0xFF ), 0 ), ColorEntry( + QColor( 0xFF, 0xFF, 0xFF ), 0 ) }; -const char* const ColorScheme::colorNames[TABLE_COLORS] = +const char *const ColorScheme::colorNames[TABLE_COLORS] = { "Foreground", "Background", @@ -95,298 +95,298 @@ const char* const ColorScheme::colorNames[TABLE_COLORS] = }; // dummy silently comment out the tr_NOOP #define tr_NOOP -const char* const ColorScheme::translatedColorNames[TABLE_COLORS] = +const char *const ColorScheme::translatedColorNames[TABLE_COLORS] = { - tr_NOOP("Foreground"), - tr_NOOP("Background"), - tr_NOOP("Color 1"), - tr_NOOP("Color 2"), - tr_NOOP("Color 3"), - tr_NOOP("Color 4"), - tr_NOOP("Color 5"), - tr_NOOP("Color 6"), - tr_NOOP("Color 7"), - tr_NOOP("Color 8"), - tr_NOOP("Foreground (Intense)"), - tr_NOOP("Background (Intense)"), - tr_NOOP("Color 1 (Intense)"), - tr_NOOP("Color 2 (Intense)"), - tr_NOOP("Color 3 (Intense)"), - tr_NOOP("Color 4 (Intense)"), - tr_NOOP("Color 5 (Intense)"), - tr_NOOP("Color 6 (Intense)"), - tr_NOOP("Color 7 (Intense)"), - tr_NOOP("Color 8 (Intense)") + tr_NOOP( "Foreground" ), + tr_NOOP( "Background" ), + tr_NOOP( "Color 1" ), + tr_NOOP( "Color 2" ), + tr_NOOP( "Color 3" ), + tr_NOOP( "Color 4" ), + tr_NOOP( "Color 5" ), + tr_NOOP( "Color 6" ), + tr_NOOP( "Color 7" ), + tr_NOOP( "Color 8" ), + tr_NOOP( "Foreground (Intense)" ), + tr_NOOP( "Background (Intense)" ), + tr_NOOP( "Color 1 (Intense)" ), + tr_NOOP( "Color 2 (Intense)" ), + tr_NOOP( "Color 3 (Intense)" ), + tr_NOOP( "Color 4 (Intense)" ), + tr_NOOP( "Color 5 (Intense)" ), + tr_NOOP( "Color 6 (Intense)" ), + tr_NOOP( "Color 7 (Intense)" ), + tr_NOOP( "Color 8 (Intense)" ) }; ColorScheme::ColorScheme() { - _table = 0; - _randomTable = 0; - _opacity = 1.0; + _table = 0; + _randomTable = 0; + _opacity = 1.0; } -ColorScheme::ColorScheme(const ColorScheme& other) - : _opacity(other._opacity) - ,_table(0) - ,_randomTable(0) +ColorScheme::ColorScheme( const ColorScheme &other ) + : _opacity( other._opacity ) + , _table( 0 ) + , _randomTable( 0 ) { - setName(other.name()); - setDescription(other.description()); + setName( other.name() ); + setDescription( other.description() ); - if ( other._table != 0 ) - { - for ( int i = 0 ; i < TABLE_COLORS ; i++ ) - setColorTableEntry(i,other._table[i]); - } + if ( other._table != 0 ) + { + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + setColorTableEntry( i, other._table[i] ); + } - if ( other._randomTable != 0 ) + if ( other._randomTable != 0 ) + { + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) { - for ( int i = 0 ; i < TABLE_COLORS ; i++ ) - { - const RandomizationRange& range = other._randomTable[i]; - setRandomizationRange(i,range.hue,range.saturation,range.value); - } + const RandomizationRange &range = other._randomTable[i]; + setRandomizationRange( i, range.hue, range.saturation, range.value ); } + } } ColorScheme::~ColorScheme() { - delete[] _table; - delete[] _randomTable; + delete[] _table; + delete[] _randomTable; } -void ColorScheme::setDescription(const QString& description) { _description = description; } +void ColorScheme::setDescription( const QString &description ) { _description = description; } QString ColorScheme::description() const { return _description; } -void ColorScheme::setName(const QString& name) { _name = name; } +void ColorScheme::setName( const QString &name ) { _name = name; } QString ColorScheme::name() const { return _name; } -void ColorScheme::setColorTableEntry(int index , const ColorEntry& entry) +void ColorScheme::setColorTableEntry( int index, const ColorEntry &entry ) { - Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); - if ( !_table ) - { - _table = new ColorEntry[TABLE_COLORS]; + if ( !_table ) + { + _table = new ColorEntry[TABLE_COLORS]; - for (int i=0;i= 0 && index < TABLE_COLORS ); + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); - if ( randomSeed != 0 ) - qsrand(randomSeed); + if ( randomSeed != 0 ) + qsrand( randomSeed ); - ColorEntry entry = colorTable()[index]; + ColorEntry entry = colorTable()[index]; - if ( randomSeed != 0 && - _randomTable != 0 && - !_randomTable[index].isNull() ) - { - const RandomizationRange& range = _randomTable[index]; + if ( randomSeed != 0 && + _randomTable != 0 && + !_randomTable[index].isNull() ) + { + const RandomizationRange &range = _randomTable[index]; - int hueDifference = range.hue ? (qrand() % range.hue) - range.hue/2 : 0; - int saturationDifference = range.saturation ? (qrand() % range.saturation) - range.saturation/2 : 0; - int valueDifference = range.value ? (qrand() % range.value) - range.value/2 : 0; + int hueDifference = range.hue ? ( qrand() % range.hue ) - range.hue / 2 : 0; + int saturationDifference = range.saturation ? ( qrand() % range.saturation ) - range.saturation / 2 : 0; + int valueDifference = range.value ? ( qrand() % range.value ) - range.value / 2 : 0; - QColor& color = entry.color; + QColor &color = entry.color; - int newHue = qAbs( (color.hue() + hueDifference) % MAX_HUE ); - int newValue = qMin( qAbs(color.value() + valueDifference) , 255 ); - int newSaturation = qMin( qAbs(color.saturation() + saturationDifference) , 255 ); + int newHue = abs( ( color.hue() + hueDifference ) % MAX_HUE ); + int newValue = qMin( abs( color.value() + valueDifference ), 255 ); + int newSaturation = qMin( abs( color.saturation() + saturationDifference ), 255 ); - color.setHsv(newHue,newSaturation,newValue); - } + color.setHsv( newHue, newSaturation, newValue ); + } - return entry; + return entry; } -void ColorScheme::getColorTable(ColorEntry* table , uint randomSeed) const +void ColorScheme::getColorTable( ColorEntry *table, uint randomSeed ) const { - for ( int i = 0 ; i < TABLE_COLORS ; i++ ) - table[i] = colorEntry(i,randomSeed); + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + table[i] = colorEntry( i, randomSeed ); } bool ColorScheme::randomizedBackgroundColor() const { - return _randomTable == 0 ? false : !_randomTable[1].isNull(); + return _randomTable == 0 ? false : !_randomTable[1].isNull(); } -void ColorScheme::setRandomizedBackgroundColor(bool randomize) +void ColorScheme::setRandomizedBackgroundColor( bool randomize ) { - // the hue of the background colour is allowed to be randomly - // adjusted as much as possible. - // - // the value and saturation are left alone to maintain read-ability - if ( randomize ) - { - setRandomizationRange( 1 /* background color index */ , MAX_HUE , 255 , 0 ); - } - else - { - if ( _randomTable ) - setRandomizationRange( 1 /* background color index */ , 0 , 0 , 0 ); - } + // the hue of the background colour is allowed to be randomly + // adjusted as much as possible. + // + // the value and saturation are left alone to maintain read-ability + if ( randomize ) + { + setRandomizationRange( 1 /* background color index */, MAX_HUE, 255, 0 ); + } + else + { + if ( _randomTable ) + setRandomizationRange( 1 /* background color index */, 0, 0, 0 ); + } } -void ColorScheme::setRandomizationRange( int index , quint16 hue , quint8 saturation , - quint8 value ) +void ColorScheme::setRandomizationRange( int index, quint16 hue, quint8 saturation, + quint8 value ) { - Q_ASSERT( hue <= MAX_HUE ); - Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + Q_ASSERT( hue <= MAX_HUE ); + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); - if ( _randomTable == 0 ) - _randomTable = new RandomizationRange[TABLE_COLORS]; + if ( _randomTable == 0 ) + _randomTable = new RandomizationRange[TABLE_COLORS]; - _randomTable[index].hue = hue; - _randomTable[index].value = value; - _randomTable[index].saturation = saturation; + _randomTable[index].hue = hue; + _randomTable[index].value = value; + _randomTable[index].saturation = saturation; } -const ColorEntry* ColorScheme::colorTable() const +const ColorEntry *ColorScheme::colorTable() const { - if ( _table ) - return _table; - else - return defaultTable; + if ( _table ) + return _table; + else + return defaultTable; } QColor ColorScheme::foregroundColor() const { - return colorTable()[0].color; + return colorTable()[0].color; } QColor ColorScheme::backgroundColor() const { - return colorTable()[1].color; + return colorTable()[1].color; } bool ColorScheme::hasDarkBackground() const { - // value can range from 0 - 255, with larger values indicating higher brightness. - // so 127 is in the middle, anything less is deemed 'dark' - return backgroundColor().value() < 127; + // value can range from 0 - 255, with larger values indicating higher brightness. + // so 127 is in the middle, anything less is deemed 'dark' + return backgroundColor().value() < 127; } -void ColorScheme::setOpacity(qreal opacity) { _opacity = opacity; } +void ColorScheme::setOpacity( qreal opacity ) { _opacity = opacity; } qreal ColorScheme::opacity() const { return _opacity; } -void ColorScheme::read(const QString & fileName) +void ColorScheme::read( const QString &fileName ) { - QSettings s(fileName, QSettings::IniFormat); - s.beginGroup(QStringLiteral("General")); + QSettings s( fileName, QSettings::IniFormat ); + s.beginGroup( QStringLiteral( "General" ) ); - _description = s.value(QStringLiteral("Description"), QObject::tr("Un-named Color Scheme")).toString(); - _opacity = s.value(QStringLiteral("Opacity"),qreal(1.0)).toDouble(); - s.endGroup(); + _description = s.value( QStringLiteral( "Description" ), QObject::tr( "Un-named Color Scheme" ) ).toString(); + _opacity = s.value( QStringLiteral( "Opacity" ), qreal( 1.0 ) ).toDouble(); + s.endGroup(); - for (int i=0 ; i < TABLE_COLORS ; i++) - { - readColorEntry(&s, i); - } + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + { + readColorEntry( &s, i ); + } } #if 0 // implemented upstream - user apps -void ColorScheme::read(KConfig& config) +void ColorScheme::read( KConfig &config ) { - KConfigGroup configGroup = config.group("General"); + KConfigGroup configGroup = config.group( "General" ); - QString description = configGroup.readEntry("Description", QObject::tr("Un-named Color Scheme")); + QString description = configGroup.readEntry( "Description", QObject::tr( "Un-named Color Scheme" ) ); - _description = tr(description.toUtf8()); - _opacity = configGroup.readEntry("Opacity",qreal(1.0)); + _description = tr( description.toUtf8() ); + _opacity = configGroup.readEntry( "Opacity", qreal( 1.0 ) ); - for (int i=0 ; i < TABLE_COLORS ; i++) - { - readColorEntry(config,i); - } + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + { + readColorEntry( config, i ); + } } -void ColorScheme::write(KConfig& config) const +void ColorScheme::write( KConfig &config ) const { - KConfigGroup configGroup = config.group("General"); + KConfigGroup configGroup = config.group( "General" ); - configGroup.writeEntry("Description",_description); - configGroup.writeEntry("Opacity",_opacity); + configGroup.writeEntry( "Description", _description ); + configGroup.writeEntry( "Opacity", _opacity ); - for (int i=0 ; i < TABLE_COLORS ; i++) - { - RandomizationRange random = _randomTable != 0 ? _randomTable[i] : RandomizationRange(); - writeColorEntry(config,colorNameForIndex(i),colorTable()[i],random); - } + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + { + RandomizationRange random = _randomTable != 0 ? _randomTable[i] : RandomizationRange(); + writeColorEntry( config, colorNameForIndex( i ), colorTable()[i], random ); + } } #endif -QString ColorScheme::colorNameForIndex(int index) +QString ColorScheme::colorNameForIndex( int index ) { - Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); - return QString(colorNames[index]); + return QString( colorNames[index] ); } -QString ColorScheme::translatedColorNameForIndex(int index) +QString ColorScheme::translatedColorNameForIndex( int index ) { - Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); - return translatedColorNames[index]; + return translatedColorNames[index]; } -void ColorScheme::readColorEntry(QSettings * s , int index) +void ColorScheme::readColorEntry( QSettings *s, int index ) { - s->beginGroup(colorNameForIndex(index)); + s->beginGroup( colorNameForIndex( index ) ); - ColorEntry entry; + ColorEntry entry; - QStringList rgbList = s->value(QStringLiteral("Color"), QStringList()).toStringList(); - if (rgbList.count() != 3) - { - Q_ASSERT(0); - } - int r, g, b; - r = rgbList[0].toInt(); - g = rgbList[1].toInt(); - b = rgbList[2].toInt(); - entry.color = QColor(r, g, b); + QStringList rgbList = s->value( QStringLiteral( "Color" ), QStringList() ).toStringList(); + if ( rgbList.count() != 3 ) + { + Q_ASSERT( 0 ); + } + int r, g, b; + r = rgbList[0].toInt(); + g = rgbList[1].toInt(); + b = rgbList[2].toInt(); + entry.color = QColor( r, g, b ); - entry.transparent = s->value(QStringLiteral("Transparent"),false).toBool(); + entry.transparent = s->value( QStringLiteral( "Transparent" ), false ).toBool(); - // Deprecated key from KDE 4.0 which set 'Bold' to true to force - // a color to be bold or false to use the current format - // - // TODO - Add a new tri-state key which allows for bold, normal or - // current format - if (s->contains(QStringLiteral("Bold"))) - entry.fontWeight = s->value(QStringLiteral("Bold"),false).toBool() ? ColorEntry::Bold : - ColorEntry::UseCurrentFormat; + // Deprecated key from KDE 4.0 which set 'Bold' to true to force + // a color to be bold or false to use the current format + // + // TODO - Add a new tri-state key which allows for bold, normal or + // current format + if ( s->contains( QStringLiteral( "Bold" ) ) ) + entry.fontWeight = s->value( QStringLiteral( "Bold" ), false ).toBool() ? ColorEntry::Bold : + ColorEntry::UseCurrentFormat; - quint16 hue = s->value(QStringLiteral("MaxRandomHue"),0).toInt(); - quint8 value = s->value(QStringLiteral("MaxRandomValue"),0).toInt(); - quint8 saturation = s->value(QStringLiteral("MaxRandomSaturation"),0).toInt(); + quint16 hue = s->value( QStringLiteral( "MaxRandomHue" ), 0 ).toInt(); + quint8 value = s->value( QStringLiteral( "MaxRandomValue" ), 0 ).toInt(); + quint8 saturation = s->value( QStringLiteral( "MaxRandomSaturation" ), 0 ).toInt(); - setColorTableEntry( index , entry ); + setColorTableEntry( index, entry ); - if ( hue != 0 || value != 0 || saturation != 0 ) - setRandomizationRange( index , hue , saturation , value ); + if ( hue != 0 || value != 0 || saturation != 0 ) + setRandomizationRange( index, hue, saturation, value ); - s->endGroup(); + s->endGroup(); } #if 0 // implemented upstream - user apps -void ColorScheme::writeColorEntry(KConfig& config , const QString& colorName, const ColorEntry& entry , const RandomizationRange& random) const +void ColorScheme::writeColorEntry( KConfig &config, const QString &colorName, const ColorEntry &entry, const RandomizationRange &random ) const { - KConfigGroup configGroup(&config,colorName); + KConfigGroup configGroup( &config, colorName ); - configGroup.writeEntry("Color",entry.color); - configGroup.writeEntry("Transparency",(bool)entry.transparent); - if (entry.fontWeight != ColorEntry::UseCurrentFormat) - { - configGroup.writeEntry("Bold",entry.fontWeight == ColorEntry::Bold); - } + configGroup.writeEntry( "Color", entry.color ); + configGroup.writeEntry( "Transparency", ( bool )entry.transparent ); + if ( entry.fontWeight != ColorEntry::UseCurrentFormat ) + { + configGroup.writeEntry( "Bold", entry.fontWeight == ColorEntry::Bold ); + } - // record randomization if this color has randomization or - // if one of the keys already exists - if ( !random.isNull() || configGroup.hasKey("MaxRandomHue") ) - { - configGroup.writeEntry("MaxRandomHue",(int)random.hue); - configGroup.writeEntry("MaxRandomValue",(int)random.value); - configGroup.writeEntry("MaxRandomSaturation",(int)random.saturation); - } + // record randomization if this color has randomization or + // if one of the keys already exists + if ( !random.isNull() || configGroup.hasKey( "MaxRandomHue" ) ) + { + configGroup.writeEntry( "MaxRandomHue", ( int )random.hue ); + configGroup.writeEntry( "MaxRandomValue", ( int )random.value ); + configGroup.writeEntry( "MaxRandomSaturation", ( int )random.saturation ); + } } #endif @@ -418,371 +418,371 @@ void ColorScheme::writeColorEntry(KConfig& config , const QString& colorName, co // -- Robert Knight 21/07/2007 // AccessibleColorScheme::AccessibleColorScheme() - : ColorScheme() + : ColorScheme() { #if 0 // It's not finished in konsole and it breaks Qt4 compilation as well - // basic attributes - setName("accessible"); - setDescription(QObject::tr("Accessible Color Scheme")); + // basic attributes + setName( "accessible" ); + setDescription( QObject::tr( "Accessible Color Scheme" ) ); - // setup colors - const int ColorRoleCount = 8; + // setup colors + const int ColorRoleCount = 8; - const KColorScheme colorScheme(QPalette::Active); + const KColorScheme colorScheme( QPalette::Active ); - QBrush colors[ColorRoleCount] = - { - colorScheme.foreground( colorScheme.NormalText ), - colorScheme.background( colorScheme.NormalBackground ), + QBrush colors[ColorRoleCount] = + { + colorScheme.foreground( colorScheme.NormalText ), + colorScheme.background( colorScheme.NormalBackground ), - colorScheme.foreground( colorScheme.InactiveText ), - colorScheme.foreground( colorScheme.ActiveText ), - colorScheme.foreground( colorScheme.LinkText ), - colorScheme.foreground( colorScheme.VisitedText ), - colorScheme.foreground( colorScheme.NegativeText ), - colorScheme.foreground( colorScheme.NeutralText ) - }; + colorScheme.foreground( colorScheme.InactiveText ), + colorScheme.foreground( colorScheme.ActiveText ), + colorScheme.foreground( colorScheme.LinkText ), + colorScheme.foreground( colorScheme.VisitedText ), + colorScheme.foreground( colorScheme.NegativeText ), + colorScheme.foreground( colorScheme.NeutralText ) + }; - for ( int i = 0 ; i < TABLE_COLORS ; i++ ) - { - ColorEntry entry; - entry.color = colors[ i % ColorRoleCount ].color(); + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + { + ColorEntry entry; + entry.color = colors[ i % ColorRoleCount ].color(); - setColorTableEntry( i , entry ); - } + setColorTableEntry( i, entry ); + } #endif } -KDE3ColorSchemeReader::KDE3ColorSchemeReader( QIODevice* device ) : - _device(device) +KDE3ColorSchemeReader::KDE3ColorSchemeReader( QIODevice *device ) : + _device( device ) { } -ColorScheme* KDE3ColorSchemeReader::read() +ColorScheme *KDE3ColorSchemeReader::read() { - Q_ASSERT( _device->openMode() == QIODevice::ReadOnly || - _device->openMode() == QIODevice::ReadWrite ); + Q_ASSERT( _device->openMode() == QIODevice::ReadOnly || + _device->openMode() == QIODevice::ReadWrite ); - ColorScheme* scheme = new ColorScheme(); + ColorScheme *scheme = new ColorScheme(); - QRegExp comment("#.*$"); - while ( !_device->atEnd() ) + QRegExp comment( "#.*$" ); + while ( !_device->atEnd() ) + { + QString line( _device->readLine() ); + line.remove( comment ); + line = line.simplified(); + + if ( line.isEmpty() ) + continue; + + if ( line.startsWith( QLatin1String( "color" ) ) ) { - QString line(_device->readLine()); - line.remove(comment); - line = line.simplified(); - - if ( line.isEmpty() ) - continue; - - if ( line.startsWith(QLatin1String("color")) ) - { - if (!readColorLine(line,scheme)) - qDebug() << "Failed to read KDE 3 color scheme line" << line; - } - else if ( line.startsWith(QLatin1String("title")) ) - { - if (!readTitleLine(line,scheme)) - qDebug() << "Failed to read KDE 3 color scheme title line" << line; - } - else - { - qDebug() << "KDE 3 color scheme contains an unsupported feature, '" << - line << "'"; - } + if ( !readColorLine( line, scheme ) ) + qDebug() << "Failed to read KDE 3 color scheme line" << line; } + else if ( line.startsWith( QLatin1String( "title" ) ) ) + { + if ( !readTitleLine( line, scheme ) ) + qDebug() << "Failed to read KDE 3 color scheme title line" << line; + } + else + { + qDebug() << "KDE 3 color scheme contains an unsupported feature, '" << + line << "'"; + } + } - return scheme; + return scheme; } -bool KDE3ColorSchemeReader::readColorLine(const QString& line,ColorScheme* scheme) +bool KDE3ColorSchemeReader::readColorLine( const QString &line, ColorScheme *scheme ) { - QStringList list = line.split(QChar(' ')); + QStringList list = line.split( QChar( ' ' ) ); - if (list.count() != 7) - return false; - if (list.first() != QLatin1String("color")) - return false; + if ( list.count() != 7 ) + return false; + if ( list.first() != QLatin1String( "color" ) ) + return false; - int index = list[1].toInt(); - int red = list[2].toInt(); - int green = list[3].toInt(); - int blue = list[4].toInt(); - int transparent = list[5].toInt(); - int bold = list[6].toInt(); + int index = list[1].toInt(); + int red = list[2].toInt(); + int green = list[3].toInt(); + int blue = list[4].toInt(); + int transparent = list[5].toInt(); + int bold = list[6].toInt(); - const int MAX_COLOR_VALUE = 255; + const int MAX_COLOR_VALUE = 255; - if( (index < 0 || index >= TABLE_COLORS ) - || (red < 0 || red > MAX_COLOR_VALUE ) - || (blue < 0 || blue > MAX_COLOR_VALUE ) - || (green < 0 || green > MAX_COLOR_VALUE ) - || (transparent != 0 && transparent != 1 ) - || (bold != 0 && bold != 1) ) - return false; + if ( ( index < 0 || index >= TABLE_COLORS ) + || ( red < 0 || red > MAX_COLOR_VALUE ) + || ( blue < 0 || blue > MAX_COLOR_VALUE ) + || ( green < 0 || green > MAX_COLOR_VALUE ) + || ( transparent != 0 && transparent != 1 ) + || ( bold != 0 && bold != 1 ) ) + return false; - ColorEntry entry; - entry.color = QColor(red,green,blue); - entry.transparent = ( transparent != 0 ); - entry.fontWeight = ( bold != 0 ) ? ColorEntry::Bold : ColorEntry::UseCurrentFormat; + ColorEntry entry; + entry.color = QColor( red, green, blue ); + entry.transparent = ( transparent != 0 ); + entry.fontWeight = ( bold != 0 ) ? ColorEntry::Bold : ColorEntry::UseCurrentFormat; - scheme->setColorTableEntry(index,entry); - return true; + scheme->setColorTableEntry( index, entry ); + return true; } -bool KDE3ColorSchemeReader::readTitleLine(const QString& line,ColorScheme* scheme) +bool KDE3ColorSchemeReader::readTitleLine( const QString &line, ColorScheme *scheme ) { - if( !line.startsWith(QLatin1String("title")) ) - return false; + if ( !line.startsWith( QLatin1String( "title" ) ) ) + return false; - int spacePos = line.indexOf(' '); - if( spacePos == -1 ) - return false; + int spacePos = line.indexOf( ' ' ); + if ( spacePos == -1 ) + return false; - QString description = line.mid(spacePos+1); + QString description = line.mid( spacePos + 1 ); - scheme->setDescription(description.toUtf8()); - return true; + scheme->setDescription( description.toUtf8() ); + return true; } ColorSchemeManager::ColorSchemeManager() - : _haveLoadedAll(false) + : _haveLoadedAll( false ) { } ColorSchemeManager::~ColorSchemeManager() { - QHashIterator iter(_colorSchemes); - while (iter.hasNext()) - { - iter.next(); - delete iter.value(); - } + QHashIterator iter( _colorSchemes ); + while ( iter.hasNext() ) + { + iter.next(); + delete iter.value(); + } } void ColorSchemeManager::loadAllColorSchemes() { - qDebug() << "loadAllColorSchemes"; - int success = 0; - int failed = 0; + qDebug() << "loadAllColorSchemes"; + int success = 0; + int failed = 0; - QList nativeColorSchemes = listColorSchemes(); + QList nativeColorSchemes = listColorSchemes(); - QListIterator nativeIter(nativeColorSchemes); - while ( nativeIter.hasNext() ) - { - if ( loadColorScheme( nativeIter.next() ) ) - success++; - else - failed++; - } - - QList kde3ColorSchemes = listKDE3ColorSchemes(); - QListIterator kde3Iter(kde3ColorSchemes); - while ( kde3Iter.hasNext() ) - { - if ( loadKDE3ColorScheme( kde3Iter.next() ) ) - success++; - else - failed++; - } - - if ( failed > 0 ) - qDebug() << "failed to load " << failed << " color schemes."; - - _haveLoadedAll = true; -} -QList ColorSchemeManager::allColorSchemes() -{ - if ( !_haveLoadedAll ) - { - loadAllColorSchemes(); - } - - return _colorSchemes.values(); -} -bool ColorSchemeManager::loadKDE3ColorScheme(const QString& filePath) -{ - QFile file(filePath); - if (!filePath.endsWith(QLatin1String(".schema")) || !file.open(QIODevice::ReadOnly)) - return false; - - KDE3ColorSchemeReader reader(&file); - ColorScheme* scheme = reader.read(); - scheme->setName(QFileInfo(file).baseName()); - file.close(); - - if (scheme->name().isEmpty()) - { - qDebug() << "color scheme name is not valid."; - delete scheme; - return false; - } - - QFileInfo info(filePath); - - if ( !_colorSchemes.contains(info.baseName()) ) - _colorSchemes.insert(scheme->name(),scheme); + QListIterator nativeIter( nativeColorSchemes ); + while ( nativeIter.hasNext() ) + { + if ( loadColorScheme( nativeIter.next() ) ) + success++; else - { - qDebug() << "color scheme with name" << scheme->name() << "has already been" << - "found, ignoring."; - delete scheme; - } + failed++; + } - return true; + QList kde3ColorSchemes = listKDE3ColorSchemes(); + QListIterator kde3Iter( kde3ColorSchemes ); + while ( kde3Iter.hasNext() ) + { + if ( loadKDE3ColorScheme( kde3Iter.next() ) ) + success++; + else + failed++; + } + + if ( failed > 0 ) + qDebug() << "failed to load " << failed << " color schemes."; + + _haveLoadedAll = true; +} +QList ColorSchemeManager::allColorSchemes() +{ + if ( !_haveLoadedAll ) + { + loadAllColorSchemes(); + } + + return _colorSchemes.values(); +} +bool ColorSchemeManager::loadKDE3ColorScheme( const QString &filePath ) +{ + QFile file( filePath ); + if ( !filePath.endsWith( QLatin1String( ".schema" ) ) || !file.open( QIODevice::ReadOnly ) ) + return false; + + KDE3ColorSchemeReader reader( &file ); + ColorScheme *scheme = reader.read(); + scheme->setName( QFileInfo( file ).baseName() ); + file.close(); + + if ( scheme->name().isEmpty() ) + { + qDebug() << "color scheme name is not valid."; + delete scheme; + return false; + } + + QFileInfo info( filePath ); + + if ( !_colorSchemes.contains( info.baseName() ) ) + _colorSchemes.insert( scheme->name(), scheme ); + else + { + qDebug() << "color scheme with name" << scheme->name() << "has already been" << + "found, ignoring."; + delete scheme; + } + + return true; } #if 0 -void ColorSchemeManager::addColorScheme(ColorScheme* scheme) +void ColorSchemeManager::addColorScheme( ColorScheme *scheme ) { - _colorSchemes.insert(scheme->name(),scheme); + _colorSchemes.insert( scheme->name(), scheme ); - // save changes to disk - QString path = KGlobal::dirs()->saveLocation("data","konsole/") + scheme->name() + ".colorscheme"; - KConfig config(path , KConfig::NoGlobals); + // save changes to disk + QString path = KGlobal::dirs()->saveLocation( "data", "konsole/" ) + scheme->name() + ".colorscheme"; + KConfig config( path, KConfig::NoGlobals ); - scheme->write(config); + scheme->write( config ); } #endif -bool ColorSchemeManager::loadCustomColorScheme(const QString& path) +bool ColorSchemeManager::loadCustomColorScheme( const QString &path ) { - if (path.endsWith(QLatin1String(".colorscheme"))) - return loadColorScheme(path); - else if (path.endsWith(QLatin1String(".schema"))) - return loadKDE3ColorScheme(path); - else - return false; + if ( path.endsWith( QLatin1String( ".colorscheme" ) ) ) + return loadColorScheme( path ); + else if ( path.endsWith( QLatin1String( ".schema" ) ) ) + return loadKDE3ColorScheme( path ); + else + return false; } -bool ColorSchemeManager::loadColorScheme(const QString& filePath) +bool ColorSchemeManager::loadColorScheme( const QString &filePath ) { - if ( !filePath.endsWith(QLatin1String(".colorscheme")) || !QFile::exists(filePath) ) - return false; + if ( !filePath.endsWith( QLatin1String( ".colorscheme" ) ) || !QFile::exists( filePath ) ) + return false; - QFileInfo info(filePath); + QFileInfo info( filePath ); - const QString& schemeName = info.baseName(); + const QString &schemeName = info.baseName(); - ColorScheme* scheme = new ColorScheme(); - scheme->setName(schemeName); - scheme->read(filePath); + ColorScheme *scheme = new ColorScheme(); + scheme->setName( schemeName ); + scheme->read( filePath ); - if (scheme->name().isEmpty()) - { - qDebug() << "Color scheme in" << filePath << "does not have a valid name and was not loaded."; - delete scheme; - return false; - } + if ( scheme->name().isEmpty() ) + { + qDebug() << "Color scheme in" << filePath << "does not have a valid name and was not loaded."; + delete scheme; + return false; + } - if ( !_colorSchemes.contains(schemeName) ) - { - _colorSchemes.insert(schemeName,scheme); - } - else - { - qDebug() << "color scheme with name" << schemeName << "has already been" << - "found, ignoring."; + if ( !_colorSchemes.contains( schemeName ) ) + { + _colorSchemes.insert( schemeName, scheme ); + } + else + { + qDebug() << "color scheme with name" << schemeName << "has already been" << + "found, ignoring."; - delete scheme; - } + delete scheme; + } - return true; + return true; } QList ColorSchemeManager::listKDE3ColorSchemes() { - QString dname(get_color_schemes_dir()); - QDir dir(dname); - QStringList filters; - filters << QStringLiteral("*.schema"); - dir.setNameFilters(filters); - QStringList list = dir.entryList(filters); - QStringList ret; - foreach(QString i, list) - ret << dname + "/" + i; - return ret; - //return KGlobal::dirs()->findAllResources("data", - // "konsole/*.schema", - // KStandardDirs::NoDuplicates); - // + QString dname( get_color_schemes_dir() ); + QDir dir( dname ); + QStringList filters; + filters << QStringLiteral( "*.schema" ); + dir.setNameFilters( filters ); + QStringList list = dir.entryList( filters ); + QStringList ret; + foreach ( QString i, list ) + ret << dname + "/" + i; + return ret; + //return KGlobal::dirs()->findAllResources("data", + // "konsole/*.schema", + // KStandardDirs::NoDuplicates); + // } QList ColorSchemeManager::listColorSchemes() { - QString dname(get_color_schemes_dir()); - QDir dir(dname); - QStringList filters; - filters << QStringLiteral("*.colorscheme"); - dir.setNameFilters(filters); - QStringList list = dir.entryList(filters); - QStringList ret; - foreach(QString i, list) - ret << dname + "/" + i; - return ret; + QString dname( get_color_schemes_dir() ); + QDir dir( dname ); + QStringList filters; + filters << QStringLiteral( "*.colorscheme" ); + dir.setNameFilters( filters ); + QStringList list = dir.entryList( filters ); + QStringList ret; + foreach ( QString i, list ) + ret << dname + "/" + i; + return ret; // return KGlobal::dirs()->findAllResources("data", // "konsole/*.colorscheme", // KStandardDirs::NoDuplicates); } const ColorScheme ColorSchemeManager::_defaultColorScheme; -const ColorScheme* ColorSchemeManager::defaultColorScheme() const +const ColorScheme *ColorSchemeManager::defaultColorScheme() const { - return &_defaultColorScheme; + return &_defaultColorScheme; } -bool ColorSchemeManager::deleteColorScheme(const QString& name) +bool ColorSchemeManager::deleteColorScheme( const QString &name ) { - Q_ASSERT( _colorSchemes.contains(name) ); + Q_ASSERT( _colorSchemes.contains( name ) ); - // lookup the path and delete - QString path = findColorSchemePath(name); - if ( QFile::remove(path) ) - { - _colorSchemes.remove(name); - return true; - } - else - { - qDebug() << "Failed to remove color scheme -" << path; - return false; - } + // lookup the path and delete + QString path = findColorSchemePath( name ); + if ( QFile::remove( path ) ) + { + _colorSchemes.remove( name ); + return true; + } + else + { + qDebug() << "Failed to remove color scheme -" << path; + return false; + } } -QString ColorSchemeManager::findColorSchemePath(const QString& name) const +QString ColorSchemeManager::findColorSchemePath( const QString &name ) const { // QString path = KStandardDirs::locate("data","konsole/"+name+".colorscheme"); - QString path(get_color_schemes_dir() + "/"+ name + ".colorscheme"); - if ( !path.isEmpty() ) - return path; - - //path = KStandardDirs::locate("data","konsole/"+name+".schema"); - path = get_color_schemes_dir() + "/"+ name + ".schema"; - + QString path( get_color_schemes_dir() + "/" + name + ".colorscheme" ); + if ( !path.isEmpty() ) return path; -} -const ColorScheme* ColorSchemeManager::findColorScheme(const QString& name) -{ - if ( name.isEmpty() ) - return defaultColorScheme(); - if ( _colorSchemes.contains(name) ) - return _colorSchemes[name]; + //path = KStandardDirs::locate("data","konsole/"+name+".schema"); + path = get_color_schemes_dir() + "/" + name + ".schema"; + + return path; +} +const ColorScheme *ColorSchemeManager::findColorScheme( const QString &name ) +{ + if ( name.isEmpty() ) + return defaultColorScheme(); + + if ( _colorSchemes.contains( name ) ) + return _colorSchemes[name]; + else + { + // look for this color scheme + QString path = findColorSchemePath( name ); + if ( !path.isEmpty() && loadColorScheme( path ) ) + { + return findColorScheme( name ); + } else { - // look for this color scheme - QString path = findColorSchemePath(name); - if ( !path.isEmpty() && loadColorScheme(path) ) - { - return findColorScheme(name); - } - else - { - if (!path.isEmpty() && loadKDE3ColorScheme(path)) - return findColorScheme(name); - } - - qDebug() << "Could not find color scheme - " << name; - - return 0; + if ( !path.isEmpty() && loadKDE3ColorScheme( path ) ) + return findColorScheme( name ); } + + qDebug() << "Could not find color scheme - " << name; + + return 0; + } } -ColorSchemeManager* ColorSchemeManager::sColorSchemeManager = 0; +ColorSchemeManager *ColorSchemeManager::sColorSchemeManager = 0; //K_GLOBAL_STATIC( ColorSchemeManager , colorSchemeManager ) -ColorSchemeManager* ColorSchemeManager::instance() +ColorSchemeManager *ColorSchemeManager::instance() { - if (! sColorSchemeManager) - sColorSchemeManager = new ColorSchemeManager(); - return sColorSchemeManager; + if ( ! sColorSchemeManager ) + sColorSchemeManager = new ColorSchemeManager(); + return sColorSchemeManager; } diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 8863a854240..03d9fee4ad2 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -528,7 +528,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi int srcBottom = ySize() - 1; int srcRight = xSize() - 1; - // Note: original approach for xRes < srcXRes || yRes < qAbs( srcYRes ) was to avoid + // Note: original approach for xRes < srcXRes || yRes < fabs( srcYRes ) was to avoid // second resampling and read with GDALRasterIO to another temporary data block // extended to fit src grid. The problem was that with src resolution much bigger // than dst res, the target could become very large @@ -1291,8 +1291,8 @@ bool QgsGdalProvider::hasHistogram( int bandNo, // min/max are stored as text in aux file => use threshold if ( myBinCount != myHistogram.binCount || - qAbs( myMinVal - myExpectedMinVal ) > qAbs( myExpectedMinVal ) / 10e6 || - qAbs( myMaxVal - myExpectedMaxVal ) > qAbs( myExpectedMaxVal ) / 10e6 ) + fabs( myMinVal - myExpectedMinVal ) > fabs( myExpectedMinVal ) / 10e6 || + fabs( myMaxVal - myExpectedMaxVal ) > fabs( myExpectedMaxVal ) / 10e6 ) { QgsDebugMsg( QString( "Params do not match binCount: %1 x %2, minVal: %3 x %4, maxVal: %5 x %6" ).arg( myBinCount ).arg( myHistogram.binCount ).arg( myMinVal ).arg( myExpectedMinVal ).arg( myMaxVal ).arg( myExpectedMaxVal ) ); return false; diff --git a/src/providers/grass/qgsgrassrasterprovider.cpp b/src/providers/grass/qgsgrassrasterprovider.cpp index 0ff9ef0a373..370acbd8f2b 100644 --- a/src/providers/grass/qgsgrassrasterprovider.cpp +++ b/src/providers/grass/qgsgrassrasterprovider.cpp @@ -123,7 +123,7 @@ QgsGrassRasterProvider::QgsGrassRasterProvider( QString const &uri ) else if ( mGrassDataType == DCELL_TYPE ) { // Don't use numeric limits, raster layer is using - // qAbs( myValue - mNoDataValue ) <= TINY_VALUE + // fabs( myValue - mNoDataValue ) <= TINY_VALUE // if the mNoDataValue would be a limit, the subtraction could overflow. // No data value is shown in GUI, use some nice number. // Choose values with small representation error. diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp index 380b23eae73..527c8441319 100644 --- a/src/providers/wcs/qgswcsprovider.cpp +++ b/src/providers/wcs/qgswcsprovider.cpp @@ -1413,8 +1413,8 @@ QgsRasterIdentifyResult QgsWcsProvider::identify( const QgsPointXY &point, QgsRa point.x() + xRes * width / 2, point.y() + yRes * height / 2 ); - double xResDiff = qAbs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); - double yResDiff = qAbs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); + double xResDiff = fabs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); + double yResDiff = fabs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); if ( !mCachedGdalDataset || !mCachedViewExtent.contains( point ) || @@ -1434,8 +1434,8 @@ QgsRasterIdentifyResult QgsWcsProvider::identify( const QgsPointXY &point, QgsRa double xRes = finalExtent.width() / width; double yRes = finalExtent.height() / height; QgsDebugMsg( QString( "width = %1 height = %2 xRes = %3 yRes = %4" ).arg( finalExtent.width() ).arg( finalExtent.height() ).arg( xRes ).arg( yRes ) ); - double xResDiff = qAbs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); - double yResDiff = qAbs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); + double xResDiff = fabs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); + double yResDiff = fabs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); QgsDebugMsg( QString( "xRes diff = %1 yRes diff = %2 relative xResDiff = %3 relative yResDiff = %4" ).arg( xResDiff ).arg( yResDiff ).arg( xResDiff / xRes ).arg( yResDiff / yRes ) ); if ( !mCachedGdalDataset || !mCachedViewExtent.contains( point ) || diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index 0ce71682196..a98c38d95ab 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -89,8 +89,8 @@ struct LessThanTileRequest QPointF p1 = req1.rect.center(); QPointF p2 = req2.rect.center(); // using chessboard distance (loading order more natural than euclidean/manhattan distance) - double d1 = qMax( qAbs( center.x() - p1.x() ), qAbs( center.y() - p1.y() ) ); - double d2 = qMax( qAbs( center.x() - p2.x() ), qAbs( center.y() - p2.y() ) ); + double d1 = qMax( fabs( center.x() - p1.x() ), fabs( center.y() - p1.y() ) ); + double d2 = qMax( fabs( center.x() - p2.x() ), fabs( center.y() - p2.y() ) ); return d1 < d2; } }; diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp index 9f876248b93..d7dccd81de7 100644 --- a/tests/src/core/testqgsdistancearea.cpp +++ b/tests/src/core/testqgsdistancearea.cpp @@ -206,7 +206,7 @@ void TestQgsDistanceArea::test_distances() // QgsDebugMsg( QString( "Distance from %1 to %2 is %3" ).arg( p1.toString( 15 ) ).arg( p2.toString( 15 ) ).arg( result, 0, 'g', 15 ) ); // QgsDebugMsg( QString( "Distance should be %1" ).arg( myLineList[6] ) ); // Check result is less than 0.5mm from expected. - QVERIFY( qAbs( result - myLineList[6].toDouble() ) < 0.0005 ); + QGSCOMPARENEAR( result, myLineList[6].toDouble(), 0.0005 ); } } diff --git a/tests/src/providers/grass/testqgsgrassprovider.cpp b/tests/src/providers/grass/testqgsgrassprovider.cpp index acf5faad0e1..0047d9a66b6 100644 --- a/tests/src/providers/grass/testqgsgrassprovider.cpp +++ b/tests/src/providers/grass/testqgsgrassprovider.cpp @@ -340,7 +340,7 @@ bool TestQgsGrassProvider::compare( const QStringList &expected, const QStringLi bool TestQgsGrassProvider::compare( double expected, double got, bool &ok ) { - if ( qAbs( got - expected ) > TINY_VALUE ) + if ( fabs( got - expected ) > TINY_VALUE ) { ok = false; return false; From 77c3be97e33e100637537f0167b5fd784639c20c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 02:12:54 +1000 Subject: [PATCH 116/364] Make sure (f)abs are prefixed with std:: --- src/analysis/interpolation/MathUtils.cc | 4 +-- src/analysis/raster/qgsalignraster.cpp | 8 ++--- src/analysis/raster/qgsrelief.cpp | 2 +- src/analysis/vector/qgsgeometrysnapper.cpp | 4 +-- src/app/dwg/libdxfrw/drw_entities.cpp | 10 +++--- src/app/dwg/libdxfrw/drw_objects.cpp | 2 +- src/app/qgsdecorationgrid.cpp | 8 ++--- src/app/qgsdecorationscalebar.cpp | 2 +- src/app/qgslabelinggui.cpp | 2 +- src/app/qgsmaptoolmeasureangle.cpp | 8 ++--- src/app/qgsundowidget.cpp | 2 +- src/core/composer/qgscomposerarrow.cpp | 2 +- src/core/composer/qgscomposermapgrid.cpp | 4 +-- src/core/composer/qgscomposermousehandles.cpp | 12 +++---- src/core/composer/qgscomposernodesitem.cpp | 6 ++-- src/core/composer/qgscomposerpicture.cpp | 4 +-- src/core/composer/qgscomposerutils.cpp | 8 ++--- src/core/composer/qgscomposition.cpp | 4 +-- src/core/dxf/qgsdxfpaintengine.cpp | 2 +- src/core/effects/qgsimageoperation.cpp | 4 +-- src/core/expression/qgsexpressionfunction.cpp | 14 ++++---- src/core/geometry/qgscircle.cpp | 18 +++++------ src/core/geometry/qgscircle.h | 6 ++-- src/core/geometry/qgscurvepolygon.cpp | 4 +-- src/core/geometry/qgsellipse.cpp | 12 +++---- src/core/geometry/qgsgeometry.cpp | 2 +- src/core/geometry/qgsgeometryutils.cpp | 2 +- src/core/geometry/qgsgeos.cpp | 4 +-- .../geometry/qgsinternalgeometryengine.cpp | 4 +-- src/core/geometry/qgsregularpolygon.cpp | 8 ++--- src/core/layout/qgslayoutsnapper.cpp | 6 ++-- src/core/pal/feature.cpp | 12 +++---- src/core/pal/pointset.cpp | 8 ++--- src/core/qgsclipper.h | 4 +-- src/core/qgscoordinatereferencesystem.cpp | 2 +- src/core/qgsdistancearea.cpp | 6 ++-- src/core/qgsmaprendererjob.cpp | 8 ++--- src/core/qgsmapsettings.cpp | 4 +-- src/core/qgspallabeling.cpp | 10 +++--- src/core/qgspointxy.cpp | 16 +++++----- src/core/qgsrenderchecker.cpp | 12 +++---- src/core/qgstestutils.h | 4 +-- src/core/qgstracer.cpp | 2 +- src/core/qgsunittypes.cpp | 26 +++++++-------- src/core/qgsvectorfilewriter.cpp | 2 +- src/core/qgsvectorlayerlabeling.cpp | 2 +- src/core/raster/qgscolorrampshader.cpp | 2 +- src/core/raster/qgsrasterchecker.cpp | 4 +-- src/core/raster/qgsrasterminmaxorigin.cpp | 6 ++-- src/core/simplify/effectivearea.cpp | 2 +- src/core/symbology/qgs25drenderer.cpp | 2 +- src/core/symbology/qgsarrowsymbollayer.cpp | 8 ++--- src/core/symbology/qgsfillsymbollayer.cpp | 14 ++++---- src/core/symbology/qgslinesymbollayer.cpp | 6 ++-- src/core/symbology/qgssymbollayerutils.cpp | 2 +- src/gui/layout/qgslayoutruler.cpp | 4 +-- src/gui/layout/qgslayoutview.cpp | 2 +- src/gui/layout/qgslayoutviewrubberband.cpp | 6 ++-- src/gui/layout/qgslayoutviewtool.cpp | 2 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 12 +++---- src/gui/qgscolorwidgets.cpp | 4 +-- src/gui/qgscomposerview.cpp | 10 +++--- src/gui/qgsgradientstopeditor.cpp | 6 ++-- src/gui/qgsmapcanvas.cpp | 6 ++-- src/gui/qgsmapcanvasannotationitem.cpp | 10 +++--- src/gui/qgsmaptoolextent.cpp | 4 +-- src/gui/qgsscalecombobox.cpp | 2 +- src/gui/raster/qgsrasterhistogramwidget.cpp | 2 +- .../symbology/qgsgraduatedhistogramwidget.cpp | 6 ++-- .../checks/qgsgeometryoverlapcheck.cpp | 2 +- .../checks/qgsgeometryoverlapcheck.h | 2 +- .../utils/qgsgeometrycheckerutils.cpp | 2 +- .../georeferencer/qgsgeorefdelegates.cpp | 2 +- .../georeferencer/qgsgeoreftransform.cpp | 10 +++--- src/plugins/georeferencer/qgsleastsquares.cpp | 4 +-- .../georeferencer/qgsmapcoordsdialog.cpp | 2 +- src/plugins/grass/qgsgrassnewmapset.cpp | 2 +- src/plugins/grass/qtermwidget/ColorScheme.cpp | 6 ++-- .../grass/qtermwidget/TerminalDisplay.cpp | 10 +++--- src/providers/gdal/qgsgdalprovider.cpp | 8 ++--- .../grass/qgsgrassrasterprovider.cpp | 2 +- src/providers/wcs/qgswcsprovider.cpp | 8 ++--- src/providers/wfs/qgswfscapabilities.cpp | 8 ++--- src/providers/wms/qgswmsprovider.cpp | 6 ++-- tests/bench/qgsbench.cpp | 2 +- tests/src/core/testqgscomposermap.cpp | 32 +++++++++---------- tests/src/core/testqgsrasterlayer.cpp | 20 ++++++------ .../providers/grass/testqgsgrassprovider.cpp | 2 +- 88 files changed, 274 insertions(+), 274 deletions(-) diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index 8b7b75bbcea..80d3391440b 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -210,7 +210,7 @@ double MathUtils::distPointFromLine( QgsPoint *thepoint, QgsPoint *p1, QgsPoint double a = normal.getX(); double b = normal.getY(); double c = -( normal.getX() * p2->x() + normal.getY() * p2->y() ); - double distance = fabs( ( a * thepoint->x() + b * thepoint->y() + c ) / ( sqrt( a * a + b * b ) ) ); + double distance = std::fabs( ( a * thepoint->x() + b * thepoint->y() + c ) / ( sqrt( a * a + b * b ) ) ); return distance; } else @@ -471,7 +471,7 @@ double MathUtils::power( double a, int b ) return 1; } double tmp = a; - for ( int i = 2; i <= abs( b ); i++ ) + for ( int i = 2; i <= std::abs( b ); i++ ) { a *= tmp; diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 8b451109de4..8d605143404 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -30,7 +30,7 @@ static double ceil_with_tolerance( double value ) { - if ( fabs( value - std::round( value ) ) < 1e-6 ) + if ( std::fabs( value - std::round( value ) ) < 1e-6 ) return std::round( value ); else return qCeil( value ); @@ -38,7 +38,7 @@ static double ceil_with_tolerance( double value ) static double floor_with_tolerance( double value ) { - if ( fabs( value - std::round( value ) ) < 1e-6 ) + if ( std::fabs( value - std::round( value ) ) < 1e-6 ) return std::round( value ); else return qFloor( value ); @@ -538,7 +538,7 @@ bool QgsAlignRaster::suggestedWarpOutput( const QgsAlignRaster::RasterInfo &info if ( eErr != CE_None ) return false; - QSizeF cs( fabs( adfDstGeoTransform[1] ), fabs( adfDstGeoTransform[5] ) ); + QSizeF cs( std::fabs( adfDstGeoTransform[1] ), std::fabs( adfDstGeoTransform[5] ) ); if ( rect ) *rect = QgsRectangle( extents[0], extents[1], extents[2], extents[3] ); @@ -582,7 +582,7 @@ QgsAlignRaster::RasterInfo::~RasterInfo() QSizeF QgsAlignRaster::RasterInfo::cellSize() const { - return QSizeF( fabs( mGeoTransform[1] ), fabs( mGeoTransform[5] ) ); + return QSizeF( std::fabs( mGeoTransform[1] ), std::fabs( mGeoTransform[5] ) ); } QPointF QgsAlignRaster::RasterInfo::gridOffset() const diff --git a/src/analysis/raster/qgsrelief.cpp b/src/analysis/raster/qgsrelief.cpp index b00f7a13ed4..7803e8b89e9 100644 --- a/src/analysis/raster/qgsrelief.cpp +++ b/src/analysis/raster/qgsrelief.cpp @@ -353,7 +353,7 @@ bool QgsRelief::processNineCellWindow( float *x1, float *x2, float *x3, float *x float aspect = mAspectFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 ); if ( hillShadeValue285 != mOutputNodataValue && aspect != mOutputNodataValue ) { - double angle_diff = fabs( 285 - aspect ); + double angle_diff = std::fabs( 285 - aspect ); if ( angle_diff > 180 ) { angle_diff -= 180; diff --git a/src/analysis/vector/qgsgeometrysnapper.cpp b/src/analysis/vector/qgsgeometrysnapper.cpp index 5ab47eccdd1..e2fb6dbb928 100644 --- a/src/analysis/vector/qgsgeometrysnapper.cpp +++ b/src/analysis/vector/qgsgeometrysnapper.cpp @@ -110,8 +110,8 @@ class Raytracer // See http://playtechs.blogspot.ch/2007/03/raytracing-on-grid.html public: Raytracer( float x0, float y0, float x1, float y1 ) - : m_dx( fabs( x1 - x0 ) ) - , m_dy( fabs( y1 - y0 ) ) + : m_dx( std::fabs( x1 - x0 ) ) + , m_dy( std::fabs( y1 - y0 ) ) , m_x( qFloor( x0 ) ) , m_y( qFloor( y0 ) ) , m_n( 1 ) diff --git a/src/app/dwg/libdxfrw/drw_entities.cpp b/src/app/dwg/libdxfrw/drw_entities.cpp index fbb65138921..9920908774e 100644 --- a/src/app/dwg/libdxfrw/drw_entities.cpp +++ b/src/app/dwg/libdxfrw/drw_entities.cpp @@ -36,7 +36,7 @@ void DRW_Entity::calculateAxis( DRW_Coord extPoint ) { //Follow the arbitrary DXF definitions for extrusion axes. - if ( fabs( extPoint.x ) < 0.015625 && fabs( extPoint.y ) < 0.015625 ) + if ( std::fabs( extPoint.x ) < 0.015625 && std::fabs( extPoint.y ) < 0.015625 ) { //If we get here, implement Ax = Wy x N where Wy is [0,1,0] per the DXF spec. //The cross product works out to Wy.y*N.z-Wy.z*N.y, Wy.z*N.x-Wy.x*N.z, Wy.x*N.y-Wy.y*N.x @@ -797,7 +797,7 @@ void DRW_Arc::applyExtrusion() // Note that the following code only handles the special case where there is a 2D // drawing with the z axis heading into the paper (or rather screen). An arbitrary // extrusion axis (with x and y values greater than 1/64) may still have issues. - if ( fabs( extPoint.x ) < 0.015625 && fabs( extPoint.y ) < 0.015625 && extPoint.z < 0.0 ) + if ( std::fabs( extPoint.x ) < 0.015625 && std::fabs( extPoint.y ) < 0.015625 && extPoint.z < 0.0 ) { staangle = M_PI - staangle; endangle = M_PI - endangle; @@ -902,7 +902,7 @@ void DRW_Ellipse::correctAxis() } if ( ratio > 1 ) { - if ( fabs( endparam - staparam - M_PIx2 ) < 1.0e-10 ) + if ( std::fabs( endparam - staparam - M_PIx2 ) < 1.0e-10 ) complete = true; double incX = secPoint.x; secPoint.x = -( secPoint.y * ratio ); @@ -977,7 +977,7 @@ void DRW_Ellipse::toPolyline( DRW_Polyline *pol, int parts ) const curAngle += incAngle; } - if ( fabs( endAngle - staparam - M_PIx2 ) < 1.0e-10 ) + if ( std::fabs( endAngle - staparam - M_PIx2 ) < 1.0e-10 ) { pol->flags = 1; } @@ -2065,7 +2065,7 @@ bool DRW_Vertex::parseDwg( DRW::Version version, dwgBuffer *buf, duint32 bs, dou stawidth = buf->getBitDouble(); if ( stawidth < 0 ) - endwidth = stawidth = fabs( stawidth ); + endwidth = stawidth = std::fabs( stawidth ); else endwidth = buf->getBitDouble(); bulge = buf->getBitDouble(); diff --git a/src/app/dwg/libdxfrw/drw_objects.cpp b/src/app/dwg/libdxfrw/drw_objects.cpp index 7c4757975c9..217706b30f6 100644 --- a/src/app/dwg/libdxfrw/drw_objects.cpp +++ b/src/app/dwg/libdxfrw/drw_objects.cpp @@ -505,7 +505,7 @@ void DRW_LType::update() size = path.size(); for ( std::vector::size_type i = 0; i < size; i++ ) { - d += fabs( path.at( i ) ); + d += std::fabs( path.at( i ) ); } length = d; } diff --git a/src/app/qgsdecorationgrid.cpp b/src/app/qgsdecorationgrid.cpp index aab776a1a09..1c6f5e852b0 100644 --- a/src/app/qgsdecorationgrid.cpp +++ b/src/app/qgsdecorationgrid.cpp @@ -582,7 +582,7 @@ int QgsDecorationGrid::xGridLines( const QgsMapSettings &mapSettings, QList< QPa QLineF lineWest( mapPolygon[3], mapPolygon[0] ); double len = lineEast.length(); - Q_ASSERT( fabs( len - lineWest.length() ) < 1e-6 ); // no shear + Q_ASSERT( std::fabs( len - lineWest.length() ) < 1e-6 ); // no shear double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0; double dist = static_cast< int >( ( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + roundCorrection ) * mGridIntervalY + mGridOffsetY; @@ -626,7 +626,7 @@ int QgsDecorationGrid::yGridLines( const QgsMapSettings &mapSettings, QList< QPa QLineF lineNorth( mapPolygon[0], mapPolygon[1] ); double len = lineSouth.length(); - Q_ASSERT( fabs( len - lineNorth.length() ) < 1e-6 ); // no shear + Q_ASSERT( std::fabs( len - lineNorth.length() ) < 1e-6 ); // no shear const QRectF &mapBoundingRect = mapPolygon.boundingRect(); double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0; @@ -836,8 +836,8 @@ bool QgsDecorationGrid::getIntervalFromCurrentLayer( double *values ) // TODO add a function in QgsRasterLayer to get x/y resolution from provider, // because this might not be 100% accurate QgsRectangle extent = rlayer->extent(); - values[0] = fabs( extent.xMaximum() - extent.xMinimum() ) / rlayer->width(); - values[1] = fabs( extent.yMaximum() - extent.yMinimum() ) / rlayer->height(); + values[0] = std::fabs( extent.xMaximum() - extent.xMinimum() ) / rlayer->width(); + values[1] = std::fabs( extent.yMaximum() - extent.yMinimum() ) / rlayer->height(); // calculate offset - when using very high resolution rasters in geographic CRS // there seems to be a small shift, but this may be due to rendering issues and depends on zoom diff --git a/src/app/qgsdecorationscalebar.cpp b/src/app/qgsdecorationscalebar.cpp index 0804e07fa07..2a927301598 100644 --- a/src/app/qgsdecorationscalebar.cpp +++ b/src/app/qgsdecorationscalebar.cpp @@ -113,7 +113,7 @@ void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRender //Get map units per pixel. This can be negative at times (to do with //projections) and that just confuses the rest of the code in this //function, so force to a positive number. - double myMapUnitsPerPixelDouble = fabs( context.mapToPixel().mapUnitsPerPixel() ); + double myMapUnitsPerPixelDouble = std::fabs( context.mapToPixel().mapUnitsPerPixel() ); double myActualSize = mPreferredSize; // Exit if the canvas width is 0 or layercount is 0 or QGIS will freeze diff --git a/src/app/qgslabelinggui.cpp b/src/app/qgslabelinggui.cpp index 241ca2bca26..6578738da94 100644 --- a/src/app/qgslabelinggui.cpp +++ b/src/app/qgslabelinggui.cpp @@ -237,7 +237,7 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer ) // curved label max character angles mMaxCharAngleInDSpinBox->setValue( lyr.maxCurvedCharAngleIn ); // lyr.maxCurvedCharAngleOut must be negative, but it is shown as positive spinbox in GUI - mMaxCharAngleOutDSpinBox->setValue( fabs( lyr.maxCurvedCharAngleOut ) ); + mMaxCharAngleOutDSpinBox->setValue( std::fabs( lyr.maxCurvedCharAngleOut ) ); wrapCharacterEdit->setText( lyr.wrapChar ); mFontMultiLineAlignComboBox->setCurrentIndex( ( unsigned int ) lyr.multilineAlign ); diff --git a/src/app/qgsmaptoolmeasureangle.cpp b/src/app/qgsmaptoolmeasureangle.cpp index d41d19be49a..60acc4bae03 100644 --- a/src/app/qgsmaptoolmeasureangle.cpp +++ b/src/app/qgsmaptoolmeasureangle.cpp @@ -64,9 +64,9 @@ void QgsMapToolMeasureAngle::canvasMoveEvent( QgsMapMouseEvent *e ) double azimuthOne = mDa.bearing( mAnglePoints.at( 1 ), mAnglePoints.at( 0 ) ); double azimuthTwo = mDa.bearing( mAnglePoints.at( 1 ), point ); double resultAngle = azimuthTwo - azimuthOne; - QgsDebugMsg( QString::number( fabs( resultAngle ) ) ); + QgsDebugMsg( QString::number( std::fabs( resultAngle ) ) ); QgsDebugMsg( QString::number( M_PI ) ); - if ( fabs( resultAngle ) > M_PI ) + if ( std::fabs( resultAngle ) > M_PI ) { if ( resultAngle < 0 ) { @@ -163,9 +163,9 @@ void QgsMapToolMeasureAngle::updateSettings() double azimuthOne = mDa.bearing( mAnglePoints.at( 1 ), mAnglePoints.at( 0 ) ); double azimuthTwo = mDa.bearing( mAnglePoints.at( 1 ), mAnglePoints.at( 2 ) ); double resultAngle = azimuthTwo - azimuthOne; - QgsDebugMsg( QString::number( fabs( resultAngle ) ) ); + QgsDebugMsg( QString::number( std::fabs( resultAngle ) ) ); QgsDebugMsg( QString::number( M_PI ) ); - if ( fabs( resultAngle ) > M_PI ) + if ( std::fabs( resultAngle ) > M_PI ) { if ( resultAngle < 0 ) { diff --git a/src/app/qgsundowidget.cpp b/src/app/qgsundowidget.cpp index 41dd32a0057..3691de36614 100644 --- a/src/app/qgsundowidget.cpp +++ b/src/app/qgsundowidget.cpp @@ -78,7 +78,7 @@ void QgsUndoWidget::indexChanged( int curIndx ) canRedo = mUndoStack->canRedo(); curCount = mUndoStack->count(); } - int offset = abs( mPreviousIndex - curIndx ); + int offset = std::abs( mPreviousIndex - curIndx ); // when individually redoing, differentiate between last redo and a new command added to stack bool lastRedo = ( mPreviousIndex == ( mPreviousCount - 1 ) && mPreviousCount == curCount && !canRedo ); diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp index ed55288360a..e905053d22d 100644 --- a/src/core/composer/qgscomposerarrow.cpp +++ b/src/core/composer/qgscomposerarrow.cpp @@ -409,7 +409,7 @@ void QgsComposerArrow::adaptItemSceneRect() { //rectangle containing start and end point QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ), - fabs( mStopPoint.x() - mStartPoint.x() ), fabs( mStopPoint.y() - mStartPoint.y() ) ); + std::fabs( mStopPoint.x() - mStartPoint.x() ), std::fabs( mStopPoint.y() - mStartPoint.y() ) ); double enlarge = computeMarkerMargin(); rect.adjust( -enlarge, -enlarge, enlarge, enlarge ); QgsComposerItem::setSceneRect( rect ); diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index 2dfc342b318..bd7a8f9c588 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -1465,11 +1465,11 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGr if ( geographic ) { //insert degree symbol for geographic coordinates - return QString::number( fabs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere; + return QString::number( std::fabs( value ), 'f', mGridAnnotationPrecision ) + QChar( 176 ) + hemisphere; } else { - return QString::number( fabs( value ), 'f', mGridAnnotationPrecision ) + hemisphere; + return QString::number( std::fabs( value ), 'f', mGridAnnotationPrecision ) + hemisphere; } } else if ( mGridAnnotationFormat == CustomFormat ) diff --git a/src/core/composer/qgscomposermousehandles.cpp b/src/core/composer/qgscomposermousehandles.cpp index 3e01aa20027..87f00616f1c 100644 --- a/src/core/composer/qgscomposermousehandles.cpp +++ b/src/core/composer/qgscomposermousehandles.cpp @@ -593,7 +593,7 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent *event double diffY = mouseMoveStopPoint.y() - mMouseMoveStartPos.y(); //it was only a click - if ( fabs( diffX ) < std::numeric_limits::min() && fabs( diffY ) < std::numeric_limits::min() ) + if ( std::fabs( diffX ) < std::numeric_limits::min() && std::fabs( diffY ) < std::numeric_limits::min() ) { mIsDragging = false; mIsResizing = false; @@ -824,7 +824,7 @@ void QgsComposerMouseHandles::dragMouseMove( QPointF currentPosition, bool lockM { //constrained (shift) moving should lock to horizontal/vertical movement //reset the smaller of the x/y movements - if ( fabs( moveRectX ) <= fabs( moveRectY ) ) + if ( std::fabs( moveRectX ) <= std::fabs( moveRectY ) ) { moveRectX = 0; } @@ -1091,7 +1091,7 @@ void QgsComposerMouseHandles::resizeMouseMove( QPointF currentPosition, bool loc mResizeRect = QRectF( QPointF( -( mBeginHandleWidth + rx ), -( mBeginHandleHeight + ry ) ), QPointF( 0, 0 ) ); } - setRect( 0, 0, fabs( mBeginHandleWidth + rx ), fabs( mBeginHandleHeight + ry ) ); + setRect( 0, 0, std::fabs( mBeginHandleWidth + rx ), std::fabs( mBeginHandleHeight + ry ) ); //show current size of selection in status bar mComposition->setStatusMessage( QString( tr( "width: %1 mm height: %2 mm" ) ).arg( rect().width() ).arg( rect().height() ) ); @@ -1270,7 +1270,7 @@ QPointF QgsComposerMouseHandles::alignPos( QPointF pos, double &alignX, double & double alignThreshold = mComposition->snapTolerance() / viewScaleFactor; QPointF result( pos.x(), pos.y() ); - if ( fabs( nearestX - pos.x() ) < alignThreshold ) + if ( std::fabs( nearestX - pos.x() ) < alignThreshold ) { result.setX( nearestX ); alignX = nearestX; @@ -1280,7 +1280,7 @@ QPointF QgsComposerMouseHandles::alignPos( QPointF pos, double &alignX, double & alignX = -1; } - if ( fabs( nearestY - pos.y() ) < alignThreshold ) + if ( std::fabs( nearestY - pos.y() ) < alignThreshold ) { result.setY( nearestY ); alignY = nearestY; @@ -1359,7 +1359,7 @@ void QgsComposerMouseHandles::checkNearestItem( double checkCoord, const QMap< d return; } - double currentDiff = fabs( checkCoord - currentCoord ); + double currentDiff = std::fabs( checkCoord - currentCoord ); //convert snap tolerance from pixels to mm double viewScaleFactor = graphicsView()->transform().m11(); double alignThreshold = mComposition->snapTolerance() / viewScaleFactor; diff --git a/src/core/composer/qgscomposernodesitem.cpp b/src/core/composer/qgscomposernodesitem.cpp index e71ca769eec..fb2597f4d5a 100644 --- a/src/core/composer/qgscomposernodesitem.cpp +++ b/src/core/composer/qgscomposernodesitem.cpp @@ -77,7 +77,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, double distance = std::numeric_limits::max(); if ( qIsInf( coef ) ) - distance = fabs( pt1.x() - start.x() ); + distance = std::fabs( pt1.x() - start.x() ); else { const double coef2 = ( -1 / coef ); @@ -86,7 +86,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, QPointF inter; if ( qIsInf( coef2 ) ) { - distance = fabs( pt1.y() - start.y() ); + distance = std::fabs( pt1.y() - start.y() ); inter.setX( start.x() ); inter.setY( pt1.y() ); } @@ -104,7 +104,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, const double length3 = computeDistance( pt1, pt2 ); const double length4 = length1 + length2; - if ( fabs( length3 - length4 ) < std::numeric_limits::epsilon() ) + if ( std::fabs( length3 - length4 ) < std::numeric_limits::epsilon() ) distance = computeDistance( inter, start ); } diff --git a/src/core/composer/qgscomposerpicture.cpp b/src/core/composer/qgscomposerpicture.cpp index e7b3be32c45..c18c690063c 100644 --- a/src/core/composer/qgscomposerpicture.cpp +++ b/src/core/composer/qgscomposerpicture.cpp @@ -578,8 +578,8 @@ void QgsComposerPicture::setSceneRect( const QRectF &rectangle ) //if height has changed more than width, then fix width and set height correspondingly //else, do the opposite - if ( fabs( rect().width() - rectangle.width() ) < - fabs( rect().height() - rectangle.height() ) ) + if ( std::fabs( rect().width() - rectangle.width() ) < + std::fabs( rect().height() - rectangle.height() ) ) { newRect.setHeight( targetImageSize.height() * newRect.width() / targetImageSize.width() ); } diff --git a/src/core/composer/qgscomposerutils.cpp b/src/core/composer/qgscomposerutils.cpp index aaf1e2a87e7..737e2749ca8 100644 --- a/src/core/composer/qgscomposerutils.cpp +++ b/src/core/composer/qgscomposerutils.cpp @@ -189,8 +189,8 @@ QRectF QgsComposerUtils::largestRotatedRectWithinBounds( const QRectF &originalR double sinAngle = sin( angleRad ); //calculate size of bounds of rotated rectangle - double widthBoundsRotatedRect = originalWidth * fabs( cosAngle ) + originalHeight * fabs( sinAngle ); - double heightBoundsRotatedRect = originalHeight * fabs( cosAngle ) + originalWidth * fabs( sinAngle ); + double widthBoundsRotatedRect = originalWidth * std::fabs( cosAngle ) + originalHeight * std::fabs( sinAngle ); + double heightBoundsRotatedRect = originalHeight * std::fabs( cosAngle ) + originalWidth * std::fabs( sinAngle ); //compare ratio of rotated rect with bounds rect and calculate scaling of rotated //rect to fit within bounds @@ -221,9 +221,9 @@ QRectF QgsComposerUtils::largestRotatedRectWithinBounds( const QRectF &originalR //now calculate offset position of rotated rectangle double offsetX = ratioBoundsRotatedRect > ratioBoundsRect ? 0 : ( boundsWidth - rectScale * widthBoundsRotatedRect ) / 2.0; - offsetX += fabs( minX ); + offsetX += std::fabs( minX ); double offsetY = ratioBoundsRotatedRect > ratioBoundsRect ? ( boundsHeight - rectScale * heightBoundsRotatedRect ) / 2.0 : 0; - offsetY += fabs( minY ); + offsetY += std::fabs( minY ); return QRectF( offsetX, offsetY, rectScaledWidth, rectScaledHeight ); } diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index bd28d613518..bff1bfcf158 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -2047,12 +2047,12 @@ QPointF QgsComposition::snapPointToGrid( QPointF scenePoint ) const double viewScaleFactor = graphicsView()->transform().m11(); double alignThreshold = mSnapTolerance / viewScaleFactor; - if ( fabs( xSnapped - scenePoint.x() ) > alignThreshold ) + if ( std::fabs( xSnapped - scenePoint.x() ) > alignThreshold ) { //snap distance is outside of tolerance xSnapped = scenePoint.x(); } - if ( fabs( ySnapped - scenePoint.y() ) > alignThreshold ) + if ( std::fabs( ySnapped - scenePoint.y() ) > alignThreshold ) { //snap distance is outside of tolerance ySnapped = scenePoint.y(); diff --git a/src/core/dxf/qgsdxfpaintengine.cpp b/src/core/dxf/qgsdxfpaintengine.cpp index 68926fbb3a9..e6ea5bbd62d 100644 --- a/src/core/dxf/qgsdxfpaintengine.cpp +++ b/src/core/dxf/qgsdxfpaintengine.cpp @@ -262,7 +262,7 @@ double QgsDxfPaintEngine::power( double a, int b ) return 1; double tmp = a; - for ( int i = 2; i <= abs( b ); i++ ) + for ( int i = 2; i <= std::abs( b ); i++ ) a *= tmp; if ( b > 0 ) diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index 846675fe59c..0e229039c97 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -889,8 +889,8 @@ QRect QgsImageOperation::nonTransparentImageRect( const QImage &image, QSize min if ( center ) { // recompute min and max to center image - const int dx = qMax( abs( xmax - width / 2 ), abs( xmin - width / 2 ) ); - const int dy = qMax( abs( ymax - height / 2 ), abs( ymin - height / 2 ) ); + const int dx = qMax( std::abs( xmax - width / 2 ), std::abs( xmin - width / 2 ) ); + const int dy = qMax( std::abs( ymax - height / 2 ), std::abs( ymin - height / 2 ) ); xmin = qMax( 0, width / 2 - dx ); xmax = qMin( width, width / 2 + dx ); ymin = qMax( 0, height / 2 - dy ); diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 268ced0c10a..32ea9e8d16d 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -241,7 +241,7 @@ static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( fabs( val ) ); + return QVariant( std::fabs( val ) ); } static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) @@ -1007,13 +1007,13 @@ static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionCont if ( strhit == lasthit || strhit == -1 ) { //if no new backward delimiter found, try to locate forward - strhit = lines[i].indexOf( rx, strcurrent + labs( wrap ) ); + strhit = lines[i].indexOf( rx, strcurrent + std::labs( wrap ) ); } lasthit = strhit; } else { - strhit = lines[i].indexOf( rx, strcurrent + labs( wrap ) ); + strhit = lines[i].indexOf( rx, strcurrent + std::labs( wrap ) ); } if ( strhit > -1 ) { @@ -2656,11 +2656,11 @@ static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionConte { if ( pt1->y() < pt2->y() ) { - return atan( fabs( pt1->x() - pt2->x() ) / fabs( pt1->y() - pt2->y() ) ); + return atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ); } else /* ( pt1->y() > pt2->y() ) - equality case handled above */ { - return atan( fabs( pt1->y() - pt2->y() ) / fabs( pt1->x() - pt2->x() ) ) + return atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI / 2 ); } } @@ -2669,12 +2669,12 @@ static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionConte { if ( pt1->y() > pt2->y() ) { - return atan( fabs( pt1->x() - pt2->x() ) / fabs( pt1->y() - pt2->y() ) ) + return atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI; } else /* ( pt1->y() < pt2->y() ) - equality case handled above */ { - return atan( fabs( pt1->y() - pt2->y() ) / fabs( pt1->x() - pt2->x() ) ) + return atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI / 2 ) ); } } diff --git a/src/core/geometry/qgscircle.cpp b/src/core/geometry/qgscircle.cpp index d098f6c6a74..c65909d174d 100644 --- a/src/core/geometry/qgscircle.cpp +++ b/src/core/geometry/qgscircle.cpp @@ -52,24 +52,24 @@ static bool isPerpendicular( const QgsPoint &pt1, const QgsPoint &pt2, const Qgs double yDelta_b = pt3.y() - pt2.y(); double xDelta_b = pt3.x() - pt2.x(); - if ( ( fabs( xDelta_a ) <= epsilon ) && ( fabs( yDelta_b ) <= epsilon ) ) + if ( ( std::fabs( xDelta_a ) <= epsilon ) && ( std::fabs( yDelta_b ) <= epsilon ) ) { return false; } - if ( fabs( yDelta_a ) <= epsilon ) + if ( std::fabs( yDelta_a ) <= epsilon ) { return true; } - else if ( fabs( yDelta_b ) <= epsilon ) + else if ( std::fabs( yDelta_b ) <= epsilon ) { return true; } - else if ( fabs( xDelta_a ) <= epsilon ) + else if ( std::fabs( xDelta_a ) <= epsilon ) { return true; } - else if ( fabs( xDelta_b ) <= epsilon ) + else if ( std::fabs( xDelta_b ) <= epsilon ) { return true; } @@ -138,7 +138,7 @@ QgsCircle QgsCircle::from3Points( const QgsPoint &pt1, const QgsPoint &pt2, cons double aSlope = yDelta_a / xDelta_a; double bSlope = yDelta_b / xDelta_b; - if ( ( fabs( xDelta_a ) <= epsilon ) && ( fabs( yDelta_b ) <= epsilon ) ) + if ( ( std::fabs( xDelta_a ) <= epsilon ) && ( std::fabs( yDelta_b ) <= epsilon ) ) { center.setX( 0.5 * ( p2.x() + p3.x() ) ); center.setY( 0.5 * ( p1.y() + p2.y() ) ); @@ -147,7 +147,7 @@ QgsCircle QgsCircle::from3Points( const QgsPoint &pt1, const QgsPoint &pt2, cons return QgsCircle( center, radius ); } - if ( fabs( aSlope - bSlope ) <= epsilon ) + if ( std::fabs( aSlope - bSlope ) <= epsilon ) { return QgsCircle(); } @@ -191,8 +191,8 @@ QgsCircle QgsCircle::from3Tangents( const QgsPoint &pt1_tg1, const QgsPoint &pt2 QgsCircle QgsCircle::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) { - double delta_x = fabs( pt1.x() - pt2.x() ); - double delta_y = fabs( pt1.x() - pt2.y() ); + double delta_x = std::fabs( pt1.x() - pt2.x() ); + double delta_y = std::fabs( pt1.x() - pt2.y() ); if ( !qgsDoubleNear( delta_x, delta_y ) ) { return QgsCircle(); diff --git a/src/core/geometry/qgscircle.h b/src/core/geometry/qgscircle.h index 7a233c5a9a1..b15b9d4572c 100644 --- a/src/core/geometry/qgscircle.h +++ b/src/core/geometry/qgscircle.h @@ -133,7 +133,7 @@ class CORE_EXPORT QgsCircle : public QgsEllipse */ void setSemiMajorAxis( const double semiMajorAxis ) override { - mSemiMajorAxis = fabs( semiMajorAxis ); + mSemiMajorAxis = std::fabs( semiMajorAxis ); mSemiMinorAxis = mSemiMajorAxis; } @@ -143,7 +143,7 @@ class CORE_EXPORT QgsCircle : public QgsEllipse */ void setSemiMinorAxis( const double semiMinorAxis ) override { - mSemiMajorAxis = fabs( semiMinorAxis ); + mSemiMajorAxis = std::fabs( semiMinorAxis ); mSemiMinorAxis = mSemiMajorAxis; } @@ -152,7 +152,7 @@ class CORE_EXPORT QgsCircle : public QgsEllipse //! Set the radius of the circle void setRadius( double radius ) { - mSemiMajorAxis = fabs( radius ); + mSemiMajorAxis = std::fabs( radius ); mSemiMinorAxis = mSemiMajorAxis; } diff --git a/src/core/geometry/qgscurvepolygon.cpp b/src/core/geometry/qgscurvepolygon.cpp index 5e1e6d3a414..aef192cfd8c 100644 --- a/src/core/geometry/qgscurvepolygon.cpp +++ b/src/core/geometry/qgscurvepolygon.cpp @@ -370,7 +370,7 @@ double QgsCurvePolygon::area() const { double area = 0.0; mExteriorRing->sumUpArea( area ); - totalArea += fabs( area ); + totalArea += std::fabs( area ); } QList::const_iterator ringIt = mInteriorRings.constBegin(); @@ -380,7 +380,7 @@ double QgsCurvePolygon::area() const if ( ( *ringIt )->isRing() ) { ( *ringIt )->sumUpArea( area ); - totalArea -= fabs( area ); + totalArea -= std::fabs( area ); } } return totalArea; diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index 3414c6a7969..16e843324a7 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -25,8 +25,8 @@ void QgsEllipse::normalizeAxis() { - mSemiMajorAxis = fabs( mSemiMajorAxis ); - mSemiMinorAxis = fabs( mSemiMinorAxis ); + mSemiMajorAxis = std::fabs( mSemiMajorAxis ); + mSemiMinorAxis = std::fabs( mSemiMinorAxis ); if ( mSemiMajorAxis < mSemiMinorAxis ) { std::swap( mSemiMajorAxis, mSemiMinorAxis ); @@ -72,8 +72,8 @@ QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsEllipse QgsEllipse::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) { QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 ); - double axis_a = fabs( pt2.x() - pt1.x() ) / 2.0; - double axis_b = fabs( pt2.y() - pt1.y() ) / 2.0; + double axis_a = std::fabs( pt2.x() - pt1.x() ) / 2.0; + double axis_b = std::fabs( pt2.y() - pt1.y() ) / 2.0; double azimuth = 90.0; return QgsEllipse( center, axis_a, axis_b, azimuth ); @@ -81,8 +81,8 @@ QgsEllipse QgsEllipse::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) QgsEllipse QgsEllipse::fromCenterPoint( const QgsPoint ¢er, const QgsPoint &pt1 ) { - double axis_a = fabs( pt1.x() - center.x() ); - double axis_b = fabs( pt1.y() - center.y() ); + double axis_a = std::fabs( pt1.x() - center.x() ); + double axis_b = std::fabs( pt1.y() - center.y() ); double azimuth = 90.0; return QgsEllipse( center, axis_a, axis_b, azimuth ); diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 920fffed53f..95ed2a5e49e 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -2558,7 +2558,7 @@ QgsLineString *smoothCurve( const QgsLineString &line, const unsigned int iterat QgsPoint p3 = result->pointN( 1 ); double angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ); - angle = fabs( M_PI - angle ); + angle = std::fabs( M_PI - angle ); skipFirst = angle > maxAngleRads; } for ( int i = 0; i < result->numPoints() - 1; i++ ) diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 41f78739346..4adb92da691 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -468,7 +468,7 @@ void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint & d = 2 * ( dx21 * dy31 - dx31 * dy21 ); // Check colinearity, Cross product = 0 - if ( qgsDoubleNear( fabs( d ), 0.0, 0.00000000001 ) ) + if ( qgsDoubleNear( std::fabs( d ), 0.0, 0.00000000001 ) ) { radius = -1.0; return; diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index c610bee2dfd..2893a7c161a 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -2705,12 +2705,12 @@ int QgsGeos::geomDigits( const GEOSGeometry *geom ) GEOSCoordSeq_getX_r( geosinit.ctxt, bBoxCoordSeq, i, &t ); int digits; - digits = ceil( log10( fabs( t ) ) ); + digits = ceil( log10( std::fabs( t ) ) ); if ( digits > maxDigits ) maxDigits = digits; GEOSCoordSeq_getY_r( geosinit.ctxt, bBoxCoordSeq, i, &t ); - digits = ceil( log10( fabs( t ) ) ); + digits = ceil( log10( std::fabs( t ) ) ); if ( digits > maxDigits ) maxDigits = digits; } diff --git a/src/core/geometry/qgsinternalgeometryengine.cpp b/src/core/geometry/qgsinternalgeometryengine.cpp index b69991b6e6a..c6b8261d0ec 100644 --- a/src/core/geometry/qgsinternalgeometryengine.cpp +++ b/src/core/geometry/qgsinternalgeometryengine.cpp @@ -290,7 +290,7 @@ QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision, bool dotProductWithinAngleTolerance( double dotProduct, double lowerThreshold, double upperThreshold ) { - return lowerThreshold > fabs( dotProduct ) || fabs( dotProduct ) > upperThreshold; + return lowerThreshold > std::fabs( dotProduct ) || std::fabs( dotProduct ) > upperThreshold; } double normalizedDotProduct( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c ) @@ -341,7 +341,7 @@ double squareness( QgsLineString *ring, double lowerThreshold, double upperThres if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) ) continue; - sum += 2.0 * qMin( fabs( dotProduct - 1.0 ), qMin( fabs( dotProduct ), fabs( dotProduct + 1 ) ) ); + sum += 2.0 * qMin( std::fabs( dotProduct - 1.0 ), qMin( std::fabs( dotProduct ), std::fabs( dotProduct + 1 ) ) ); } a = b; b = c; diff --git a/src/core/geometry/qgsregularpolygon.cpp b/src/core/geometry/qgsregularpolygon.cpp index ef2db585517..36bc799d8c1 100644 --- a/src/core/geometry/qgsregularpolygon.cpp +++ b/src/core/geometry/qgsregularpolygon.cpp @@ -46,13 +46,13 @@ QgsRegularPolygon::QgsRegularPolygon( const QgsPoint ¢er, const double radiu { case InscribedCircle: { - mRadius = fabs( radius ); + mRadius = std::fabs( radius ); mFirstVertex = mCenter.project( mRadius, azimuth ); break; } case CircumscribedCircle: { - mRadius = apothemToRadius( fabs( radius ), numSides ); + mRadius = apothemToRadius( std::fabs( radius ), numSides ); mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 ); break; } @@ -118,7 +118,7 @@ QgsRegularPolygon::QgsRegularPolygon( const QgsPoint &pt1, const QgsPoint &pt2, mCenter = pt1.project( hypothenuse, azimuth + angle ); mFirstVertex = pt1; - mRadius = fabs( hypothenuse ); + mRadius = std::fabs( hypothenuse ); } } @@ -152,7 +152,7 @@ void QgsRegularPolygon::setCenter( const QgsPoint ¢er ) void QgsRegularPolygon::setRadius( const double radius ) { - mRadius = fabs( radius ); + mRadius = std::fabs( radius ); double azimuth = mCenter.azimuth( mFirstVertex ); // TODO: double inclination = mCenter.inclination(mFirstVertex); mFirstVertex = mCenter.project( mRadius, azimuth ); diff --git a/src/core/layout/qgslayoutsnapper.cpp b/src/core/layout/qgslayoutsnapper.cpp index 0e307b66a5d..dc1f7425d5a 100644 --- a/src/core/layout/qgslayoutsnapper.cpp +++ b/src/core/layout/qgslayoutsnapper.cpp @@ -89,7 +89,7 @@ QPointF QgsLayoutSnapper::snapPointToGrid( QPointF point, double scaleFactor, bo //convert snap tolerance from pixels to layout units double alignThreshold = mTolerance / scaleFactor; - if ( fabs( xSnapped - point.x() ) > alignThreshold ) + if ( std::fabs( xSnapped - point.x() ) > alignThreshold ) { //snap distance is outside of tolerance xSnapped = point.x(); @@ -98,7 +98,7 @@ QPointF QgsLayoutSnapper::snapPointToGrid( QPointF point, double scaleFactor, bo { snappedX = true; } - if ( fabs( ySnapped - point.y() ) > alignThreshold ) + if ( std::fabs( ySnapped - point.y() ) > alignThreshold ) { //snap distance is outside of tolerance ySnapped = point.y(); @@ -128,7 +128,7 @@ double QgsLayoutSnapper::snapPointToGuides( double original, QgsLayoutGuide::Ori Q_FOREACH ( QgsLayoutGuide *guide, mLayout->guides().guides( orientation ) ) { double guidePos = guide->layoutPosition(); - double diff = fabs( original - guidePos ); + double diff = std::fabs( original - guidePos ); if ( diff < smallestDiff ) { smallestDiff = diff; diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 9c2c70e2962..73f423dd799 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -720,7 +720,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList 1 (lower for longer segments) - double segmentAngleCost = 1 - fabs( fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2; // 0 -> 1, lower for more horizontal segments + double segmentAngleCost = 1 - std::fabs( fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2; // 0 -> 1, lower for more horizontal segments while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment ) { @@ -745,7 +745,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList 1 + double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment ); // 0 -> 1 cost += costCenter * 0.0005; // < 0, 0.0005 > if ( !closedLine ) @@ -753,7 +753,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList 1 + double costLineCenter = 2 * std::fabs( labelCenter - middleOfLine ) / totalLineLength; // 0 -> 1 cost += costLineCenter * 0.0005; // < 0, 0.0005 > } @@ -901,7 +901,7 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList & } // penalize positions which are further from the line's midpoint - double costCenter = fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength; // <0, 0.5> + double costCenter = std::fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength; // <0, 0.5> cost += costCenter / 1000; // < 0, 0.0005 > cost += initialCost; @@ -1223,7 +1223,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos { if ( tmp != slp ) // not first? { - diff = fabs( tmp->getAlpha() - angle_last ); + diff = std::fabs( tmp->getAlpha() - angle_last ); if ( diff > 2 * M_PI ) diff -= 2 * M_PI; diff = qMin( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg... angle_diff += diff; @@ -1241,7 +1241,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos // penalize positions which are further from the line's midpoint double labelCenter = i + getLabelWidth() / 2; - double costCenter = fabs( total_distance / 2 - labelCenter ) / total_distance; // <0, 0.5> + double costCenter = std::fabs( total_distance / 2 - labelCenter ) / total_distance; // <0, 0.5> cost += costCenter / 1000; // < 0, 0.0005 > slp->setCost( cost ); diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index 27a640fb5e5..8a0939462e5 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -371,9 +371,9 @@ void PointSet::splitPolygons( QLinkedList &shapes_toProcess, // lookup for the deepest point in the hole for ( i = ips; i != cHull[ihn]; i = ( i + 1 ) % nbp ) { - cp = fabs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]], - x[cHull[ihn]], y[cHull[ihn]], - x[i], y[i] ) ); + cp = std::fabs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]], + x[cHull[ihn]], y[cHull[ihn]], + x[i], y[i] ) ); if ( cp - bestcp > EPSILON ) { bestcp = cp; @@ -444,7 +444,7 @@ void PointSet::splitPolygons( QLinkedList &shapes_toProcess, fx = cx + dx; fy = cy - dy; - if ( seg_length < EPSILON || fabs( ( b = GeomFunction::cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j + if ( seg_length < EPSILON || std::fabs( ( b = GeomFunction::cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j { if ( ( ex = GeomFunction::dist_euc2d_sq( x[i], y[i], x[retainedPt], y[retainedPt] ) ) < ( ey = GeomFunction::dist_euc2d_sq( x[j], y[j], x[retainedPt], y[retainedPt] ) ) ) { diff --git a/src/core/qgsclipper.h b/src/core/qgsclipper.h index e967b376a0b..4f7e906293a 100644 --- a/src/core/qgsclipper.h +++ b/src/core/qgsclipper.h @@ -395,7 +395,7 @@ inline QgsPointXY QgsClipper::intersect( const double x1, const double y1, QgsPointXY p; - if ( fabs( r_d ) > SMALL_NUM && fabs( r_n ) > SMALL_NUM ) + if ( std::fabs( r_d ) > SMALL_NUM && std::fabs( r_n ) > SMALL_NUM ) { // they cross double r = r_n / r_d; @@ -405,7 +405,7 @@ inline QgsPointXY QgsClipper::intersect( const double x1, const double y1, { // Should never get here, but if we do for some reason, cause a // clunk because something else is wrong if we do. - Q_ASSERT( fabs( r_d ) > SMALL_NUM && fabs( r_n ) > SMALL_NUM ); + Q_ASSERT( std::fabs( r_d ) > SMALL_NUM && std::fabs( r_n ) > SMALL_NUM ); } return p; diff --git a/src/core/qgscoordinatereferencesystem.cpp b/src/core/qgscoordinatereferencesystem.cpp index 653647a1596..c5da999aaee 100644 --- a/src/core/qgscoordinatereferencesystem.cpp +++ b/src/core/qgscoordinatereferencesystem.cpp @@ -1164,7 +1164,7 @@ void QgsCoordinateReferenceSystem::setMapUnits() static const double FEET_TO_METER = 0.3048; static const double SMALL_NUM = 1e-3; - if ( fabs( toMeter - FEET_TO_METER ) < SMALL_NUM ) + if ( std::fabs( toMeter - FEET_TO_METER ) < SMALL_NUM ) unit = QStringLiteral( "Foot" ); if ( qgsDoubleNear( toMeter, 1.0 ) ) //Unit name for meters would be "metre" diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 7bcb21b3b07..9c517de33cc 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -442,7 +442,7 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( sigma = ( distance / ( b * A ) ) + delta_sigma; i++; } - while ( i < 999 && fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); + while ( i < 999 && std::fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); lat2 = atan2( ( sin( u1 ) * cos( sigma ) + cos( u1 ) * sin( sigma ) * cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + @@ -571,7 +571,7 @@ double QgsDistanceArea::computeDistanceBearing( double tu2 = 0; int iterLimit = 20; - while ( fabs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 ) + while ( std::fabs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 ) { sinLambda = sin( lambda ); cosLambda = cos( lambda ); @@ -741,7 +741,7 @@ double QgsDistanceArea::computePolygonArea( const QList &points ) co dx = x2 - x1; dy = y2 - y1; - if ( fabs( dy ) > thresh ) + if ( std::fabs( dy ) > thresh ) { /* account for different latitudes y1, y2 */ area += dx * ( m_Qp - ( Qbar2 - Qbar1 ) / dy ); diff --git a/src/core/qgsmaprendererjob.cpp b/src/core/qgsmaprendererjob.cpp index 7aba351c4c4..8a1dded3106 100644 --- a/src/core/qgsmaprendererjob.cpp +++ b/src/core/qgsmaprendererjob.cpp @@ -134,12 +134,12 @@ bool QgsMapRendererJob::reprojectToLayerExtent( const QgsMapLayer *ml, const Qgs QgsDebugMsgLevel( QString( "\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" ) .arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ) .arg( extent1.toString(), extent2.toString() ).arg( extent2.width() ).arg( extent2.height() ) - .arg( fabs( 1.0 - extent2.width() / extent.width() ) ) - .arg( fabs( 1.0 - extent2.height() / extent.height() ) ) + .arg( std::fabs( 1.0 - extent2.width() / extent.width() ) ) + .arg( std::fabs( 1.0 - extent2.height() / extent.height() ) ) , 3 ); - if ( fabs( 1.0 - extent2.width() / extent.width() ) < 0.5 && - fabs( 1.0 - extent2.height() / extent.height() ) < 0.5 ) + if ( std::fabs( 1.0 - extent2.width() / extent.width() ) < 0.5 && + std::fabs( 1.0 - extent2.height() / extent.height() ) < 0.5 ) { extent = extent1; } diff --git a/src/core/qgsmapsettings.cpp b/src/core/qgsmapsettings.cpp index 3919741956a..34ab9638260 100644 --- a/src/core/qgsmapsettings.cpp +++ b/src/core/qgsmapsettings.cpp @@ -143,8 +143,8 @@ void QgsMapSettings::updateDerived() { // Use abs() on the extent to avoid the case where the extent is // symmetrical about 0. - double xMean = ( fabs( extent.xMinimum() ) + fabs( extent.xMaximum() ) ) * 0.5; - double yMean = ( fabs( extent.yMinimum() ) + fabs( extent.yMaximum() ) ) * 0.5; + double xMean = ( std::fabs( extent.xMinimum() ) + std::fabs( extent.xMaximum() ) ) * 0.5; + double yMean = ( std::fabs( extent.yMinimum() ) + std::fabs( extent.yMaximum() ) ) * 0.5; double xRange = extent.width() / xMean; double yRange = extent.height() / yMean; diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index f18b0dad51f..c90695ecfc6 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -1171,8 +1171,8 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, QString t #if 0 // XXX strk QgsPointXY ptSize = xform->toMapCoordinatesF( w, h ); - labelX = fabs( ptSize.x() - ptZero.x() ); - labelY = fabs( ptSize.y() - ptZero.y() ); + labelX = std::fabs( ptSize.x() - ptZero.x() ); + labelY = std::fabs( ptSize.y() - ptZero.y() ); #else double uPP = xform->mapUnitsPerPixel(); labelX = w * uPP; @@ -1224,7 +1224,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont // scales closer than 1:1 if ( maxScale < 0 ) { - maxScale = 1 / fabs( maxScale ); + maxScale = 1 / std::fabs( maxScale ); } if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() < maxScale ) @@ -1239,7 +1239,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont // scales closer than 1:1 if ( minScale < 0 ) { - minScale = 1 / fabs( minScale ); + minScale = 1 / std::fabs( minScale ); } if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() > minScale ) @@ -1436,7 +1436,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont } } // make sure maxcharangleout is always negative - maxcharangleout = -( fabs( maxcharangleout ) ); + maxcharangleout = -( std::fabs( maxcharangleout ) ); } // data defined centroid whole or clipped? diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index 6a85e7a1dd5..d9489f3e9bb 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -84,13 +84,13 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix myWrappedY = myWrappedY + 180.0; } - int myDegreesX = int( fabs( myWrappedX ) ); - double myFloatMinutesX = double( ( fabs( myWrappedX ) - myDegreesX ) * 60 ); + int myDegreesX = int( std::fabs( myWrappedX ) ); + double myFloatMinutesX = double( ( std::fabs( myWrappedX ) - myDegreesX ) * 60 ); int myIntMinutesX = int( myFloatMinutesX ); double mySecondsX = double( myFloatMinutesX - myIntMinutesX ) * 60; - int myDegreesY = int( fabs( myWrappedY ) ); - double myFloatMinutesY = double( ( fabs( myWrappedY ) - myDegreesY ) * 60 ); + int myDegreesY = int( std::fabs( myWrappedY ) ); + double myFloatMinutesY = double( ( std::fabs( myWrappedY ) - myDegreesY ) * 60 ); int myIntMinutesY = int( myFloatMinutesY ); double mySecondsY = double( myFloatMinutesY - myIntMinutesY ) * 60; @@ -186,11 +186,11 @@ QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const myWrappedX = myWrappedX + 360.0; } - int myDegreesX = int( fabs( myWrappedX ) ); - double myFloatMinutesX = double( ( fabs( myWrappedX ) - myDegreesX ) * 60 ); + int myDegreesX = int( std::fabs( myWrappedX ) ); + double myFloatMinutesX = double( ( std::fabs( myWrappedX ) - myDegreesX ) * 60 ); - int myDegreesY = int( fabs( mY ) ); - double myFloatMinutesY = double( ( fabs( mY ) - myDegreesY ) * 60 ); + int myDegreesY = int( std::fabs( mY ) ); + double myFloatMinutesY = double( ( std::fabs( mY ) - myDegreesY ) * 60 ); //make sure rounding to specified precision doesn't create minutes >= 60 if ( std::round( myFloatMinutesX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) diff --git a/src/core/qgsrenderchecker.cpp b/src/core/qgsrenderchecker.cpp index 93d2a1b47c0..66f4c29b55f 100644 --- a/src/core/qgsrenderchecker.cpp +++ b/src/core/qgsrenderchecker.cpp @@ -381,8 +381,8 @@ bool QgsRenderChecker::compareImages( const QString &testName, { qDebug( "Test image and result image for %s are different dimensions", testName.toLocal8Bit().constData() ); - if ( abs( myExpectedImage.width() - myResultImage.width() ) > mMaxSizeDifferenceX || - abs( myExpectedImage.height() - myResultImage.height() ) > mMaxSizeDifferenceY ) + if ( std::abs( myExpectedImage.width() - myResultImage.width() ) > mMaxSizeDifferenceX || + std::abs( myExpectedImage.height() - myResultImage.height() ) > mMaxSizeDifferenceY ) { mReport += QLatin1String( "" ); mReport += "Expected image and result image for " + testName + " are different dimensions - FAILING!"; @@ -438,10 +438,10 @@ bool QgsRenderChecker::compareImages( const QString &testName, } else { - if ( abs( qRed( myExpectedPixel ) - qRed( myActualPixel ) ) > pixelTolerance || - abs( qGreen( myExpectedPixel ) - qGreen( myActualPixel ) ) > pixelTolerance || - abs( qBlue( myExpectedPixel ) - qBlue( myActualPixel ) ) > pixelTolerance || - abs( qAlpha( myExpectedPixel ) - qAlpha( myActualPixel ) ) > pixelTolerance ) + if ( std::abs( qRed( myExpectedPixel ) - qRed( myActualPixel ) ) > pixelTolerance || + std::abs( qGreen( myExpectedPixel ) - qGreen( myActualPixel ) ) > pixelTolerance || + std::abs( qBlue( myExpectedPixel ) - qBlue( myActualPixel ) ) > pixelTolerance || + std::abs( qAlpha( myExpectedPixel ) - qAlpha( myActualPixel ) ) > pixelTolerance ) { ++mMismatchCount; diffScanline[ x ] = qRgb( 255, 0, 0 ); diff --git a/src/core/qgstestutils.h b/src/core/qgstestutils.h index 1d38a7c576c..c81ccd4d23f 100644 --- a/src/core/qgstestutils.h +++ b/src/core/qgstestutils.h @@ -29,7 +29,7 @@ bool _xxxresult = qgsDoubleNear( value, expected, epsilon ); \ if ( !_xxxresult ) \ { \ - qDebug( "Expecting %f got %f (diff %f > %f)", static_cast< double >( expected ), static_cast< double >( value ), fabs( static_cast< double >( expected ) - value ), static_cast< double >( epsilon ) ); \ + qDebug( "Expecting %f got %f (diff %f > %f)", static_cast< double >( expected ), static_cast< double >( value ), std::fabs( static_cast< double >( expected ) - value ), static_cast< double >( epsilon ) ); \ } \ QVERIFY( qgsDoubleNear( value, expected, epsilon ) ); \ } @@ -38,7 +38,7 @@ bool _xxxresult = qgsDoubleNear( value, not_expected, epsilon ); \ if ( _xxxresult ) \ { \ - qDebug( "Expecting %f to be differerent from %f (diff %f > %f)", static_cast< double >( value ), static_cast< double >( not_expected ), fabs( static_cast< double >( not_expected ) - value ), static_cast< double >( epsilon ) ); \ + qDebug( "Expecting %f to be differerent from %f (diff %f > %f)", static_cast< double >( value ), static_cast< double >( not_expected ), std::fabs( static_cast< double >( not_expected ) - value ), static_cast< double >( epsilon ) ); \ } \ QVERIFY( !qgsDoubleNear( value, not_expected, epsilon ) ); \ } diff --git a/src/core/qgstracer.cpp b/src/core/qgstracer.cpp index 1c6e54f2f6a..16ce217e1af 100644 --- a/src/core/qgstracer.cpp +++ b/src/core/qgstracer.cpp @@ -264,7 +264,7 @@ int point2vertex( const QgsTracerGraph &g, const QgsPointXY &pt, double epsilon for ( int i = 0; i < g.v.count(); ++i ) { const QgsTracerGraph::V &v = g.v.at( i ); - if ( v.pt == pt || ( fabs( v.pt.x() - pt.x() ) < epsilon && fabs( v.pt.y() - pt.y() ) < epsilon ) ) + if ( v.pt == pt || ( std::fabs( v.pt.x() - pt.x() ) < epsilon && std::fabs( v.pt.y() - pt.y() ) < epsilon ) ) return i; } diff --git a/src/core/qgsunittypes.cpp b/src/core/qgsunittypes.cpp index 471d2f51619..9e4ae8e7a63 100644 --- a/src/core/qgsunittypes.cpp +++ b/src/core/qgsunittypes.cpp @@ -1383,17 +1383,17 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceMeters; } - else if ( fabs( distance ) > 1000.0 ) + else if ( std::fabs( distance ) > 1000.0 ) { result.value = qgsRound( distance / 1000, decimals ); result.unit = QgsUnitTypes::DistanceKilometers; } - else if ( fabs( distance ) < 0.01 ) + else if ( std::fabs( distance ) < 0.01 ) { result.value = qgsRound( distance * 1000, decimals ); result.unit = QgsUnitTypes::DistanceMillimeters; } - else if ( fabs( distance ) < 0.1 ) + else if ( std::fabs( distance ) < 0.1 ) { result.value = qgsRound( distance * 100, decimals ); @@ -1407,7 +1407,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceKilometers: - if ( keepBaseUnit || fabs( distance ) >= 1.0 ) + if ( keepBaseUnit || std::fabs( distance ) >= 1.0 ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceKilometers; @@ -1420,7 +1420,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceFeet: - if ( fabs( distance ) <= 5280.0 || keepBaseUnit ) + if ( std::fabs( distance ) <= 5280.0 || keepBaseUnit ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceFeet; @@ -1433,7 +1433,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceYards: - if ( fabs( distance ) <= 1760.0 || keepBaseUnit ) + if ( std::fabs( distance ) <= 1760.0 || keepBaseUnit ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceYards; @@ -1446,7 +1446,7 @@ QgsUnitTypes::DistanceValue QgsUnitTypes::scaledDistance( double distance, QgsUn break; case DistanceMiles: - if ( fabs( distance ) >= 1.0 || keepBaseUnit ) + if ( std::fabs( distance ) >= 1.0 || keepBaseUnit ) { result.value = qgsRound( distance, decimals ); result.unit = QgsUnitTypes::DistanceMiles; @@ -1531,12 +1531,12 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaSquareMeters; } - else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaSquareMeters ) ) + else if ( std::fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaSquareMeters ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMeters, QgsUnitTypes::AreaSquareKilometers ), decimals ); result.unit = QgsUnitTypes::AreaSquareKilometers; } - else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaHectares, QgsUnitTypes::AreaSquareMeters ) ) + else if ( std::fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaHectares, QgsUnitTypes::AreaSquareMeters ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMeters, QgsUnitTypes::AreaHectares ), decimals ); result.unit = QgsUnitTypes::AreaHectares; @@ -1563,7 +1563,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaSquareFeet; } - else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareFeet ) ) + else if ( std::fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareFeet ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareFeet, QgsUnitTypes::AreaSquareMiles ), decimals ); result.unit = QgsUnitTypes::AreaSquareMiles; @@ -1583,7 +1583,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaSquareYards; } - else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareYards ) ) + else if ( std::fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaSquareYards ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareYards, QgsUnitTypes::AreaSquareMiles ), decimals ); result.unit = QgsUnitTypes::AreaSquareMiles; @@ -1610,7 +1610,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaHectares; } - else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaHectares ) ) + else if ( std::fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareKilometers, QgsUnitTypes::AreaHectares ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaHectares, QgsUnitTypes::AreaSquareKilometers ), decimals ); result.unit = QgsUnitTypes::AreaSquareKilometers; @@ -1630,7 +1630,7 @@ QgsUnitTypes::AreaValue QgsUnitTypes::scaledArea( double area, QgsUnitTypes::Are result.value = qgsRound( area, decimals ); result.unit = QgsUnitTypes::AreaAcres; } - else if ( fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaAcres ) ) + else if ( std::fabs( area ) > QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaSquareMiles, QgsUnitTypes::AreaAcres ) ) { result.value = qgsRound( area * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AreaAcres, QgsUnitTypes::AreaSquareMiles ), decimals ); result.unit = QgsUnitTypes::AreaSquareMiles; diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 1c24ec469e9..7afd65dfea4 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -2472,7 +2472,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer *layer, { QVariant min = layer->minimumValue( i ); QVariant max = layer->maximumValue( i ); - if ( qMax( llabs( min.toLongLong() ), llabs( max.toLongLong() ) ) < INT_MAX ) + if ( qMax( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < INT_MAX ) { fields[i].setType( QVariant::Int ); } diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp index 74f2770720f..44b1286b035 100644 --- a/src/core/qgsvectorlayerlabeling.cpp +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -465,7 +465,7 @@ void QgsVectorLayerSimpleLabeling::toSld( QDomNode &parent, const QgsStringMap & if ( mSettings->maxCurvedCharAngleIn > 0 || mSettings->maxCurvedCharAngleOut > 0 ) { // SLD has no notion for this, the GeoTools ecosystem can only do a single angle - double angle = qMin( fabs( mSettings->maxCurvedCharAngleIn ), fabs( mSettings->maxCurvedCharAngleOut ) ); + double angle = qMin( std::fabs( mSettings->maxCurvedCharAngleIn ), std::fabs( mSettings->maxCurvedCharAngleOut ) ); QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "maxAngleDelta" ), qgsDoubleToString( angle ) ); textSymbolizerElement.appendChild( vo ); } diff --git a/src/core/raster/qgscolorrampshader.cpp b/src/core/raster/qgscolorrampshader.cpp index 99e7a88c213..485728bd816 100644 --- a/src/core/raster/qgscolorrampshader.cpp +++ b/src/core/raster/qgscolorrampshader.cpp @@ -285,7 +285,7 @@ void QgsColorRampShader::classifyColorRamp( const int classes, const int band, c QVector::const_iterator color_it = entryColors.begin(); // calculate a reasonable number of decimals to display - double maxabs = log10( qMax( fabs( max ), fabs( min ) ) ); + double maxabs = log10( qMax( std::fabs( max ), std::fabs( min ) ) ); int nDecimals = std::round( qMax( 3.0 + maxabs - log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); QList colorRampItems; diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index 8d47800bb89..a9806fb8cf4 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -203,7 +203,7 @@ double QgsRasterChecker::tolerance( double val, int places ) { // float precision is about 7 decimal digits, double about 16 // default places = 6 - return 1. * qPow( 10, std::round( log10( fabs( val ) ) - places ) ); + return 1. * qPow( 10, std::round( log10( std::fabs( val ) ) - places ) ); } QString QgsRasterChecker::compareHead() @@ -224,7 +224,7 @@ void QgsRasterChecker::compare( const QString ¶mName, int verifiedVal, int e bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double tolerance ) { // values may be nan - return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( fabs( verifiedVal - expectedVal ) <= tolerance ); + return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( std::fabs( verifiedVal - expectedVal ) <= tolerance ); } void QgsRasterChecker::compare( const QString ¶mName, double verifiedVal, double expectedVal, QString &report, bool &ok, double tolerance ) diff --git a/src/core/raster/qgsrasterminmaxorigin.cpp b/src/core/raster/qgsrasterminmaxorigin.cpp index 8d476a73dc4..5a126d37378 100644 --- a/src/core/raster/qgsrasterminmaxorigin.cpp +++ b/src/core/raster/qgsrasterminmaxorigin.cpp @@ -40,9 +40,9 @@ bool QgsRasterMinMaxOrigin::operator ==( const QgsRasterMinMaxOrigin &other ) co return mLimits == other.mLimits && mExtent == other.mExtent && mAccuracy == other.mAccuracy && - fabs( mCumulativeCutLower - other.mCumulativeCutLower ) < 1e-5 && - fabs( mCumulativeCutUpper - other.mCumulativeCutUpper ) < 1e-5 && - fabs( mStdDevFactor - other.mStdDevFactor ) < 1e-5; + std::fabs( mCumulativeCutLower - other.mCumulativeCutLower ) < 1e-5 && + std::fabs( mCumulativeCutUpper - other.mCumulativeCutUpper ) < 1e-5 && + std::fabs( mStdDevFactor - other.mStdDevFactor ) < 1e-5; } QString QgsRasterMinMaxOrigin::limitsString( Limits limits ) diff --git a/src/core/simplify/effectivearea.cpp b/src/core/simplify/effectivearea.cpp index 4863b96ded2..0dbc9df1d9a 100644 --- a/src/core/simplify/effectivearea.cpp +++ b/src/core/simplify/effectivearea.cpp @@ -65,7 +65,7 @@ static double triarea3d( const QgsPoint &P1, const QgsPoint &P2, const QgsPoint cy = az * bx - ax * bz; cz = ax * by - ay * bx; - area = fabs( 0.5 * ( sqrt( cx * cx + cy * cy + cz * cz ) ) ); + area = std::fabs( 0.5 * ( sqrt( cx * cx + cy * cy + cz * cz ) ) ); return area; } diff --git a/src/core/symbology/qgs25drenderer.cpp b/src/core/symbology/qgs25drenderer.cpp index cca1b31628d..726b72119fb 100644 --- a/src/core/symbology/qgs25drenderer.cpp +++ b/src/core/symbology/qgs25drenderer.cpp @@ -53,7 +53,7 @@ "set_color_part( " \ " @symbol_color," \ " 'value'," \ - " 40 + 19 * abs( $pi - azimuth( " \ + " 40 + 19 * std::abs( $pi - azimuth( " \ " point_n( geometry_n($geometry, @geometry_part_num) , 1 ), " \ " point_n( geometry_n($geometry, @geometry_part_num) , 2 )" \ " ) ) " \ diff --git a/src/core/symbology/qgsarrowsymbollayer.cpp b/src/core/symbology/qgsarrowsymbollayer.cpp index caaf48950c1..c11bcab8653 100644 --- a/src/core/symbology/qgsarrowsymbollayer.cpp +++ b/src/core/symbology/qgsarrowsymbollayer.cpp @@ -355,7 +355,7 @@ bool pointsToCircle( QPointF a, QPointF b, QPointF c, QPointF ¢er, qreal &ra QPointF bc2 = ( b + c ) / 2.0; // Aligned points - if ( fabs( ab.x() * bc.y() - ab.y() * bc.x() ) < 0.001 ) // Empirical threshold for nearly aligned points + if ( std::fabs( ab.x() * bc.y() - ab.y() * bc.x() ) < 0.001 ) // Empirical threshold for nearly aligned points return false; // in case AB is horizontal @@ -493,9 +493,9 @@ QPolygonF curvedArrow( QPointF po, QPointF pm, QPointF pd, qreal length = euclidian_distance( po, pd ); // for close points and deltaAngle < 180, draw a straight line - if ( fabs( deltaAngle ) < M_PI && ( ( ( headType == QgsArrowSymbolLayer::HeadSingle ) && ( length < headWidth ) ) || - ( ( headType == QgsArrowSymbolLayer::HeadReversed ) && ( length < headWidth ) ) || - ( ( headType == QgsArrowSymbolLayer::HeadDouble ) && ( length < 2 * headWidth ) ) ) ) + if ( std::fabs( deltaAngle ) < M_PI && ( ( ( headType == QgsArrowSymbolLayer::HeadSingle ) && ( length < headWidth ) ) || + ( ( headType == QgsArrowSymbolLayer::HeadReversed ) && ( length < headWidth ) ) || + ( ( headType == QgsArrowSymbolLayer::HeadDouble ) && ( length < 2 * headWidth ) ) ) ) { return straightArrow( po, pd, startWidth, width, headWidth, headHeight, headType, arrowType, offset ); } diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index 01e1539a321..8a7365ccb2a 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -395,7 +395,7 @@ QgsSymbolLayer *QgsSimpleFillSymbolLayer::createFromSld( QDomElement &element ) double QgsSimpleFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { double penBleed = context.convertToPainterUnits( mStrokeStyle == Qt::NoPen ? 0 : ( mStrokeWidth / 2.0 ), mStrokeWidthUnit, mStrokeWidthMapUnitScale ); - double offsetBleed = context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return penBleed + offsetBleed; } @@ -916,7 +916,7 @@ QgsGradientFillSymbolLayer *QgsGradientFillSymbolLayer::clone() const double QgsGradientFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - double offsetBleed = context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return offsetBleed; } @@ -1512,7 +1512,7 @@ QgsShapeburstFillSymbolLayer *QgsShapeburstFillSymbolLayer::clone() const double QgsShapeburstFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - double offsetBleed = context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return offsetBleed; } @@ -2600,14 +2600,14 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & lineAngle += 360.; } - height = abs( height ); - width = abs( width ); + height = std::abs( height ); + width = std::abs( width ); outputPixelDist = height * cos( lineAngle * M_PI / 180 ); // Round offset to correspond to one pixel height, otherwise lines may // be shifted on tile border if offset falls close to pixel center - int offsetHeight = std::round( fabs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) ); + int offsetHeight = std::round( std::fabs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) ); outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 ); } @@ -3749,7 +3749,7 @@ QgsRasterFillSymbolLayer *QgsRasterFillSymbolLayer::clone() const double QgsRasterFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - return context.convertToPainterUnits( qMax( fabs( mOffset.x() ), fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + return context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); } void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath ) diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 1efbac4a39d..58b05f4a8b8 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -581,7 +581,7 @@ double QgsSimpleLineSymbolLayer::estimateMaxBleed( const QgsRenderContext &conte else { return context.convertToPainterUnits( ( mWidth / 2.0 ), mWidthUnit, mWidthMapUnitScale ) + - context.convertToPainterUnits( fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); + context.convertToPainterUnits( std::fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); } } @@ -1236,7 +1236,7 @@ void QgsMarkerLineSymbolLayer::renderOffsetVertexAlongLine( const QPolygonF &poi QPointF previousPoint = points[vertex]; int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 ); int endPoint = distance > 0 ? points.count() - 1 : 0; - double distanceLeft = fabs( distance ); + double distanceLeft = std::fabs( distance ); for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement ) { @@ -1604,7 +1604,7 @@ QSet QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext & double QgsMarkerLineSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { return context.convertToPainterUnits( ( mMarker->size() / 2.0 ), mMarker->sizeUnit(), mMarker->sizeMapUnitScale() ) + - context.convertToPainterUnits( fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); + context.convertToPainterUnits( std::fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale ); } diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index a2fb2792ca3..a30465e1cb4 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3895,7 +3895,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, else { int U = 1; - cell = qMax( fabs( minimum ), fabs( maximum ) ); + cell = qMax( std::fabs( minimum ), std::fabs( maximum ) ); if ( adjustBias >= 1.5 * h + 0.5 ) { U = 1 + ( 1.0 / ( 1 + h ) ); diff --git a/src/gui/layout/qgslayoutruler.cpp b/src/gui/layout/qgslayoutruler.cpp index a74ff53da44..3cc239ec009 100644 --- a/src/gui/layout/qgslayoutruler.cpp +++ b/src/gui/layout/qgslayoutruler.cpp @@ -376,11 +376,11 @@ QgsLayoutGuide *QgsLayoutRuler::guideAtPoint( QPoint localPoint ) const switch ( mOrientation ) { case Qt::Horizontal: - currentDelta = fabs( layoutPoint.x() - guide->layoutPosition() ); + currentDelta = std::fabs( layoutPoint.x() - guide->layoutPosition() ); break; case Qt::Vertical: - currentDelta = fabs( layoutPoint.y() - guide->layoutPosition() ); + currentDelta = std::fabs( layoutPoint.y() - guide->layoutPosition() ); break; } if ( currentDelta < minDelta ) diff --git a/src/gui/layout/qgslayoutview.cpp b/src/gui/layout/qgslayoutview.cpp index 2eca4704392..785ec47f152 100644 --- a/src/gui/layout/qgslayoutview.cpp +++ b/src/gui/layout/qgslayoutview.cpp @@ -456,7 +456,7 @@ void QgsLayoutView::wheelZoom( QWheelEvent *event ) double zoomFactor = settings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble(); // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps - zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * fabs( event->angleDelta().y() ); + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( event->angleDelta().y() ); if ( event->modifiers() & Qt::ControlModifier ) { diff --git a/src/gui/layout/qgslayoutviewrubberband.cpp b/src/gui/layout/qgslayoutviewrubberband.cpp index 7dc65655160..f4a8a0fcd21 100644 --- a/src/gui/layout/qgslayoutviewrubberband.cpp +++ b/src/gui/layout/qgslayoutviewrubberband.cpp @@ -49,14 +49,14 @@ QRectF QgsLayoutViewRubberBand::updateRect( QPointF start, QPointF position, boo if ( constrainSquare ) { - if ( fabs( dx ) > fabs( dy ) ) + if ( std::fabs( dx ) > std::fabs( dy ) ) { - width = fabs( dx ); + width = std::fabs( dx ); height = width; } else { - height = fabs( dy ); + height = std::fabs( dy ); width = height; } diff --git a/src/gui/layout/qgslayoutviewtool.cpp b/src/gui/layout/qgslayoutviewtool.cpp index 2d981d80c61..76ef9f125f8 100644 --- a/src/gui/layout/qgslayoutviewtool.cpp +++ b/src/gui/layout/qgslayoutviewtool.cpp @@ -31,7 +31,7 @@ bool QgsLayoutViewTool::isClickAndDrag( QPoint startViewPoint, QPoint endViewPoi { int diffX = endViewPoint.x() - startViewPoint.x(); int diffY = endViewPoint.y() - startViewPoint.y(); - if ( abs( diffX ) >= 2 || abs( diffY ) >= 2 ) + if ( std::abs( diffX ) >= 2 || std::abs( diffY ) >= 2 ) { return true; } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 06b51e5a448..e0dd9b0089d 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -66,11 +66,11 @@ bool QgsAdvancedDigitizingDockWidget::lineCircleIntersection( const QgsPointXY & const int sgnDy = dy < 0 ? -1 : 1; const double ax = center.x() + ( d * dy + sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); - const double ay = center.y() + ( -d * dx + fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); + const double ay = center.y() + ( -d * dx + std::fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); const QgsPointXY p1( ax, ay ); const double bx = center.x() + ( d * dy - sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); - const double by = center.y() + ( -d * dx - fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); + const double by = center.y() + ( -d * dx - std::fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); const QgsPointXY p2( bx, by ); // snap to nearest intersection @@ -656,14 +656,14 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) softAngle -= deltaAngle; } int quo = std::round( softAngle / commonAngle ); - if ( fabs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES ) + if ( std::fabs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES ) { // also check the distance in pixel to the line, otherwise it's too sticky at long ranges softAngle = quo * commonAngle ; // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1 - const double dist = fabs( qCos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) - - qSin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) ); + const double dist = std::fabs( qCos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) + - qSin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) ); if ( dist / mMapCanvas->mapSettings().mapUnitsPerPixel() < SOFT_CONSTRAINT_TOLERANCE_PIXEL ) { mAngleConstraint->setLockMode( CadConstraint::SoftLock ); @@ -745,7 +745,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) // do not compute intersection if lines are almost parallel // this threshold might be adapted - if ( fabs( d ) > 0.01 ) + if ( std::fabs( d ) > 0.01 ) { point.setX( ( ( x3 - x4 ) * ( x1 * y2 - y1 * x2 ) - ( x1 - x2 ) * ( x3 * y4 - y3 * x4 ) ) / d ); point.setY( ( ( y3 - y4 ) * ( x1 * y2 - y1 * x2 ) - ( y1 - y2 ) * ( x3 * y4 - y3 * x4 ) ) / d ); diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index be041c56bda..b5f624a4b35 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -464,7 +464,7 @@ void QgsColorWheel::paintEvent( QPaintEvent *event ) double mx = ( sx + vx ) / 2.0; double my = ( sy + vy ) / 2.0; - double a = ( 1 - 2.0 * fabs( lightness - 0.5 ) ) * mCurrentColor.hslSaturationF(); + double a = ( 1 - 2.0 * std::fabs( lightness - 0.5 ) ) * mCurrentColor.hslSaturationF(); double x = sx + ( vx - sx ) * lightness + ( hx - mx ) * a; double y = sy + ( vy - sy ) * lightness + ( hy - my ) * a; @@ -564,7 +564,7 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) double triangleSideLength = sqrt( 3.0 ) * triangleLength; double newL = ( ( -sin( rad0 ) * r ) / triangleSideLength ) + 0.5; - double widthShare = 1.0 - ( fabs( newL - 0.5 ) * 2.0 ); + double widthShare = 1.0 - ( std::fabs( newL - 0.5 ) * 2.0 ); double newS = ( ( ( cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare; s = qMin( static_cast< int >( std::round( qMax( 0.0, newS ) * 255.0 ) ), 255 ); l = qMin( static_cast< int >( std::round( qMax( 0.0, newL ) * 255.0 ) ), 255 ); diff --git a/src/gui/qgscomposerview.cpp b/src/gui/qgscomposerview.cpp index a7e10e9df9c..93933fbf9e9 100644 --- a/src/gui/qgscomposerview.cpp +++ b/src/gui/qgscomposerview.cpp @@ -753,7 +753,7 @@ void QgsComposerView::mouseReleaseEvent( QMouseEvent *e ) //was this just a click? or a click and drag? bool clickOnly = false; - if ( fabs( diffX ) < 2 && fabs( diffY ) < 2 ) + if ( std::fabs( diffX ) < 2 && std::fabs( diffY ) < 2 ) { clickOnly = true; } @@ -1315,14 +1315,14 @@ void QgsComposerView::updateRubberBandRect( QPointF &pos, const bool constrainSq if ( constrainSquare ) { - if ( fabs( dx ) > fabs( dy ) ) + if ( std::fabs( dx ) > std::fabs( dy ) ) { - width = fabs( dx ); + width = std::fabs( dx ); height = width; } else { - height = fabs( dy ); + height = std::fabs( dy ); width = height; } @@ -2015,7 +2015,7 @@ void QgsComposerView::wheelZoom( QWheelEvent *event ) double zoomFactor = mySettings.value( QStringLiteral( "qgis/zoom_factor" ), 2 ).toDouble(); // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps - zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * fabs( event->angleDelta().y() ); + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( event->angleDelta().y() ); if ( event->modifiers() & Qt::ControlModifier ) { diff --git a/src/gui/qgsgradientstopeditor.cpp b/src/gui/qgsgradientstopeditor.cpp index b81692957b5..1a4f15d14dd 100644 --- a/src/gui/qgsgradientstopeditor.cpp +++ b/src/gui/qgsgradientstopeditor.cpp @@ -285,7 +285,7 @@ int QgsGradientStopEditor::findClosestStop( int x, int threshold ) const int i = 1; Q_FOREACH ( const QgsGradientStop &stop, mGradient.stops() ) { - currentDiff = abs( relativePositionToPoint( stop.offset ) + 1 - x ); + currentDiff = std::abs( relativePositionToPoint( stop.offset ) + 1 - x ); if ( ( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff ) { closestStop = i; @@ -295,7 +295,7 @@ int QgsGradientStopEditor::findClosestStop( int x, int threshold ) const } //first stop - currentDiff = abs( relativePositionToPoint( 0.0 ) + 1 - x ); + currentDiff = std::abs( relativePositionToPoint( 0.0 ) + 1 - x ); if ( ( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff ) { closestStop = 0; @@ -303,7 +303,7 @@ int QgsGradientStopEditor::findClosestStop( int x, int threshold ) const } //last stop - currentDiff = abs( relativePositionToPoint( 1.0 ) + 1 - x ); + currentDiff = std::abs( relativePositionToPoint( 1.0 ) + 1 - x ); if ( ( threshold < 0 || currentDiff < threshold ) && currentDiff < closestDiff ) { closestStop = mGradient.count() - 1; diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index fe828b8a55f..8ff4e666957 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -1091,8 +1091,8 @@ void QgsMapCanvas::keyPressEvent( QKeyEvent *e ) // Don't want to interfer with mouse events QgsRectangle currentExtent = mapSettings().visibleExtent(); - double dx = fabs( currentExtent.width() / 4 ); - double dy = fabs( currentExtent.height() / 4 ); + double dx = std::fabs( currentExtent.width() / 4 ); + double dy = std::fabs( currentExtent.height() / 4 ); switch ( e->key() ) { @@ -1420,7 +1420,7 @@ void QgsMapCanvas::wheelEvent( QWheelEvent *e ) double zoomFactor = mWheelZoomFactor; // "Normal" mouse have an angle delta of 120, precision mouses provide data faster, in smaller steps - zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * fabs( e->angleDelta().y() ); + zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 120.0 * std::fabs( e->angleDelta().y() ); if ( e->modifiers() & Qt::ControlModifier ) { diff --git a/src/gui/qgsmapcanvasannotationitem.cpp b/src/gui/qgsmapcanvasannotationitem.cpp index 6a7e51ba0e6..ca0ce5f9bd1 100644 --- a/src/gui/qgsmapcanvasannotationitem.cpp +++ b/src/gui/qgsmapcanvasannotationitem.cpp @@ -187,7 +187,7 @@ QgsMapCanvasAnnotationItem::MouseMoveAction QgsMapCanvasAnnotationItem::moveActi int cursorSensitivity = 7; if ( mAnnotation && mAnnotation->hasFixedMapPosition() && - fabs( itemPos.x() ) < cursorSensitivity && fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin + std::fabs( itemPos.x() ) < cursorSensitivity && std::fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin { return MoveMapPosition; } @@ -196,10 +196,10 @@ QgsMapCanvasAnnotationItem::MouseMoveAction QgsMapCanvasAnnotationItem::moveActi QSizeF frameSize = mAnnotation ? mAnnotation->frameSize() : QSizeF( 0, 0 ); bool left, right, up, down; - left = fabs( itemPos.x() - offset.x() ) < cursorSensitivity; - right = fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity; - up = fabs( itemPos.y() - offset.y() ) < cursorSensitivity; - down = fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity; + left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity; + right = std::fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity; + up = std::fabs( itemPos.y() - offset.y() ) < cursorSensitivity; + down = std::fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity; if ( left && up ) { diff --git a/src/gui/qgsmaptoolextent.cpp b/src/gui/qgsmaptoolextent.cpp index d895883f4aa..d3b41e3de91 100644 --- a/src/gui/qgsmaptoolextent.cpp +++ b/src/gui/qgsmaptoolextent.cpp @@ -47,7 +47,7 @@ void QgsMapToolExtent::canvasMoveEvent( QgsMapMouseEvent *e ) QgsPointXY p = toMapCoordinates( e->pos() ); if ( mRatio.width() > 0 && mRatio.height() > 0 ) { - double width = fabs( p.x() - mStartPoint.x() ); + double width = std::fabs( p.x() - mStartPoint.x() ); double height = width * ( mRatio.width() / mRatio.height() ); if ( p.y() - mStartPoint.y() < 0 ) height *= -1; @@ -98,7 +98,7 @@ void QgsMapToolExtent::calculateEndPoint( QgsPointXY &point ) { if ( mRatio.width() > 0 && mRatio.height() > 0 ) { - double width = fabs( point.x() - mStartPoint.x() ); + double width = std::fabs( point.x() - mStartPoint.x() ); double height = width * mRatio.height() / mRatio.width(); if ( point.y() - mStartPoint.y() < 0 ) height *= -1; diff --git a/src/gui/qgsscalecombobox.cpp b/src/gui/qgsscalecombobox.cpp index 3bd1145ca5c..6ff6afb1b85 100644 --- a/src/gui/qgsscalecombobox.cpp +++ b/src/gui/qgsscalecombobox.cpp @@ -98,7 +98,7 @@ void QgsScaleComboBox::showPopup() { parts = itemText( i ).split( ':' ); nextScale = parts.at( 1 ).toLong( &ok ); - delta = labs( currScale - nextScale ); + delta = std::labs( currScale - nextScale ); if ( delta < min ) { min = delta; diff --git a/src/gui/raster/qgsrasterhistogramwidget.cpp b/src/gui/raster/qgsrasterhistogramwidget.cpp index e63e9c0ecdf..50a31768ee6 100644 --- a/src/gui/raster/qgsrasterhistogramwidget.cpp +++ b/src/gui/raster/qgsrasterhistogramwidget.cpp @@ -1005,7 +1005,7 @@ QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 1 current += diff; if ( current > target ) { - closest = ( fabs( target - current + diff ) < fabs( target - current ) ) ? current - diff : current; + closest = ( std::fabs( target - current + diff ) < std::fabs( target - current ) ) ? current - diff : current; break; } } diff --git a/src/gui/symbology/qgsgraduatedhistogramwidget.cpp b/src/gui/symbology/qgsgraduatedhistogramwidget.cpp index bef17e88ab0..e316e57be6e 100644 --- a/src/gui/symbology/qgsgraduatedhistogramwidget.cpp +++ b/src/gui/symbology/qgsgraduatedhistogramwidget.cpp @@ -158,11 +158,11 @@ void QgsGraduatedHistogramWidget::findClosestRange( double value, int &closestRa int pressedPixel = mPlot->canvasMap( QwtPlot::xBottom ).transform( value ); for ( int i = 0; i < ranges.count() - 1; ++i ) { - if ( fabs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance ) + if ( std::fabs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance ) { closestRangeIndex = i; - minDistance = fabs( mPressedValue - ranges.at( i ).upperValue() ); - pixelDistance = fabs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) ); + minDistance = std::fabs( mPressedValue - ranges.at( i ).upperValue() ); + pixelDistance = std::fabs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) ); } } } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp index a458195fc0f..0ac17d81f07 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp @@ -111,7 +111,7 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart ); - if ( fabs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() && + if ( std::fabs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() && QgsGeometryCheckerUtils::pointsFuzzyEqual( part->centroid(), overlapError->location(), QgsGeometryCheckPrecision::reducedTolerance() ) ) { interPart = part; diff --git a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h index 34cbb40fe95..79508a35043 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h @@ -38,7 +38,7 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError other->featureId() == featureId() && err->otherId() == otherId() && QgsGeometryCheckerUtils::pointsFuzzyEqual( location(), other->location(), QgsGeometryCheckPrecision::reducedTolerance() ) && - fabs( value().toDouble() - other->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance(); + std::fabs( value().toDouble() - other->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance(); } bool closeMatch( QgsGeometryCheckError *other ) const override diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp index 29a9f73a1f5..64a7f70deb0 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp @@ -54,7 +54,7 @@ namespace QgsGeometryCheckerUtils static inline double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q ) { - double nom = fabs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() ); + double nom = std::fabs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() ); double dx = p2.x() - p1.x(); double dy = p2.y() - p1.y(); return nom / qSqrt( dx * dx + dy * dy ); diff --git a/src/plugins/georeferencer/qgsgeorefdelegates.cpp b/src/plugins/georeferencer/qgsgeorefdelegates.cpp index 3004dc3d9d0..fa3f36113d0 100644 --- a/src/plugins/georeferencer/qgsgeorefdelegates.cpp +++ b/src/plugins/georeferencer/qgsgeorefdelegates.cpp @@ -77,7 +77,7 @@ double QgsDmsAndDdDelegate::dmsToDD( const QString &dms ) const { QStringList list = dms.split( ' ' ); QString tmpStr = list.at( 0 ); - double res = fabs( tmpStr.toDouble() ); + double res = std::fabs( tmpStr.toDouble() ); tmpStr = list.value( 1 ); if ( !tmpStr.isEmpty() ) diff --git a/src/plugins/georeferencer/qgsgeoreftransform.cpp b/src/plugins/georeferencer/qgsgeoreftransform.cpp index 84dbb40d6e9..f44b9ba2859 100644 --- a/src/plugins/georeferencer/qgsgeoreftransform.cpp +++ b/src/plugins/georeferencer/qgsgeoreftransform.cpp @@ -379,8 +379,8 @@ int QgsLinearGeorefTransform::linear_transform( void *pTransformerArg, int bDstT else { // Guard against division by zero - if ( fabs( t->scaleX ) < std::numeric_limits::epsilon() || - fabs( t->scaleY ) < std::numeric_limits::epsilon() ) + if ( std::fabs( t->scaleX ) < std::numeric_limits::epsilon() || + std::fabs( t->scaleY ) < std::numeric_limits::epsilon() ) { for ( int i = 0; i < nPointCount; ++i ) { @@ -460,7 +460,7 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs else { // Guard against division by zero - if ( fabs( s ) < std::numeric_limits::epsilon() ) + if ( std::fabs( s ) < std::numeric_limits::epsilon() ) { for ( int i = 0; i < nPointCount; ++i ) { @@ -602,7 +602,7 @@ bool QgsProjectiveGeorefTransform::updateParametersFromGCPs( const QVector::epsilon() ) + if ( std::fabs( det ) < 1024.0 * std::numeric_limits::epsilon() ) { mParameters.hasInverse = false; } @@ -654,7 +654,7 @@ int QgsProjectiveGeorefTransform::projective_transform( void *pTransformerArg, i { double Z = x[i] * H[6] + y[i] * H[7] + H[8]; // Projects to infinity? - if ( fabs( Z ) < 1024.0 * std::numeric_limits::epsilon() ) + if ( std::fabs( Z ) < 1024.0 * std::numeric_limits::epsilon() ) { panSuccess[i] = false; continue; diff --git a/src/plugins/georeferencer/qgsleastsquares.cpp b/src/plugins/georeferencer/qgsleastsquares.cpp index ac218fdcd36..6d1ee228e23 100644 --- a/src/plugins/georeferencer/qgsleastsquares.cpp +++ b/src/plugins/georeferencer/qgsleastsquares.cpp @@ -57,8 +57,8 @@ void QgsLeastSquares::linear( const QVector &mapCoords, origin.setX( aX ); origin.setY( aY ); - pixelXSize = fabs( bX ); - pixelYSize = fabs( bY ); + pixelXSize = std::fabs( bX ); + pixelYSize = std::fabs( bY ); } diff --git a/src/plugins/georeferencer/qgsmapcoordsdialog.cpp b/src/plugins/georeferencer/qgsmapcoordsdialog.cpp index 5ab6af74d98..a23f5a9ef1f 100644 --- a/src/plugins/georeferencer/qgsmapcoordsdialog.cpp +++ b/src/plugins/georeferencer/qgsmapcoordsdialog.cpp @@ -137,7 +137,7 @@ double QgsMapCoordsDialog::dmsToDD( const QString &dms ) { QStringList list = dms.split( ' ' ); QString tmpStr = list.at( 0 ); - double res = fabs( tmpStr.toDouble() ); + double res = std::fabs( tmpStr.toDouble() ); tmpStr = list.value( 1 ); if ( !tmpStr.isEmpty() ) diff --git a/src/plugins/grass/qgsgrassnewmapset.cpp b/src/plugins/grass/qgsgrassnewmapset.cpp index f336a69db5d..dd096f1f601 100644 --- a/src/plugins/grass/qgsgrassnewmapset.cpp +++ b/src/plugins/grass/qgsgrassnewmapset.cpp @@ -1087,7 +1087,7 @@ void QgsGrassNewMapset::drawRegion() double x1 = points[i].x(); double x2 = points[i + 1].x(); - if ( fabs( x2 - x1 ) > 150 ) + if ( std::fabs( x2 - x1 ) > 150 ) { if ( x2 < x1 ) { diff --git a/src/plugins/grass/qtermwidget/ColorScheme.cpp b/src/plugins/grass/qtermwidget/ColorScheme.cpp index 008399b94fc..563ce71d3a9 100644 --- a/src/plugins/grass/qtermwidget/ColorScheme.cpp +++ b/src/plugins/grass/qtermwidget/ColorScheme.cpp @@ -196,9 +196,9 @@ ColorEntry ColorScheme::colorEntry( int index, uint randomSeed ) const QColor &color = entry.color; - int newHue = abs( ( color.hue() + hueDifference ) % MAX_HUE ); - int newValue = qMin( abs( color.value() + valueDifference ), 255 ); - int newSaturation = qMin( abs( color.saturation() + saturationDifference ), 255 ); + int newHue = std::abs( ( color.hue() + hueDifference ) % MAX_HUE ); + int newValue = qMin( std::abs( color.value() + valueDifference ), 255 ); + int newSaturation = qMin( std::abs( color.saturation() + saturationDifference ), 255 ); color.setHsv( newHue, newSaturation, newValue ); } diff --git a/src/plugins/grass/qtermwidget/TerminalDisplay.cpp b/src/plugins/grass/qtermwidget/TerminalDisplay.cpp index 73fcab70119..320c3278b04 100644 --- a/src/plugins/grass/qtermwidget/TerminalDisplay.cpp +++ b/src/plugins/grass/qtermwidget/TerminalDisplay.cpp @@ -817,7 +817,7 @@ void TerminalDisplay::scrollImage( int lines, const QRect &screenWindowRegion ) if ( lines == 0 || _image == 0 || !region.isValid() - || ( region.top() + abs( lines ) ) >= region.bottom() + || ( region.top() + std::abs( lines ) ) >= region.bottom() || this->_lines <= region.height() ) return; // hide terminal size label to prevent it being scrolled @@ -848,10 +848,10 @@ void TerminalDisplay::scrollImage( int lines, const QRect &screenWindowRegion ) scrollRect.setRight( width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP ); } void *firstCharPos = &_image[ region.top() * this->_columns ]; - void *lastCharPos = &_image[( region.top() + abs( lines ) ) * this->_columns ]; + void *lastCharPos = &_image[( region.top() + std::abs( lines ) ) * this->_columns ]; int top = _topMargin + ( region.top() * _fontHeight ); - int linesToMove = region.height() - abs( lines ); + int linesToMove = region.height() - std::abs( lines ); int bytesToMove = linesToMove * this->_columns * sizeof( Character ); @@ -884,7 +884,7 @@ void TerminalDisplay::scrollImage( int lines, const QRect &screenWindowRegion ) memmove( lastCharPos, firstCharPos, bytesToMove ); //set region of the display to scroll - scrollRect.setTop( top + abs( lines ) * _fontHeight ); + scrollRect.setTop( top + std::abs( lines ) * _fontHeight ); } scrollRect.setHeight( linesToMove * _fontHeight ); @@ -2392,7 +2392,7 @@ void TerminalDisplay::wheelEvent( QWheelEvent *ev ) // QWheelEvent::delta() gives rotation in eighths of a degree int wheelDegrees = ev->delta() / 8; - int linesToScroll = abs( wheelDegrees ) / 5; + int linesToScroll = std::abs( wheelDegrees ) / 5; QKeyEvent keyScrollEvent( QEvent::KeyPress, key, Qt::NoModifier ); diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 03d9fee4ad2..11ce0732fb9 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -528,7 +528,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi int srcBottom = ySize() - 1; int srcRight = xSize() - 1; - // Note: original approach for xRes < srcXRes || yRes < fabs( srcYRes ) was to avoid + // Note: original approach for xRes < srcXRes || yRes < std::fabs( srcYRes ) was to avoid // second resampling and read with GDALRasterIO to another temporary data block // extended to fit src grid. The problem was that with src resolution much bigger // than dst res, the target could become very large @@ -587,7 +587,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi { tmpWidth = static_cast( std::round( srcWidth * srcXRes / xRes ) ); } - if ( yRes > fabs( srcYRes ) ) + if ( yRes > std::fabs( srcYRes ) ) { tmpHeight = static_cast( std::round( -1.*srcHeight * srcYRes / yRes ) ); } @@ -1291,8 +1291,8 @@ bool QgsGdalProvider::hasHistogram( int bandNo, // min/max are stored as text in aux file => use threshold if ( myBinCount != myHistogram.binCount || - fabs( myMinVal - myExpectedMinVal ) > fabs( myExpectedMinVal ) / 10e6 || - fabs( myMaxVal - myExpectedMaxVal ) > fabs( myExpectedMaxVal ) / 10e6 ) + std::fabs( myMinVal - myExpectedMinVal ) > std::fabs( myExpectedMinVal ) / 10e6 || + std::fabs( myMaxVal - myExpectedMaxVal ) > std::fabs( myExpectedMaxVal ) / 10e6 ) { QgsDebugMsg( QString( "Params do not match binCount: %1 x %2, minVal: %3 x %4, maxVal: %5 x %6" ).arg( myBinCount ).arg( myHistogram.binCount ).arg( myMinVal ).arg( myExpectedMinVal ).arg( myMaxVal ).arg( myExpectedMaxVal ) ); return false; diff --git a/src/providers/grass/qgsgrassrasterprovider.cpp b/src/providers/grass/qgsgrassrasterprovider.cpp index 370acbd8f2b..3e95e76530d 100644 --- a/src/providers/grass/qgsgrassrasterprovider.cpp +++ b/src/providers/grass/qgsgrassrasterprovider.cpp @@ -123,7 +123,7 @@ QgsGrassRasterProvider::QgsGrassRasterProvider( QString const &uri ) else if ( mGrassDataType == DCELL_TYPE ) { // Don't use numeric limits, raster layer is using - // fabs( myValue - mNoDataValue ) <= TINY_VALUE + // std::fabs( myValue - mNoDataValue ) <= TINY_VALUE // if the mNoDataValue would be a limit, the subtraction could overflow. // No data value is shown in GUI, use some nice number. // Choose values with small representation error. diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp index 527c8441319..b3e23e5593d 100644 --- a/src/providers/wcs/qgswcsprovider.cpp +++ b/src/providers/wcs/qgswcsprovider.cpp @@ -1413,8 +1413,8 @@ QgsRasterIdentifyResult QgsWcsProvider::identify( const QgsPointXY &point, QgsRa point.x() + xRes * width / 2, point.y() + yRes * height / 2 ); - double xResDiff = fabs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); - double yResDiff = fabs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); + double xResDiff = std::fabs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); + double yResDiff = std::fabs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); if ( !mCachedGdalDataset || !mCachedViewExtent.contains( point ) || @@ -1434,8 +1434,8 @@ QgsRasterIdentifyResult QgsWcsProvider::identify( const QgsPointXY &point, QgsRa double xRes = finalExtent.width() / width; double yRes = finalExtent.height() / height; QgsDebugMsg( QString( "width = %1 height = %2 xRes = %3 yRes = %4" ).arg( finalExtent.width() ).arg( finalExtent.height() ).arg( xRes ).arg( yRes ) ); - double xResDiff = fabs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); - double yResDiff = fabs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); + double xResDiff = std::fabs( mCachedViewExtent.width() / mCachedViewWidth - xRes ); + double yResDiff = std::fabs( mCachedViewExtent.height() / mCachedViewHeight - yRes ); QgsDebugMsg( QString( "xRes diff = %1 yRes diff = %2 relative xResDiff = %3 relative yResDiff = %4" ).arg( xResDiff ).arg( yResDiff ).arg( xResDiff / xRes ).arg( yResDiff / yRes ) ); if ( !mCachedGdalDataset || !mCachedViewExtent.contains( point ) || diff --git a/src/providers/wfs/qgswfscapabilities.cpp b/src/providers/wfs/qgswfscapabilities.cpp index fc20e4911a0..89e317be8d4 100644 --- a/src/providers/wfs/qgswfscapabilities.cpp +++ b/src/providers/wfs/qgswfscapabilities.cpp @@ -403,10 +403,10 @@ void QgsWfsCapabilities::capabilitiesReplyFinished() QgsDebugMsg( ptMinBack.toString() ); QgsDebugMsg( ptMaxBack.toString() ); - if ( fabs( featureType.bbox.xMinimum() - ptMinBack.x() ) < 1e-5 && - fabs( featureType.bbox.yMinimum() - ptMinBack.y() ) < 1e-5 && - fabs( featureType.bbox.xMaximum() - ptMaxBack.x() ) < 1e-5 && - fabs( featureType.bbox.yMaximum() - ptMaxBack.y() ) < 1e-5 ) + if ( std::fabs( featureType.bbox.xMinimum() - ptMinBack.x() ) < 1e-5 && + std::fabs( featureType.bbox.yMinimum() - ptMinBack.y() ) < 1e-5 && + std::fabs( featureType.bbox.xMaximum() - ptMaxBack.x() ) < 1e-5 && + std::fabs( featureType.bbox.yMaximum() - ptMaxBack.y() ) < 1e-5 ) { QgsDebugMsg( "Values of LatLongBoundingBox are consistent with WGS84 long/lat bounds, so as the CRS is projected, assume they are indeed in WGS84 and not in the CRS units" ); featureType.bboxSRSIsWGS84 = true; diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index a98c38d95ab..c4236fc7dc4 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -89,8 +89,8 @@ struct LessThanTileRequest QPointF p1 = req1.rect.center(); QPointF p2 = req2.rect.center(); // using chessboard distance (loading order more natural than euclidean/manhattan distance) - double d1 = qMax( fabs( center.x() - p1.x() ), fabs( center.y() - p1.y() ) ); - double d2 = qMax( fabs( center.x() - p2.x() ), fabs( center.y() - p2.y() ) ); + double d1 = qMax( std::fabs( center.x() - p1.x() ), std::fabs( center.y() - p1.y() ) ); + double d2 = qMax( std::fabs( center.x() - p2.x() ), std::fabs( center.y() - p2.y() ) ); return d1 < d2; } }; @@ -4044,7 +4044,7 @@ static QString formatDouble( double x ) { if ( x == 0.0 ) return QStringLiteral( "0" ); - const int numberOfDecimals = qMax( 0, 19 - static_cast( ceil( log10( fabs( x ) ) ) ) ); + const int numberOfDecimals = qMax( 0, 19 - static_cast( ceil( log10( std::fabs( x ) ) ) ) ); return qgsDoubleToString( x, numberOfDecimals ); } diff --git a/tests/bench/qgsbench.cpp b/tests/bench/qgsbench.cpp index 79fc1043c6f..50f2e0b709f 100644 --- a/tests/bench/qgsbench.cpp +++ b/tests/bench/qgsbench.cpp @@ -243,7 +243,7 @@ void QgsBench::render() { for ( int i = 0; i < mTimes.size(); i++ ) { - double d = fabs( avg[t] - mTimes.at( i )[t] ); + double d = std::fabs( avg[t] - mTimes.at( i )[t] ); stdev[t] += pow( d, 2 ); if ( i == 0 || d > maxdev[t] ) maxdev[t] = d; } diff --git a/tests/src/core/testqgscomposermap.cpp b/tests/src/core/testqgscomposermap.cpp index 2bfb11a17bc..98b4c13cce5 100644 --- a/tests/src/core/testqgscomposermap.cpp +++ b/tests/src/core/testqgscomposermap.cpp @@ -216,14 +216,14 @@ void TestQgsComposerMap::mapPolygonVertices() QPolygonF visibleExtent = mComposerMap->visibleExtentPolygon(); //vertices should be returned in clockwise order starting at the top-left point - QVERIFY( fabs( visibleExtent[0].x() - 781662.375 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[0].y() - 3345223.125 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[1].x() - 793062.375 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[1].y() - 3345223.125 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[2].x() - 793062.375 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[2].y() - 3339523.125 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[3].x() - 781662.375 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[3].y() - 3339523.125 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[0].x() - 781662.375 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[0].y() - 3345223.125 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[1].x() - 793062.375 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[1].y() - 3345223.125 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[2].x() - 793062.375 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[2].y() - 3339523.125 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[3].x() - 781662.375 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[3].y() - 3339523.125 ) < 0.001 ); //polygon should be closed QVERIFY( visibleExtent.isClosed() ); @@ -233,14 +233,14 @@ void TestQgsComposerMap::mapPolygonVertices() visibleExtent = mComposerMap->visibleExtentPolygon(); //vertices should be returned in clockwise order starting at the top-left point - QVERIFY( fabs( visibleExtent[0].x() - 781254.0735015 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[0].y() - 3344190.0324834 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[1].x() - 792480.881886 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[1].y() - 3346169.62171 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[2].x() - 793470.676499 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[2].y() - 3340556.21752 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[3].x() - 782243.868114 ) < 0.001 ); - QVERIFY( fabs( visibleExtent[3].y() - 3338576.62829 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[0].x() - 781254.0735015 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[0].y() - 3344190.0324834 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[1].x() - 792480.881886 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[1].y() - 3346169.62171 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[2].x() - 793470.676499 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[2].y() - 3340556.21752 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[3].x() - 782243.868114 ) < 0.001 ); + QVERIFY( std::fabs( visibleExtent[3].y() - 3338576.62829 ) < 0.001 ); //polygon should be closed QVERIFY( visibleExtent.isClosed() ); diff --git a/tests/src/core/testqgsrasterlayer.cpp b/tests/src/core/testqgsrasterlayer.cpp index 42ce3b6daf9..e93c7607f2f 100644 --- a/tests/src/core/testqgsrasterlayer.cpp +++ b/tests/src/core/testqgsrasterlayer.cpp @@ -435,19 +435,19 @@ void TestQgsRasterLayer::checkScaleOffset() QVERIFY( myRasterLayer->height() == 10 ); //QVERIFY( myStatistics.elementCount == 100 ); double minVal = 0.0; - mReport += QStringLiteral( "min = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.minimumValue ).arg( minVal ).arg( fabs( myStatistics.minimumValue - minVal ) ); + mReport += QStringLiteral( "min = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.minimumValue ).arg( minVal ).arg( std::fabs( myStatistics.minimumValue - minVal ) ); double maxVal = 9.0; - mReport += QStringLiteral( "max = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.maximumValue ).arg( maxVal ).arg( fabs( myStatistics.maximumValue - maxVal ) ); + mReport += QStringLiteral( "max = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.maximumValue ).arg( maxVal ).arg( std::fabs( myStatistics.maximumValue - maxVal ) ); double meanVal = 4.5; - mReport += QStringLiteral( "min = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.mean ).arg( meanVal ).arg( fabs( myStatistics.mean - meanVal ) ); - QVERIFY( fabs( myStatistics.minimumValue - minVal ) < 0.0000001 ); - QVERIFY( fabs( myStatistics.maximumValue - maxVal ) < 0.0000001 ); - QVERIFY( fabs( myStatistics.mean - meanVal ) < 0.0000001 ); + mReport += QStringLiteral( "min = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.mean ).arg( meanVal ).arg( std::fabs( myStatistics.mean - meanVal ) ); + QVERIFY( std::fabs( myStatistics.minimumValue - minVal ) < 0.0000001 ); + QVERIFY( std::fabs( myStatistics.maximumValue - maxVal ) < 0.0000001 ); + QVERIFY( std::fabs( myStatistics.mean - meanVal ) < 0.0000001 ); double stdDev = 2.87228615; // TODO: verify why GDAL stdDev is so different from generic (2.88675) - mReport += QStringLiteral( "stdDev = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.stdDev ).arg( stdDev ).arg( fabs( myStatistics.stdDev - stdDev ) ); - QVERIFY( fabs( myStatistics.stdDev - stdDev ) < 0.0000001 ); + mReport += QStringLiteral( "stdDev = %1 expected = %2 diff = %3
    \n" ).arg( myStatistics.stdDev ).arg( stdDev ).arg( std::fabs( myStatistics.stdDev - stdDev ) ); + QVERIFY( std::fabs( myStatistics.stdDev - stdDev ) < 0.0000001 ); QgsRasterDataProvider *myProvider = myRasterLayer->dataProvider(); QgsPointXY myPoint( 1535030, 5083350 ); @@ -474,8 +474,8 @@ void TestQgsRasterLayer::checkScaleOffset() double value = values.value( bandNo ).toDouble(); valueString = QgsRasterBlock::printValue( value ); mReport += QStringLiteral( " %1 = %2
    \n" ).arg( myProvider->generateBandName( bandNo ), valueString ); - mReport += QStringLiteral( " value = %1 expected = %2 diff = %3
    \n" ).arg( value ).arg( expected ).arg( fabs( value - expected ) ); - QVERIFY( fabs( value - expected ) < 0.0000001 ); + mReport += QStringLiteral( " value = %1 expected = %2 diff = %3
    \n" ).arg( value ).arg( expected ).arg( std::fabs( value - expected ) ); + QVERIFY( std::fabs( value - expected ) < 0.0000001 ); } } } diff --git a/tests/src/providers/grass/testqgsgrassprovider.cpp b/tests/src/providers/grass/testqgsgrassprovider.cpp index 0047d9a66b6..9296933e0a5 100644 --- a/tests/src/providers/grass/testqgsgrassprovider.cpp +++ b/tests/src/providers/grass/testqgsgrassprovider.cpp @@ -340,7 +340,7 @@ bool TestQgsGrassProvider::compare( const QStringList &expected, const QStringLi bool TestQgsGrassProvider::compare( double expected, double got, bool &ok ) { - if ( fabs( got - expected ) > TINY_VALUE ) + if ( std::fabs( got - expected ) > TINY_VALUE ) { ok = false; return false; From 031bf41d1b8f501a0ece0e10fb75f664bad80352 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 02:53:39 +1000 Subject: [PATCH 117/364] Swap q(pow) -> std::pow --- .../interpolation/CloughTocherInterpolator.cc | 2 +- src/analysis/interpolation/MathUtils.cc | 2 +- .../interpolation/qgsidwinterpolator.cpp | 2 +- src/analysis/raster/qgskde.cpp | 22 +++++++++---------- src/analysis/raster/qgsrastermatrix.cpp | 2 +- src/analysis/vector/qgszonalstatistics.cpp | 2 +- src/app/qgsdecorationgrid.cpp | 2 +- src/app/qgsdecorationscalebar.cpp | 2 +- src/core/composer/qgscomposermapgrid.cpp | 2 +- src/core/composer/qgscomposernodesitem.cpp | 2 +- src/core/composer/qgscomposerscalebar.cpp | 6 ++--- src/core/effects/qgsimageoperation.cpp | 2 +- src/core/effects/qgsimageoperation.h | 2 +- src/core/expression/qgsexpressionfunction.cpp | 4 ++-- src/core/expression/qgsexpressionnodeimpl.cpp | 2 +- src/core/geometry/qgsellipse.cpp | 2 +- src/core/geometry/qgsgeometryutils.cpp | 8 +++---- src/core/geometry/qgsgeos.cpp | 4 ++-- src/core/geometry/qgslinestring.cpp | 12 +++++----- src/core/pal/feature.cpp | 6 ++--- src/core/pal/pal.cpp | 2 +- src/core/pal/rtree.hpp | 2 +- src/core/qgis.h | 4 ++-- src/core/qgscoordinatetransform.cpp | 2 +- src/core/qgsfield.cpp | 2 +- src/core/qgshistogram.cpp | 2 +- src/core/qgspointxy.cpp | 20 ++++++++--------- src/core/qgspropertytransformer.cpp | 4 ++-- src/core/qgsscalecalculator.cpp | 4 ++-- src/core/qgsstatisticalsummary.cpp | 4 ++-- src/core/qgstextrenderer.cpp | 2 +- .../raster/qgsbrightnesscontrastfilter.cpp | 2 +- src/core/raster/qgscubicrasterresampler.cpp | 2 +- src/core/raster/qgshuesaturationfilter.cpp | 2 +- src/core/raster/qgsrasterchecker.cpp | 2 +- src/core/symbology/qgsfillsymbollayer.cpp | 2 +- src/core/symbology/qgsheatmaprenderer.cpp | 8 +++---- src/core/symbology/qgssymbollayerutils.cpp | 2 +- src/gui/editorwidgets/qgsdoublespinbox.cpp | 2 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 12 +++++----- src/gui/qgscurveeditorwidget.cpp | 2 +- src/gui/qgsgradientcolorrampdialog.cpp | 8 +++---- .../qgsgraduatedsymbolrendererwidget.cpp | 2 +- .../checks/qgsgeometrycheck.cpp | 4 ++-- .../georeferencer/qgsresidualplotitem.cpp | 8 +++---- src/plugins/grass/qgsgrassmapcalc.cpp | 20 ++++++++--------- src/providers/grass/qgis.g.info.c | 4 ++-- src/providers/wms/qgswmsprovider.cpp | 2 +- tests/bench/qgsbench.cpp | 2 +- tests/src/core/testqgsmapsettings.cpp | 2 +- 50 files changed, 112 insertions(+), 112 deletions(-) diff --git a/src/analysis/interpolation/CloughTocherInterpolator.cc b/src/analysis/interpolation/CloughTocherInterpolator.cc index c15bdfe2b70..d66c9421f95 100644 --- a/src/analysis/interpolation/CloughTocherInterpolator.cc +++ b/src/analysis/interpolation/CloughTocherInterpolator.cc @@ -64,7 +64,7 @@ double CloughTocherInterpolator::calcBernsteinPoly( int n, int i, int j, int k, return 0; } - double result = MathUtils::faculty( n ) * qPow( u, i ) * qPow( v, j ) * qPow( w, k ) / ( MathUtils::faculty( i ) * MathUtils::faculty( j ) * MathUtils::faculty( k ) ); + double result = MathUtils::faculty( n ) * std::pow( u, i ) * std::pow( v, j ) * std::pow( w, k ) / ( MathUtils::faculty( i ) * MathUtils::faculty( j ) * MathUtils::faculty( k ) ); return result; } diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index 80d3391440b..2b81fa59c61 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -107,7 +107,7 @@ double MathUtils::calcBernsteinPoly( int n, int i, double t ) return 0; } - return lower( n, i ) * qPow( t, i ) * qPow( ( 1 - t ), ( n - i ) ); + return lower( n, i ) * std::pow( t, i ) * std::pow( ( 1 - t ), ( n - i ) ); } double MathUtils::cFDerBernsteinPoly( int n, int i, double t ) diff --git a/src/analysis/interpolation/qgsidwinterpolator.cpp b/src/analysis/interpolation/qgsidwinterpolator.cpp index 37ddaf54627..2c71ad8b0b1 100644 --- a/src/analysis/interpolation/qgsidwinterpolator.cpp +++ b/src/analysis/interpolation/qgsidwinterpolator.cpp @@ -50,7 +50,7 @@ int QgsIDWInterpolator::interpolatePoint( double x, double y, double &result ) result = vertex_it.z; return 0; } - currentWeight = 1 / ( pow( distance, mDistanceCoefficient ) ); + currentWeight = 1 / ( std::pow( distance, mDistanceCoefficient ) ); sumCounter += ( currentWeight * vertex_it.z ); sumDenominator += currentWeight; } diff --git a/src/analysis/raster/qgskde.cpp b/src/analysis/raster/qgskde.cpp index 21c9a2abb63..62f5a13fb7a 100644 --- a/src/analysis/raster/qgskde.cpp +++ b/src/analysis/raster/qgskde.cpp @@ -181,7 +181,7 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::addFeature( const double pixelCentroidX = ( xPosition + xp + 0.5 ) * mPixelSize + mBounds.xMinimum(); double pixelCentroidY = ( yPosition + yp + 0.5 ) * mPixelSize + mBounds.yMinimum(); - double distance = sqrt( pow( pixelCentroidX - ( *pointIt ).x(), 2.0 ) + pow( pixelCentroidY - ( *pointIt ).y(), 2.0 ) ); + double distance = sqrt( std::pow( pixelCentroidX - ( *pointIt ).x(), 2.0 ) + std::pow( pixelCentroidY - ( *pointIt ).y(), 2.0 ) ); // is pixel outside search bandwidth of feature? if ( distance > radius ) @@ -324,13 +324,13 @@ double QgsKernelDensityEstimation::quarticKernel( const double distance, const d case OutputScaled: { // Normalizing constant - double k = 116. / ( 5. * M_PI * pow( bandwidth, 2 ) ); + double k = 116. / ( 5. * M_PI * std::pow( bandwidth, 2 ) ); // Derived from Wand and Jones (1995), p. 175 - return k * ( 15. / 16. ) * pow( 1. - pow( distance / bandwidth, 2 ), 2 ); + return k * ( 15. / 16. ) * std::pow( 1. - std::pow( distance / bandwidth, 2 ), 2 ); } case OutputRaw: - return pow( 1. - pow( distance / bandwidth, 2 ), 2 ); + return pow( 1. - std::pow( distance / bandwidth, 2 ), 2 ); } return 0.0; //no, seriously, I told you NO WARNINGS! } @@ -342,13 +342,13 @@ double QgsKernelDensityEstimation::triweightKernel( const double distance, const case OutputScaled: { // Normalizing constant - double k = 128. / ( 35. * M_PI * pow( bandwidth, 2 ) ); + double k = 128. / ( 35. * M_PI * std::pow( bandwidth, 2 ) ); // Derived from Wand and Jones (1995), p. 175 - return k * ( 35. / 32. ) * pow( 1. - pow( distance / bandwidth, 2 ), 3 ); + return k * ( 35. / 32. ) * std::pow( 1. - std::pow( distance / bandwidth, 2 ), 3 ); } case OutputRaw: - return pow( 1. - pow( distance / bandwidth, 2 ), 3 ); + return pow( 1. - std::pow( distance / bandwidth, 2 ), 3 ); } return 0.0; // this is getting ridiculous... don't you ever listen to a word I say? } @@ -360,13 +360,13 @@ double QgsKernelDensityEstimation::epanechnikovKernel( const double distance, co case OutputScaled: { // Normalizing constant - double k = 8. / ( 3. * M_PI * pow( bandwidth, 2 ) ); + double k = 8. / ( 3. * M_PI * std::pow( bandwidth, 2 ) ); // Derived from Wand and Jones (1995), p. 175 - return k * ( 3. / 4. ) * ( 1. - pow( distance / bandwidth, 2 ) ); + return k * ( 3. / 4. ) * ( 1. - std::pow( distance / bandwidth, 2 ) ); } case OutputRaw: - return ( 1. - pow( distance / bandwidth, 2 ) ); + return ( 1. - std::pow( distance / bandwidth, 2 ) ); } return 0.0; // la la la i'm not listening @@ -383,7 +383,7 @@ double QgsKernelDensityEstimation::triangularKernel( const double distance, cons if ( mDecay >= 0 ) { - double k = 3. / ( ( 1. + 2. * mDecay ) * M_PI * pow( bandwidth, 2 ) ); + double k = 3. / ( ( 1. + 2. * mDecay ) * M_PI * std::pow( bandwidth, 2 ) ); // Derived from Wand and Jones (1995), p. 175 (with addition of decay parameter) return k * ( 1. - ( 1. - mDecay ) * ( distance / bandwidth ) ); diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index 7f37a773851..f8b033ee865 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -292,7 +292,7 @@ double QgsRasterMatrix::calculateTwoArgumentOp( TwoArgOperator op, double arg1, } else { - return qPow( arg1, arg2 ); + return pow( arg1, arg2 ); } case opEQ: return ( arg1 == arg2 ? 1.0 : 0.0 ); diff --git a/src/analysis/vector/qgszonalstatistics.cpp b/src/analysis/vector/qgszonalstatistics.cpp index 6658ba07907..7513c925b63 100644 --- a/src/analysis/vector/qgszonalstatistics.cpp +++ b/src/analysis/vector/qgszonalstatistics.cpp @@ -309,7 +309,7 @@ int QgsZonalStatistics::calculateStatistics( QgsFeedback *feedback ) double variance = sumSquared / featureStats.values.count(); if ( mStatistics & QgsZonalStatistics::StDev ) { - double stdev = qPow( variance, 0.5 ); + double stdev = std::pow( variance, 0.5 ); changeAttributeMap.insert( stdevIndex, QVariant( stdev ) ); } if ( mStatistics & QgsZonalStatistics::Variance ) diff --git a/src/app/qgsdecorationgrid.cpp b/src/app/qgsdecorationgrid.cpp index 1c6f5e852b0..512a55acbcd 100644 --- a/src/app/qgsdecorationgrid.cpp +++ b/src/app/qgsdecorationgrid.cpp @@ -787,7 +787,7 @@ bool QgsDecorationGrid::getIntervalFromExtent( double *values, bool useXAxis ) if ( !qgsDoubleNear( interval, 0.0 ) ) { double interval2 = 0; - int factor = pow( 10, floor( log10( interval ) ) ); + int factor = std::pow( 10, floor( log10( interval ) ) ); if ( factor != 0 ) { interval2 = std::round( interval / factor ) * factor; diff --git a/src/app/qgsdecorationscalebar.cpp b/src/app/qgsdecorationscalebar.cpp index 2a927301598..2f717cb8cb7 100644 --- a/src/app/qgsdecorationscalebar.cpp +++ b/src/app/qgsdecorationscalebar.cpp @@ -163,7 +163,7 @@ void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRender // snap to integer < 10 times power of 10 if ( mSnapping ) { - double scaler = pow( 10.0, myPowerOf10 ); + double scaler = std::pow( 10.0, myPowerOf10 ); myActualSize = std::round( myActualSize / scaler ) * scaler; myScaleBarWidth = myActualSize / myMapUnitsPerPixelDouble; } diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index bd7a8f9c588..a7a39180cde 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -1445,7 +1445,7 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGr { QString hemisphere; - double coordRounded = std::round( value * pow( 10.0, mGridAnnotationPrecision ) ) / pow( 10.0, mGridAnnotationPrecision ); + double coordRounded = std::round( value * std::pow( 10.0, mGridAnnotationPrecision ) ) / std::pow( 10.0, mGridAnnotationPrecision ); if ( coord == QgsComposerMapGrid::Longitude ) { //don't use E/W suffixes if ambiguous (e.g., 180 degrees) diff --git a/src/core/composer/qgscomposernodesitem.cpp b/src/core/composer/qgscomposernodesitem.cpp index fb2597f4d5a..c6548002ea1 100644 --- a/src/core/composer/qgscomposernodesitem.cpp +++ b/src/core/composer/qgscomposernodesitem.cpp @@ -50,7 +50,7 @@ QgsComposerNodesItem::QgsComposerNodesItem( const QString &tagName, double QgsComposerNodesItem::computeDistance( QPointF pt1, QPointF pt2 ) const { - return sqrt( pow( pt1.x() - pt2.x(), 2 ) + pow( pt1.y() - pt2.y(), 2 ) ); + return sqrt( std::pow( pt1.x() - pt2.x(), 2 ) + std::pow( pt1.y() - pt2.y(), 2 ) ); } bool QgsComposerNodesItem::addNode( QPointF pt, diff --git a/src/core/composer/qgscomposerscalebar.cpp b/src/core/composer/qgscomposerscalebar.cpp index 15fa2795e4c..94d72063b44 100644 --- a/src/core/composer/qgscomposerscalebar.cpp +++ b/src/core/composer/qgscomposerscalebar.cpp @@ -262,14 +262,14 @@ void QgsComposerScaleBar::refreshDataDefinedProperty( const QgsComposerObject::D // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc inline double nextNiceNumber( double a, double d = 1 ) { - double s = qPow( 10.0, floor( log10( a ) ) ) / d; + double s = std::pow( 10.0, floor( log10( a ) ) ) / d; return ceil( a / s ) * s; } // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc inline double prevNiceNumber( double a, double d = 1 ) { - double s = qPow( 10.0, floor( log10( a ) ) ) / d; + double s = std::pow( 10.0, floor( log10( a ) ) ) / d; return floor( a / s ) * s; } @@ -474,7 +474,7 @@ void QgsComposerScaleBar::applyDefaultSize( QgsUnitTypes::DistanceUnit u ) double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier; int segmentMagnitude = floor( log10( segmentWidth ) ); - double unitsPerSegment = upperMagnitudeMultiplier * ( qPow( 10.0, segmentMagnitude ) ); + double unitsPerSegment = upperMagnitudeMultiplier * ( std::pow( 10.0, segmentMagnitude ) ); double multiplier = floor( ( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5; if ( multiplier > 0 ) diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index 0e229039c97..2e83c5b44a6 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -290,7 +290,7 @@ void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb, cons { // Raising the saturation. Use a saturation curve to prevent // clipping at maximum saturation with ugly results. - s = qMin( static_cast< int >( 255. * ( 1 - qPow( 1 - ( s / 255. ), qPow( mSaturation, 2 ) ) ) ), 255 ); + s = qMin( static_cast< int >( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturation, 2 ) ) ) ), 255 ); } if ( mColorize ) diff --git a/src/core/effects/qgsimageoperation.h b/src/core/effects/qgsimageoperation.h index 130aac9d3c3..6272b2467e1 100644 --- a/src/core/effects/qgsimageoperation.h +++ b/src/core/effects/qgsimageoperation.h @@ -375,7 +375,7 @@ class CORE_EXPORT QgsImageOperation , mSpread( spread ) , mProperties( properties ) { - mSpreadSquared = qPow( mSpread, 2.0 ); + mSpreadSquared = std::pow( mSpread, 2.0 ); } void operator()( QRgb &rgb, const int x, const int y ); diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 32ea9e8d16d..af092cb7703 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -402,7 +402,7 @@ static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionCont } // Return exponentially scaled value - return QVariant( ( ( rangeMax - rangeMin ) / pow( domainMax - domainMin, exponent ) ) * pow( val - domainMin, exponent ) + rangeMin ); + return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin ); } static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) @@ -2883,7 +2883,7 @@ static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext if ( values.length() == 2 && values.at( 1 ).toInt() != 0 ) { double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - double scaler = pow( 10.0, QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) ); + double scaler = std::pow( 10.0, QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) ); return QVariant( std::round( number * scaler ) / scaler ); } diff --git a/src/core/expression/qgsexpressionnodeimpl.cpp b/src/core/expression/qgsexpressionnodeimpl.cpp index 7db1680325a..9cf3f640475 100644 --- a/src/core/expression/qgsexpressionnodeimpl.cpp +++ b/src/core/expression/qgsexpressionnodeimpl.cpp @@ -279,7 +279,7 @@ QVariant QgsExpressionNodeBinaryOperator::evalNode( QgsExpression *parent, const ENSURE_NO_EVAL_ERROR; double fR = QgsExpressionUtils::getDoubleValue( vR, parent ); ENSURE_NO_EVAL_ERROR; - return QVariant( pow( fL, fR ) ); + return QVariant( std::pow( fL, fR ) ); } case boAnd: diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index 16e843324a7..8d7337c8779 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -64,7 +64,7 @@ QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 ); double axis_a = dist / 2.0; - double axis_b = sqrt( pow( axis_a, 2.0 ) - pow( dist_p1p2 / 2.0, 2.0 ) ); + double axis_b = sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) ); return QgsEllipse( center, axis_a, axis_b, azimuth ); } diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 4adb92da691..e30b790bb57 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -451,7 +451,7 @@ void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint & { centerX = ( pt1.x() + pt2.x() ) / 2.0; centerY = ( pt1.y() + pt2.y() ) / 2.0; - radius = sqrt( pow( centerX - pt1.x(), 2.0 ) + pow( centerY - pt1.y(), 2.0 ) ); + radius = sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) ); return; } @@ -461,8 +461,8 @@ void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint & dx31 = pt3.x() - pt1.x(); dy31 = pt3.y() - pt1.y(); - h21 = pow( dx21, 2.0 ) + pow( dy21, 2.0 ); - h31 = pow( dx31, 2.0 ) + pow( dy31, 2.0 ); + h21 = std::pow( dx21, 2.0 ) + std::pow( dy21, 2.0 ); + h31 = std::pow( dx31, 2.0 ) + std::pow( dy31, 2.0 ); // 2*Cross product, d<0 means clockwise and d>0 counterclockwise sweeping angle d = 2 * ( dx21 * dy31 - dx31 * dy21 ); @@ -477,7 +477,7 @@ void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint & // Calculate centroid coordinates and radius centerX = pt1.x() + ( h21 * dy31 - h31 * dy21 ) / d; centerY = pt1.y() - ( h21 * dx31 - h31 * dx21 ) / d; - radius = sqrt( pow( centerX - pt1.x(), 2.0 ) + pow( centerY - pt1.y(), 2.0 ) ); + radius = sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) ); } bool QgsGeometryUtils::circleClockwise( double angle1, double angle2, double angle3 ) diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index 2893a7c161a..1fa75fa7208 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -2635,7 +2635,7 @@ int QgsGeos::lineContainedInLine( const GEOSGeometry *line1, const GEOSGeometry return -1; } - double bufferDistance = pow( 10.0L, geomDigits( line2 ) - 11 ); + double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 ); GEOSGeometry *bufferGeom = GEOSBuffer_r( geosinit.ctxt, line2, bufferDistance, DEFAULT_QUADRANT_SEGMENTS ); if ( !bufferGeom ) @@ -2665,7 +2665,7 @@ int QgsGeos::pointContainedInLine( const GEOSGeometry *point, const GEOSGeometry if ( !point || !line ) return -1; - double bufferDistance = pow( 10.0L, geomDigits( line ) - 11 ); + double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 ); GEOSGeometry *lineBuffer = GEOSBuffer_r( geosinit.ctxt, line, bufferDistance, 8 ); if ( !lineBuffer ) diff --git a/src/core/geometry/qgslinestring.cpp b/src/core/geometry/qgslinestring.cpp index f7c45747f13..2019a1b5225 100644 --- a/src/core/geometry/qgslinestring.cpp +++ b/src/core/geometry/qgslinestring.cpp @@ -679,8 +679,8 @@ void QgsLineString::extend( double startDistance, double endDistance ) // start of line if ( startDistance > 0 ) { - double currentLen = sqrt( qPow( mX.at( 0 ) - mX.at( 1 ), 2 ) + - qPow( mY.at( 0 ) - mY.at( 1 ), 2 ) ); + double currentLen = sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) + + std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) ); double newLen = currentLen + startDistance; mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen; mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen; @@ -689,8 +689,8 @@ void QgsLineString::extend( double startDistance, double endDistance ) if ( endDistance > 0 ) { int last = mX.size() - 1; - double currentLen = sqrt( qPow( mX.at( last ) - mX.at( last - 1 ), 2 ) + - qPow( mY.at( last ) - mY.at( last - 1 ), 2 ) ); + double currentLen = sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) + + std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) ); double newLen = currentLen + endDistance; mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen; mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen; @@ -916,8 +916,8 @@ QgsPoint QgsLineString::centroid() const { double currentX = mX.at( i ); double currentY = mY.at( i ); - double segmentLength = sqrt( qPow( currentX - prevX, 2.0 ) + - qPow( currentY - prevY, 2.0 ) ); + double segmentLength = sqrt( std::pow( currentX - prevX, 2.0 ) + + std::pow( currentY - prevY, 2.0 ) ); if ( qgsDoubleNear( segmentLength, 0.0 ) ) continue; diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 73f423dd799..de4a8c639b0 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -1161,7 +1161,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos if ( i == 0 ) path_distances[i] = 0; else - path_distances[i] = sqrt( pow( old_x - mapShape->x[i], 2 ) + pow( old_y - mapShape->y[i], 2 ) ); + path_distances[i] = sqrt( std::pow( old_x - mapShape->x[i], 2 ) + std::pow( old_y - mapShape->y[i], 2 ) ); old_x = mapShape->x[i]; old_y = mapShape->y[i]; @@ -1808,13 +1808,13 @@ bool FeaturePart::nextCharPosition( double charWidth, double segment_length, Poi dx = new_x - old_x; dy = new_y - old_y; } - while ( sqrt( pow( start_x - new_x, 2 ) + pow( start_y - new_y, 2 ) ) < charWidth ); // Distance from start_ to new_ + while ( sqrt( std::pow( start_x - new_x, 2 ) + std::pow( start_y - new_y, 2 ) ) < charWidth ); // Distance from start_ to new_ // Calculate the position to place the end of the character on GeomFunction::findLineCircleIntersection( start_x, start_y, charWidth, old_x, old_y, new_x, new_y, end_x, end_y ); // Need to calculate distance on the new segment - distance = sqrt( pow( old_x - end_x, 2 ) + pow( old_y - end_y, 2 ) ); + distance = sqrt( std::pow( old_x - end_x, 2 ) + std::pow( old_y - end_y, 2 ) ); } return true; } diff --git a/src/core/pal/pal.cpp b/src/core/pal/pal.cpp index 19673848fe0..fd6bab1a9e8 100644 --- a/src/core/pal/pal.cpp +++ b/src/core/pal/pal.cpp @@ -356,7 +356,7 @@ Problem *Pal::extract( double lambda_min, double phi_min, double lambda_max, dou feat = fFeats->takeFirst(); prob->featStartId[i] = idlp; - prob->inactiveCost[i] = pow( 2, 10 - 10 * feat->priority ); + prob->inactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority ); switch ( feat->feature->getGeosType() ) { diff --git a/src/core/pal/rtree.hpp b/src/core/pal/rtree.hpp index c250f790c14..d59138804c0 100644 --- a/src/core/pal/rtree.hpp +++ b/src/core/pal/rtree.hpp @@ -1180,7 +1180,7 @@ namespace pal } else { - return static_cast< ELEMTYPEREAL >( pow( radius, NUMDIMS ) * m_unitSphereVolume ); + return static_cast< ELEMTYPEREAL >( std::pow( radius, NUMDIMS ) * m_unitSphereVolume ); } } diff --git a/src/core/qgis.h b/src/core/qgis.h index 2d80dd59dc7..4a1e853fd2c 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -236,7 +236,7 @@ inline bool qgsDoubleNearSig( double a, double b, int significantDigits = 10 ) double br = frexp( b, &bexp ); return aexp == bexp && - std::round( ar * pow( 10.0, significantDigits ) ) == std::round( br * pow( 10.0, significantDigits ) ); + std::round( ar * std::pow( 10.0, significantDigits ) ) == std::round( br * std::pow( 10.0, significantDigits ) ); } /** @@ -246,7 +246,7 @@ inline bool qgsDoubleNearSig( double a, double b, int significantDigits = 10 ) */ inline double qgsRound( double number, double places ) { - int scaleFactor = pow( 10, places ); + int scaleFactor = std::pow( 10, places ); return static_cast( static_cast( number * scaleFactor + 0.5 ) ) / scaleFactor; } diff --git a/src/core/qgscoordinatetransform.cpp b/src/core/qgscoordinatetransform.cpp index 59a4724e22e..ef6587d1e7c 100644 --- a/src/core/qgscoordinatetransform.cpp +++ b/src/core/qgscoordinatetransform.cpp @@ -352,7 +352,7 @@ QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &r // even with 1000 points it takes < 1ms // TODO: how to effectively and precisely reproject bounding box? const int nPoints = 1000; - double d = sqrt( ( rect.width() * rect.height() ) / pow( sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) ); + double d = sqrt( ( rect.width() * rect.height() ) / std::pow( sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) ); int nXPoints = static_cast< int >( ceil( rect.width() / d ) ) + 1; int nYPoints = static_cast< int >( ceil( rect.height() / d ) ) + 1; diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 64a1bc2c9ca..523712b8c87 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -268,7 +268,7 @@ bool QgsField::convertCompatible( QVariant &v ) const if ( d->type == QVariant::Double && d->precision > 0 ) { - double s = qPow( 10, d->precision ); + double s = std::pow( 10, d->precision ); double d = v.toDouble() * s; v = QVariant( ( d < 0 ? ceil( d - 0.5 ) : floor( d + 0.5 ) ) / s ); return true; diff --git a/src/core/qgshistogram.cpp b/src/core/qgshistogram.cpp index bfdfc433f80..42d5589a0b4 100644 --- a/src/core/qgshistogram.cpp +++ b/src/core/qgshistogram.cpp @@ -65,7 +65,7 @@ bool QgsHistogram::setValues( const QgsVectorLayer *layer, const QString &fieldO double QgsHistogram::optimalBinWidth() const { //Freedman-Diaconis rule - return 2.0 * mIQR * qPow( mValues.count(), -1 / 3.0 ); + return 2.0 * mIQR * std::pow( mValues.count(), -1 / 3.0 ); } int QgsHistogram::optimalNumberBins() const diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index d9489f3e9bb..b386881450a 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -95,7 +95,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix double mySecondsY = double( myFloatMinutesY - myIntMinutesY ) * 60; //make sure rounding to specified precision doesn't create seconds >= 60 - if ( std::round( mySecondsX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( mySecondsX * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { mySecondsX = qMax( mySecondsX - 60, 0.0 ); myIntMinutesX++; @@ -105,7 +105,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix myDegreesX++; } } - if ( std::round( mySecondsY * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( mySecondsY * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { mySecondsY = qMax( mySecondsY - 60, 0.0 ); myIntMinutesY++; @@ -138,18 +138,18 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix } //check if coordinate is all zeros for the specified precision, and if so, //remove the sign and hemisphere strings - if ( myDegreesX == 0 && myIntMinutesX == 0 && std::round( mySecondsX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 0 && myIntMinutesX == 0 && std::round( mySecondsX * std::pow( 10.0, precision ) ) == 0 ) { myXSign = QString(); myXHemisphere = QString(); } - if ( myDegreesY == 0 && myIntMinutesY == 0 && std::round( mySecondsY * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesY == 0 && myIntMinutesY == 0 && std::round( mySecondsY * std::pow( 10.0, precision ) ) == 0 ) { myYSign = QString(); myYHemisphere = QString(); } //also remove directional prefix from 180 degree longitudes - if ( myDegreesX == 180 && myIntMinutesX == 0 && std::round( mySecondsX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 180 && myIntMinutesX == 0 && std::round( mySecondsX * std::pow( 10.0, precision ) ) == 0 ) { myXHemisphere = QString(); } @@ -193,12 +193,12 @@ QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const double myFloatMinutesY = double( ( std::fabs( mY ) - myDegreesY ) * 60 ); //make sure rounding to specified precision doesn't create minutes >= 60 - if ( std::round( myFloatMinutesX * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( myFloatMinutesX * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { myFloatMinutesX = qMax( myFloatMinutesX - 60, 0.0 ); myDegreesX++; } - if ( std::round( myFloatMinutesY * pow( 10.0, precision ) ) >= 60 * pow( 10.0, precision ) ) + if ( std::round( myFloatMinutesY * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { myFloatMinutesY = qMax( myFloatMinutesY - 60, 0.0 ); myDegreesY++; @@ -226,18 +226,18 @@ QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const } //check if coordinate is all zeros for the specified precision, and if so, //remove the sign and hemisphere strings - if ( myDegreesX == 0 && std::round( myFloatMinutesX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 0 && std::round( myFloatMinutesX * std::pow( 10.0, precision ) ) == 0 ) { myXSign = QString(); myXHemisphere = QString(); } - if ( myDegreesY == 0 && std::round( myFloatMinutesY * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesY == 0 && std::round( myFloatMinutesY * std::pow( 10.0, precision ) ) == 0 ) { myYSign = QString(); myYHemisphere = QString(); } //also remove directional prefix from 180 degree longitudes - if ( myDegreesX == 180 && std::round( myFloatMinutesX * pow( 10.0, precision ) ) == 0 ) + if ( myDegreesX == 180 && std::round( myFloatMinutesX * std::pow( 10.0, precision ) ) == 0 ) { myXHemisphere = QString(); } diff --git a/src/core/qgspropertytransformer.cpp b/src/core/qgspropertytransformer.cpp index c5b482fc7da..2aee158e353 100644 --- a/src/core/qgspropertytransformer.cpp +++ b/src/core/qgspropertytransformer.cpp @@ -198,7 +198,7 @@ double QgsGenericNumericTransformer::value( double input ) const if ( qgsDoubleNear( mExponent, 1.0 ) ) return mMinOutput + ( qBound( mMinValue, input, mMaxValue ) - mMinValue ) * ( mMaxOutput - mMinOutput ) / ( mMaxValue - mMinValue ); else - return mMinOutput + qPow( qBound( mMinValue, input, mMaxValue ) - mMinValue, mExponent ) * ( mMaxOutput - mMinOutput ) / qPow( mMaxValue - mMinValue, mExponent ); + return mMinOutput + std::pow( qBound( mMinValue, input, mMaxValue ) - mMinValue, mExponent ) * ( mMaxOutput - mMinOutput ) / std::pow( mMaxValue - mMinValue, mExponent ); } QVariant QgsGenericNumericTransformer::transform( const QgsExpressionContext &context, const QVariant &v ) const @@ -400,7 +400,7 @@ double QgsSizeScaleTransformer::size( double value ) const case Area: case Flannery: case Exponential: - return mMinSize + qPow( qBound( mMinValue, value, mMaxValue ) - mMinValue, mExponent ) * ( mMaxSize - mMinSize ) / qPow( mMaxValue - mMinValue, mExponent ); + return mMinSize + std::pow( qBound( mMinValue, value, mMaxValue ) - mMinValue, mExponent ) * ( mMaxSize - mMinSize ) / std::pow( mMaxValue - mMinValue, mExponent ); } return 0; diff --git a/src/core/qgsscalecalculator.cpp b/src/core/qgsscalecalculator.cpp index 82e44da5008..6d5b66fb46b 100644 --- a/src/core/qgsscalecalculator.cpp +++ b/src/core/qgsscalecalculator.cpp @@ -116,14 +116,14 @@ double QgsScaleCalculator::calculateGeographicDistance( const QgsRectangle &mapE // For a longitude change of 180 degrees double lat = ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5; static const double RADS = ( 4.0 * atan( 1.0 ) ) / 180.0; - double a = pow( cos( lat * RADS ), 2 ); + double a = std::pow( cos( lat * RADS ), 2 ); double c = 2.0 * atan2( sqrt( a ), sqrt( 1.0 - a ) ); static const double RA = 6378000; // [m] // The eccentricity. This comes from sqrt(1.0 - rb*rb/(ra*ra)) with rb set // to 6357000 m. static const double E = 0.0810820288; double radius = RA * ( 1.0 - E * E ) / - pow( 1.0 - E * E * sin( lat * RADS ) * sin( lat * RADS ), 1.5 ); + std::pow( 1.0 - E * E * sin( lat * RADS ) * sin( lat * RADS ), 1.5 ); double meters = ( mapExtent.xMaximum() - mapExtent.xMinimum() ) / 180.0 * radius * c; QgsDebugMsgLevel( "Distance across map extent (m): " + QString::number( meters ), 4 ); diff --git a/src/core/qgsstatisticalsummary.cpp b/src/core/qgsstatisticalsummary.cpp index 5303a601233..ce19cdf324a 100644 --- a/src/core/qgsstatisticalsummary.cpp +++ b/src/core/qgsstatisticalsummary.cpp @@ -126,8 +126,8 @@ void QgsStatisticalSummary::finalize() double diff = value - mMean; sumSquared += diff * diff; } - mStdev = qPow( sumSquared / mValues.count(), 0.5 ); - mSampleStdev = qPow( sumSquared / ( mValues.count() - 1 ), 0.5 ); + mStdev = std::pow( sumSquared / mValues.count(), 0.5 ); + mSampleStdev = std::pow( sumSquared / ( mValues.count() - 1 ), 0.5 ); } if ( mStatistics & QgsStatisticalSummary::Median diff --git a/src/core/qgstextrenderer.cpp b/src/core/qgstextrenderer.cpp index c0bf7c4b712..c594cb1697f 100644 --- a/src/core/qgstextrenderer.cpp +++ b/src/core/qgstextrenderer.cpp @@ -2192,7 +2192,7 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer else if ( background.type() == QgsTextBackgroundSettings::ShapeCircle ) { // start with label bound by circle - h = sqrt( pow( w, 2 ) + pow( h, 2 ) ); + h = sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) ); w = h; } else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse ) diff --git a/src/core/raster/qgsbrightnesscontrastfilter.cpp b/src/core/raster/qgsbrightnesscontrastfilter.cpp index eb0f4de73f2..173aafdd0b5 100644 --- a/src/core/raster/qgsbrightnesscontrastfilter.cpp +++ b/src/core/raster/qgsbrightnesscontrastfilter.cpp @@ -141,7 +141,7 @@ QgsRasterBlock *QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle co QRgb myColor; int r, g, b, alpha; - double f = qPow( ( mContrast + 100 ) / 100.0, 2 ); + double f = std::pow( ( mContrast + 100 ) / 100.0, 2 ); for ( qgssize i = 0; i < ( qgssize )width * height; i++ ) { diff --git a/src/core/raster/qgscubicrasterresampler.cpp b/src/core/raster/qgscubicrasterresampler.cpp index 43d07d20d0e..85615373a0b 100644 --- a/src/core/raster/qgscubicrasterresampler.cpp +++ b/src/core/raster/qgscubicrasterresampler.cpp @@ -472,7 +472,7 @@ double QgsCubicRasterResampler::calcBernsteinPolyN3( int i, double t ) return 0; } - return lowerN3( i ) * qPow( t, i ) * qPow( ( 1 - t ), ( 3 - i ) ); + return lowerN3( i ) * std::pow( t, i ) * std::pow( ( 1 - t ), ( 3 - i ) ); } inline int QgsCubicRasterResampler::lowerN3( int i ) diff --git a/src/core/raster/qgshuesaturationfilter.cpp b/src/core/raster/qgshuesaturationfilter.cpp index 0a045363428..0f3dcd92c75 100644 --- a/src/core/raster/qgshuesaturationfilter.cpp +++ b/src/core/raster/qgshuesaturationfilter.cpp @@ -307,7 +307,7 @@ void QgsHueSaturationFilter::processSaturation( int &r, int &g, int &b, int &h, { // Raising the saturation. Use a saturation curve to prevent // clipping at maximum saturation with ugly results. - s = qMin( ( int )( 255. * ( 1 - pow( 1 - ( s / 255. ), pow( mSaturationScale, 2 ) ) ) ), 255 ); + s = qMin( ( int )( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturationScale, 2 ) ) ) ), 255 ); } // Saturation changed, so update rgb values diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index a9806fb8cf4..f59739ab09d 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -203,7 +203,7 @@ double QgsRasterChecker::tolerance( double val, int places ) { // float precision is about 7 decimal digits, double about 16 // default places = 6 - return 1. * qPow( 10, std::round( log10( std::fabs( val ) ) - places ) ); + return 1. * std::pow( 10, std::round( log10( std::fabs( val ) ) - places ) ); } QString QgsRasterChecker::compareHead() diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index 8a7365ccb2a..ce13de7cc2c 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -2964,7 +2964,7 @@ QgsSymbolLayer *QgsLinePatternFillSymbolLayer::createFromSld( QDomElement &eleme QPointF vectOffset; if ( QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, vectOffset ) ) { - offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) ); + offset = sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) ); } QString uom = element.attribute( QStringLiteral( "uom" ), "" ); diff --git a/src/core/symbology/qgsheatmaprenderer.cpp b/src/core/symbology/qgsheatmaprenderer.cpp index a71ce71d0e4..c367122a8b4 100644 --- a/src/core/symbology/qgsheatmaprenderer.cpp +++ b/src/core/symbology/qgsheatmaprenderer.cpp @@ -165,7 +165,7 @@ bool QgsHeatmapRenderer::renderFeature( QgsFeature &feature, QgsRenderContext &c { continue; } - double distanceSquared = pow( pointX - x, 2.0 ) + pow( pointY - y, 2.0 ); + double distanceSquared = std::pow( pointX - x, 2.0 ) + std::pow( pointY - y, 2.0 ); if ( distanceSquared > mRadiusSquared ) { continue; @@ -203,17 +203,17 @@ double QgsHeatmapRenderer::uniformKernel( const double distance, const int bandw double QgsHeatmapRenderer::quarticKernel( const double distance, const int bandwidth ) const { - return pow( 1. - pow( distance / static_cast< double >( bandwidth ), 2 ), 2 ); + return pow( 1. - std::pow( distance / static_cast< double >( bandwidth ), 2 ), 2 ); } double QgsHeatmapRenderer::triweightKernel( const double distance, const int bandwidth ) const { - return pow( 1. - pow( distance / static_cast< double >( bandwidth ), 2 ), 3 ); + return pow( 1. - std::pow( distance / static_cast< double >( bandwidth ), 2 ), 3 ); } double QgsHeatmapRenderer::epanechnikovKernel( const double distance, const int bandwidth ) const { - return ( 1. - pow( distance / static_cast< double >( bandwidth ), 2 ) ); + return ( 1. - std::pow( distance / static_cast< double >( bandwidth ), 2 ) ); } double QgsHeatmapRenderer::triangularKernel( const double distance, const int bandwidth ) const diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index a30465e1cb4..00746b95278 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3932,7 +3932,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, cell = 20 * 1e-07; } - double base = pow( 10.0, floor( log10( cell ) ) ); + double base = std::pow( 10.0, floor( log10( cell ) ) ); double unit = base; if ( ( 2 * base ) - cell < h * ( cell - unit ) ) { diff --git a/src/gui/editorwidgets/qgsdoublespinbox.cpp b/src/gui/editorwidgets/qgsdoublespinbox.cpp index 2b346b20b54..66aac453c48 100644 --- a/src/gui/editorwidgets/qgsdoublespinbox.cpp +++ b/src/gui/editorwidgets/qgsdoublespinbox.cpp @@ -71,7 +71,7 @@ void QgsDoubleSpinBox::wheelEvent( QWheelEvent *event ) double newStep = step / 10; // but don't ever use an increment smaller than would be visible in the widget // i.e. if showing 2 decimals, smallest increment will be 0.01 - newStep = qMax( newStep, pow( 10.0, 0.0 - decimals() ) ); + newStep = qMax( newStep, std::pow( 10.0, 0.0 - decimals() ) ); setSingleStep( newStep ); diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index e0dd9b0089d..b6e03f434bd 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -50,10 +50,10 @@ bool QgsAdvancedDigitizingDockWidget::lineCircleIntersection( const QgsPointXY & const double dx = x2 - x1; const double dy = y2 - y1; - const double dr = sqrt( pow( dx, 2 ) + pow( dy, 2 ) ); + const double dr = sqrt( std::pow( dx, 2 ) + std::pow( dy, 2 ) ); const double d = x1 * y2 - x2 * y1; - const double disc = pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ); + const double disc = std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ); if ( disc < 0 ) { @@ -65,12 +65,12 @@ bool QgsAdvancedDigitizingDockWidget::lineCircleIntersection( const QgsPointXY & // two solutions const int sgnDy = dy < 0 ? -1 : 1; - const double ax = center.x() + ( d * dy + sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); - const double ay = center.y() + ( -d * dx + std::fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); + const double ax = center.x() + ( d * dy + sgnDy * dx * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); + const double ay = center.y() + ( -d * dx + std::fabs( dy ) * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); const QgsPointXY p1( ax, ay ); - const double bx = center.x() + ( d * dy - sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); - const double by = center.y() + ( -d * dx - std::fabs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) ); + const double bx = center.x() + ( d * dy - sgnDy * dx * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); + const double by = center.y() + ( -d * dx - std::fabs( dy ) * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); const QgsPointXY p2( bx, by ); // snap to nearest intersection diff --git a/src/gui/qgscurveeditorwidget.cpp b/src/gui/qgscurveeditorwidget.cpp index fedf130e927..2bf0ac2420d 100644 --- a/src/gui/qgscurveeditorwidget.cpp +++ b/src/gui/qgscurveeditorwidget.cpp @@ -190,7 +190,7 @@ int QgsCurveEditorWidget::findNearestControlPoint( QPointF point ) const { QgsPointXY currentPoint = controlPoints.at( i ); double currentDist; - currentDist = qPow( point.x() - currentPoint.x(), 2.0 ) + qPow( point.y() - currentPoint.y(), 2.0 ); + currentDist = std::pow( point.x() - currentPoint.x(), 2.0 ) + std::pow( point.y() - currentPoint.y(), 2.0 ); if ( currentDist < minDist ) { minDist = currentDist; diff --git a/src/gui/qgsgradientcolorrampdialog.cpp b/src/gui/qgsgradientcolorrampdialog.cpp index 93dce7443a2..01cca0d5ed5 100644 --- a/src/gui/qgsgradientcolorrampdialog.cpp +++ b/src/gui/qgsgradientcolorrampdialog.cpp @@ -377,7 +377,7 @@ void QgsGradientColorRampDialog::plotMousePress( QPointF point ) double currentDist; if ( mPlotHueCheckbox->isChecked() ) { - currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.hslHueF(), 2.0 ); + currentDist = std::pow( point.x() - currentOff, 2.0 ) + std::pow( point.y() - currentCol.hslHueF(), 2.0 ); if ( currentDist < minDist ) { minDist = currentDist; @@ -387,7 +387,7 @@ void QgsGradientColorRampDialog::plotMousePress( QPointF point ) } if ( mPlotLightnessCheckbox->isChecked() ) { - currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.lightnessF(), 2.0 ); + currentDist = std::pow( point.x() - currentOff, 2.0 ) + std::pow( point.y() - currentCol.lightnessF(), 2.0 ); if ( currentDist < minDist ) { minDist = currentDist; @@ -397,7 +397,7 @@ void QgsGradientColorRampDialog::plotMousePress( QPointF point ) } if ( mPlotSaturationCheckbox->isChecked() ) { - currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.hslSaturationF(), 2.0 ); + currentDist = std::pow( point.x() - currentOff, 2.0 ) + std::pow( point.y() - currentCol.hslSaturationF(), 2.0 ); if ( currentDist < minDist ) { minDist = currentDist; @@ -407,7 +407,7 @@ void QgsGradientColorRampDialog::plotMousePress( QPointF point ) } if ( mPlotAlphaCheckbox->isChecked() ) { - currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.alphaF(), 2.0 ); + currentDist = std::pow( point.x() - currentOff, 2.0 ) + std::pow( point.y() - currentCol.alphaF(), 2.0 ); if ( currentDist < minDist ) { minDist = currentDist;; diff --git a/src/gui/symbology/qgsgraduatedsymbolrendererwidget.cpp b/src/gui/symbology/qgsgraduatedsymbolrendererwidget.cpp index 633d8e5c79a..af2a313d97d 100644 --- a/src/gui/symbology/qgsgraduatedsymbolrendererwidget.cpp +++ b/src/gui/symbology/qgsgraduatedsymbolrendererwidget.cpp @@ -1110,7 +1110,7 @@ QgsSymbol *QgsGraduatedSymbolRendererWidget::findSymbolForRange( double lowerBou int decimalPlaces = mRenderer->labelFormat().precision() + 2; if ( decimalPlaces < 0 ) decimalPlaces = 0; - double precision = 1.0 / qPow( 10, decimalPlaces ); + double precision = 1.0 / std::pow( 10, decimalPlaces ); for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it ) { diff --git a/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp index caf43b5e362..d3fbd2e93ff 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp @@ -48,12 +48,12 @@ int QgsGeometryCheckPrecision::reducedPrecision() double QgsGeometryCheckPrecision::tolerance() { - return qPow( 10, -get()->mPrecision ); + return pow( 10, -get()->mPrecision ); } double QgsGeometryCheckPrecision::reducedTolerance() { - return qPow( 10, -get()->mReducedPrecision ); + return pow( 10, -get()->mReducedPrecision ); } QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, diff --git a/src/plugins/georeferencer/qgsresidualplotitem.cpp b/src/plugins/georeferencer/qgsresidualplotitem.cpp index 3f2218b70b2..21dcba02387 100644 --- a/src/plugins/georeferencer/qgsresidualplotitem.cpp +++ b/src/plugins/georeferencer/qgsresidualplotitem.cpp @@ -112,16 +112,16 @@ void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsIt if ( scaleBarWidthUnits < 1 ) { nDecPlaces = -floor( log10( scaleBarWidthUnits ) ); - scaleBarWidthUnits *= pow( 10.0, nDecPlaces ); + scaleBarWidthUnits *= std::pow( 10.0, nDecPlaces ); scaleBarWidthUnits = ( int )( scaleBarWidthUnits + 0.5 ); - scaleBarWidthUnits /= pow( 10.0, nDecPlaces ); + scaleBarWidthUnits /= std::pow( 10.0, nDecPlaces ); } else { nDecPlaces = ( int )log10( scaleBarWidthUnits ); - scaleBarWidthUnits /= pow( 10.0, nDecPlaces ); + scaleBarWidthUnits /= std::pow( 10.0, nDecPlaces ); scaleBarWidthUnits = ( int )( scaleBarWidthUnits + 0.5 ); - scaleBarWidthUnits *= pow( 10.0, nDecPlaces ); + scaleBarWidthUnits *= std::pow( 10.0, nDecPlaces ); } initialScaleBarWidth = scaleBarWidthUnits * minMMPixelRatio; diff --git a/src/plugins/grass/qgsgrassmapcalc.cpp b/src/plugins/grass/qgsgrassmapcalc.cpp index 459f8f32699..ea75e10f5bf 100644 --- a/src/plugins/grass/qgsgrassmapcalc.cpp +++ b/src/plugins/grass/qgsgrassmapcalc.cpp @@ -421,8 +421,8 @@ void QgsGrassMapcalc::mouseReleaseEvent( QMouseEvent *e ) if ( mToolStep == 1 ) { QPoint p0 = mConnector->point( 0 ); - double d = sqrt( pow( ( double )( p.x() - p0.x() ), 2.0 ) - + pow( ( double )( p.y() - p0.y() ), 2.0 ) ); + double d = sqrt( std::pow( ( double )( p.x() - p0.x() ), 2.0 ) + + std::pow( ( double )( p.y() - p0.y() ), 2.0 ) ); QgsDebugMsg( QString( "d = %1" ).arg( d ) ); if ( d < 5 ) // filter 'single' clicks { @@ -1697,8 +1697,8 @@ bool QgsGrassMapcalcObject::tryConnect( QgsGrassMapcalcConnector *connector, if ( mInputConnectors[i] ) continue; // used - double d = sqrt( pow( ( double )( mInputPoints[i].x() + pos().x() - p.x() ), 2.0 ) - + pow( ( double )( mInputPoints[i].y() + pos().y() - p.y() ), 2.0 ) ); + double d = sqrt( std::pow( ( double )( mInputPoints[i].x() + pos().x() - p.x() ), 2.0 ) + + std::pow( ( double )( mInputPoints[i].y() + pos().y() - p.y() ), 2.0 ) ); if ( d <= mSocketHalf ) { @@ -1713,8 +1713,8 @@ bool QgsGrassMapcalcObject::tryConnect( QgsGrassMapcalcConnector *connector, // Output if ( !connector->connected( Out ) && !mOutputConnector ) { - double d = sqrt( pow( ( double )( mOutputPoint.x() + pos().x() - p.x() ), 2.0 ) - + pow( ( double )( mOutputPoint.y() + pos().y() - p.y() ), 2.0 ) ); + double d = sqrt( std::pow( ( double )( mOutputPoint.x() + pos().x() - p.x() ), 2.0 ) + + std::pow( ( double )( mOutputPoint.y() + pos().y() - p.y() ), 2.0 ) ); if ( d <= mSocketHalf ) { @@ -1899,11 +1899,11 @@ void QgsGrassMapcalcConnector::selectEnd( QPoint point ) { mSelectedEnd = -1; - double d0 = sqrt( pow( ( double )( point.x() - mPoints[0].x() ), 2.0 ) - + pow( ( double )( point.y() - mPoints[0].y() ), 2.0 ) ); + double d0 = sqrt( std::pow( ( double )( point.x() - mPoints[0].x() ), 2.0 ) + + std::pow( ( double )( point.y() - mPoints[0].y() ), 2.0 ) ); - double d1 = sqrt( pow( ( double )( point.x() - mPoints[1].x() ), 2.0 ) - + pow( ( double )( point.y() - mPoints[1].y() ), 2.0 ) ); + double d1 = sqrt( std::pow( ( double )( point.x() - mPoints[1].x() ), 2.0 ) + + std::pow( ( double )( point.y() - mPoints[1].y() ), 2.0 ) ); if ( d0 < 15 || d1 < 15 ) diff --git a/src/providers/grass/qgis.g.info.c b/src/providers/grass/qgis.g.info.c index c48d327ac60..7e6426b5bf9 100644 --- a/src/providers/grass/qgis.g.info.c +++ b/src/providers/grass/qgis.g.info.c @@ -324,12 +324,12 @@ int main( int argc, char **argv ) if ( val > max ) max = val; sum += val; count++; - squares_sum += pow( val, 2 ); + squares_sum += val * val; } } } mean = count > 0 ? sum / count : 0; - squares_sum -= count * pow( mean, 2 ); + squares_sum -= count * mean * mean; stdev = sqrt( squares_sum / ( count - 1 ) ); fprintf( stdout, "MIN:%.17e\n", min ); diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index c4236fc7dc4..8436c40a63b 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -501,7 +501,7 @@ void QgsWmsProvider::setFormatQueryItem( QUrl &url ) static bool _fuzzyContainsRect( const QRectF &r1, const QRectF &r2 ) { double significantDigits = log10( qMax( r1.width(), r1.height() ) ); - double epsilon = pow( 10.0, significantDigits - 5 ); // floats have 6-9 significant digits + double epsilon = std::pow( 10.0, significantDigits - 5 ); // floats have 6-9 significant digits return r1.contains( r2.adjusted( epsilon, epsilon, -epsilon, -epsilon ) ); } diff --git a/tests/bench/qgsbench.cpp b/tests/bench/qgsbench.cpp index 50f2e0b709f..c2df882ea9a 100644 --- a/tests/bench/qgsbench.cpp +++ b/tests/bench/qgsbench.cpp @@ -244,7 +244,7 @@ void QgsBench::render() for ( int i = 0; i < mTimes.size(); i++ ) { double d = std::fabs( avg[t] - mTimes.at( i )[t] ); - stdev[t] += pow( d, 2 ); + stdev[t] += std::pow( d, 2 ); if ( i == 0 || d > maxdev[t] ) maxdev[t] = d; } diff --git a/tests/src/core/testqgsmapsettings.cpp b/tests/src/core/testqgsmapsettings.cpp index e4eca2031a4..481a93abe7d 100644 --- a/tests/src/core/testqgsmapsettings.cpp +++ b/tests/src/core/testqgsmapsettings.cpp @@ -62,7 +62,7 @@ QString TestQgsMapSettings::toString( const QPolygonF &p, int dec ) const { QString s; const char *sep = ""; - double r = pow( 10.0, dec ); + double r = std::pow( 10.0, dec ); for ( int i = 0; i < p.size(); ++i ) { s += QStringLiteral( "%1%2 %3" ).arg( sep ) From 249b5050aaa2ea48e0b6bf8fa0d67b8832798a0f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 02:57:00 +1000 Subject: [PATCH 118/364] (q)atan2 -> std::atan2 --- src/analysis/raster/qgsaspectfilter.cpp | 2 +- src/analysis/raster/qgshillshadefilter.cpp | 2 +- src/app/dwg/libdxfrw/drw_entities.cpp | 4 +-- src/app/nodetool/qgsnodetool.cpp | 2 +- src/app/qgsmaptoolrotatefeature.cpp | 4 +-- src/app/qgsmaptoolrotatepointsymbols.cpp | 2 +- src/core/expression/qgsexpressionfunction.cpp | 2 +- src/core/geometry/qgsellipse.cpp | 2 +- src/core/geometry/qgsgeometryutils.cpp | 14 +++++----- src/core/geometry/qgspoint.cpp | 2 +- src/core/pal/feature.cpp | 24 ++++++++--------- src/core/qgsdistancearea.cpp | 22 ++++++++-------- src/core/qgspointxy.cpp | 2 +- src/core/qgsscalecalculator.cpp | 2 +- src/core/qgsvector.cpp | 4 +-- src/core/raster/qgshillshaderenderer.cpp | 2 +- src/core/symbology/qgsarrowsymbollayer.cpp | 14 +++++----- src/core/symbology/qgsfillsymbollayer.cpp | 2 +- src/core/symbology/qgslinesymbollayer.cpp | 2 +- src/gui/qgsadvanceddigitizingcanvasitem.cpp | 4 +-- src/gui/qgsadvanceddigitizingdockwidget.cpp | 26 +++++++++---------- 21 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/analysis/raster/qgsaspectfilter.cpp b/src/analysis/raster/qgsaspectfilter.cpp index cd0d96830c4..0a4f6060eb4 100644 --- a/src/analysis/raster/qgsaspectfilter.cpp +++ b/src/analysis/raster/qgsaspectfilter.cpp @@ -39,7 +39,7 @@ float QgsAspectFilter::processNineCellWindow( } else { - return 180.0 + atan2( derX, derY ) * 180.0 / M_PI; + return 180.0 + std::atan2( derX, derY ) * 180.0 / M_PI; } } diff --git a/src/analysis/raster/qgshillshadefilter.cpp b/src/analysis/raster/qgshillshadefilter.cpp index c14145012e3..a2221c12fef 100644 --- a/src/analysis/raster/qgshillshadefilter.cpp +++ b/src/analysis/raster/qgshillshadefilter.cpp @@ -47,7 +47,7 @@ float QgsHillshadeFilter::processNineCellWindow( float *x11, float *x21, float * } else { - aspect_rad = M_PI + atan2( derX, derY ); + aspect_rad = M_PI + std::atan2( derX, derY ); } return qMax( 0.0, 255.0 * ( ( cos( zenith_rad ) * cos( slope_rad ) ) + ( sin( zenith_rad ) * sin( slope_rad ) * cos( azimuth_rad - aspect_rad ) ) ) ); } diff --git a/src/app/dwg/libdxfrw/drw_entities.cpp b/src/app/dwg/libdxfrw/drw_entities.cpp index 9920908774e..f2a80a095cb 100644 --- a/src/app/dwg/libdxfrw/drw_entities.cpp +++ b/src/app/dwg/libdxfrw/drw_entities.cpp @@ -957,7 +957,7 @@ void DRW_Ellipse::toPolyline( DRW_Polyline *pol, int parts ) const double radMajor = sqrt( secPoint.x * secPoint.x + secPoint.y * secPoint.y ); double radMinor = radMajor * ratio; //calculate sin & cos of included angle - double incAngle = atan2( secPoint.y, secPoint.x ); + double incAngle = std::atan2( secPoint.y, secPoint.x ); double cosRot = cos( incAngle ); double sinRot = sin( incAngle ); @@ -1860,7 +1860,7 @@ void DRW_MText::updateAngle() { if ( haveXAxis ) { - angle = atan2( secPoint.y, secPoint.x ) * 180 / M_PI; + angle = std::atan2( secPoint.y, secPoint.x ) * 180 / M_PI; } } diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index 3aa7ed03638..561ae2bca06 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -681,7 +681,7 @@ QgsPointXY QgsNodeTool::positionForEndpointMarker( const QgsPointLocator::Match double dx = pt1.x() - pt0.x(); double dy = pt1.y() - pt0.y(); double dist = 15 * canvas()->mapSettings().mapUnitsPerPixel(); - double angle = atan2( dy, dx ); // to the top: angle=0, to the right: angle=90, to the left: angle=-90 + double angle = std::atan2( dy, dx ); // to the top: angle=0, to the right: angle=90, to the left: angle=-90 double x = pt1.x() + cos( angle ) * dist; double y = pt1.y() + sin( angle ) * dist; return QgsPointXY( x, y ); diff --git a/src/app/qgsmaptoolrotatefeature.cpp b/src/app/qgsmaptoolrotatefeature.cpp index 99a051b64e6..d807b97b8d2 100644 --- a/src/app/qgsmaptoolrotatefeature.cpp +++ b/src/app/qgsmaptoolrotatefeature.cpp @@ -152,7 +152,7 @@ void QgsMapToolRotateFeature::canvasMoveEvent( QgsMapMouseEvent *e ) { const double XDistance = e->pos().x() - mStPoint.x(); const double YDistance = e->pos().y() - mStPoint.y(); - double rotation = atan2( YDistance, XDistance ) * ( 180 / PI ); + double rotation = std::atan2( YDistance, XDistance ) * ( 180 / PI ); if ( mRotationWidget ) { @@ -297,7 +297,7 @@ void QgsMapToolRotateFeature::canvasReleaseEvent( QgsMapMouseEvent *e ) double XDistance = mInitialPos.x() - mAnchorPoint->x(); double YDistance = mInitialPos.y() - mAnchorPoint->y() ; - mRotationOffset = atan2( YDistance, XDistance ) * ( 180 / PI ); + mRotationOffset = std::atan2( YDistance, XDistance ) * ( 180 / PI ); createRotationWidget(); if ( e->modifiers() & Qt::ShiftModifier ) diff --git a/src/app/qgsmaptoolrotatepointsymbols.cpp b/src/app/qgsmaptoolrotatepointsymbols.cpp index a5207975043..6ca89912756 100644 --- a/src/app/qgsmaptoolrotatepointsymbols.cpp +++ b/src/app/qgsmaptoolrotatepointsymbols.cpp @@ -204,7 +204,7 @@ double QgsMapToolRotatePointSymbols::calculateAzimut( QPoint mousePos ) { int dx = mousePos.x() - mSnappedPoint.x(); int dy = mousePos.y() - mSnappedPoint.y(); - return 180 - atan2( ( double ) dx, ( double ) dy ) * 180.0 / M_PI; + return 180 - std::atan2( ( double ) dx, ( double ) dy ) * 180.0 / M_PI; } void QgsMapToolRotatePointSymbols::createPixmapItem( QgsMarkerSymbol *markerSymbol ) diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index af092cb7703..9102d06f1b0 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -288,7 +288,7 @@ static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext { double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ); - return QVariant( atan2( y, x ) ); + return QVariant( std::atan2( y, x ) ); } static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index 8d7337c8779..363aff68f87 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -199,7 +199,7 @@ QgsPointSequence QgsEllipse::points( unsigned int segments ) const double m = mCenter.m(); QVector t; - double azimuth = atan2( quadrant().at( 0 ).y() - mCenter.y(), quadrant().at( 0 ).x() - mCenter.x() ); + double azimuth = std::atan2( quadrant().at( 0 ).y() - mCenter.y(), quadrant().at( 0 ).x() - mCenter.x() ); for ( unsigned int i = 0; i < segments; ++i ) { t.append( 2 * M_PI - ( ( 2 * M_PI ) / segments * i ) ); // Since the algorithm used rotates in the trigonometric direction (counterclockwise) diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index e30b790bb57..59a3c104cc3 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -430,7 +430,7 @@ QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, double QgsGeometryUtils::ccwAngle( double dy, double dx ) { - double angle = atan2( dy, dx ) * 180 / M_PI; + double angle = std::atan2( dy, dx ) * 180 / M_PI; if ( angle < 0 ) { return 360 + angle; @@ -664,9 +664,9 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co } //angles of pt1, pt2, pt3 - double a1 = atan2( circlePoint1.y() - centerY, circlePoint1.x() - centerX ); - double a2 = atan2( circlePoint2.y() - centerY, circlePoint2.x() - centerX ); - double a3 = atan2( circlePoint3.y() - centerY, circlePoint3.x() - centerX ); + double a1 = std::atan2( circlePoint1.y() - centerY, circlePoint1.x() - centerX ); + double a2 = std::atan2( circlePoint2.y() - centerY, circlePoint2.x() - centerX ); + double a3 = std::atan2( circlePoint3.y() - centerY, circlePoint3.x() - centerX ); /* Adjust a3 up so we can increment from a1 to a3 cleanly */ if ( a3 <= a1 ) @@ -1092,15 +1092,15 @@ QgsLineString QgsGeometryUtils::perpendicularSegment( const QgsPoint &p, const Q double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 ) { - double at = atan2( y2 - y1, x2 - x1 ); + double at = std::atan2( y2 - y1, x2 - x1 ); double a = -at + M_PI / 2.0; return normalizedAngle( a ); } double QgsGeometryUtils::angleBetweenThreePoints( double x1, double y1, double x2, double y2, double x3, double y3 ) { - double angle1 = atan2( y1 - y2, x1 - x2 ); - double angle2 = atan2( y3 - y2, x3 - x2 ); + double angle1 = std::atan2( y1 - y2, x1 - x2 ); + double angle2 = std::atan2( y3 - y2, x3 - x2 ); return normalizedAngle( angle1 - angle2 ); } diff --git a/src/core/geometry/qgspoint.cpp b/src/core/geometry/qgspoint.cpp index db775a1c858..db6a69c392b 100644 --- a/src/core/geometry/qgspoint.cpp +++ b/src/core/geometry/qgspoint.cpp @@ -538,7 +538,7 @@ double QgsPoint::azimuth( const QgsPoint &other ) const { double dx = other.x() - mX; double dy = other.y() - mY; - return ( atan2( dx, dy ) * 180.0 / M_PI ); + return ( std::atan2( dx, dy ) * 180.0 / M_PI ); } double QgsPoint::inclination( const QgsPoint &other ) const diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index de4a8c639b0..885d7dde125 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -456,8 +456,8 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo if ( distanceToLabel > 0 ) { - gamma1 = atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 ); - gamma2 = atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 ); + gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 ); + gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 ); } else { @@ -629,7 +629,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QListnbPoints - 1] = totalLineLength; straightSegmentLengths << currentStraightSegmentLength; - straightSegmentAngles << QgsGeometryUtils::normalizedAngle( atan2( y[numberNodes - 1] - segmentStartY, x[numberNodes - 1] - segmentStartX ) ); + straightSegmentAngles << QgsGeometryUtils::normalizedAngle( std::atan2( y[numberNodes - 1] - segmentStartY, x[numberNodes - 1] - segmentStartX ) ); longestSegmentLength = qMax( longestSegmentLength, currentStraightSegmentLength ); double middleOfLine = totalLineLength / 2.0; @@ -765,7 +765,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList & angle = 0.0; } else - angle = atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX ); + angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX ); beta = angle + M_PI / 2; @@ -1022,7 +1022,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d // Determine the angle of the path segment under consideration double dx = path_positions->x[endindex] - path_positions->x[index]; double dy = path_positions->y[endindex] - path_positions->y[index]; - double line_angle = atan2( -dy, dx ); + double line_angle = std::atan2( -dy, dx ); bool isRightToLeft = ( line_angle > 0.55 * M_PI || line_angle < -0.45 * M_PI ); reversed = isRightToLeft; @@ -1051,7 +1051,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d double dx = new_x - old_x; double dy = new_y - old_y; - double angle = atan2( -dy, dx ); + double angle = std::atan2( -dy, dx ); for ( int i = 0; i < li->char_num; i++ ) { @@ -1071,7 +1071,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d } // Calculate angle from the start of the character to the end based on start_/end_ position - angle = atan2( start_y - end_y, end_x - start_x ); + angle = std::atan2( start_y - end_y, end_x - start_x ); // Test last_character_angle vs angle // since our rendering angle has changed then check against our @@ -1246,7 +1246,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos slp->setCost( cost ); // average angle is calculated with respect to periodicity of angles - double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num ); + double angle_avg = std::atan2( sin_avg / li->char_num, cos_avg / li->char_num ); bool localreversed = flip ? !reversed : reversed; // displacement - we loop through 3 times, generating above, online then below line placements successively for ( int i = 0; i <= 2; ++i ) @@ -1446,7 +1446,7 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin alpha = box->alpha; } - beta = atan2( labelHeight, labelWidth ) + alpha; + beta = std::atan2( labelHeight, labelWidth ) + alpha; //alpha = box->alpha; diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 9c517de33cc..84581b342a0 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -426,7 +426,7 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( { azimuth = azimuth - M_PI * 2.0; } - sigma1 = atan2( tan_u1, cos( azimuth ) ); + sigma1 = std::atan2( tan_u1, cos( azimuth ) ); sin_alpha = cos( u1 ) * sin( azimuth ); alpha = asin( sin_alpha ); cos_alphasq = 1.0 - POW2( sin_alpha ); @@ -444,12 +444,12 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( } while ( i < 999 && std::fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); - lat2 = atan2( ( sin( u1 ) * cos( sigma ) + cos( u1 ) * sin( sigma ) * - cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + - POW2( sin( u1 ) * sin( sigma ) - cos( u1 ) * cos( sigma ) * - cos( azimuth ) ) ) ) ); - lambda = atan2( ( sin( sigma ) * sin( azimuth ) ), ( cos( u1 ) * cos( sigma ) - - sin( u1 ) * sin( sigma ) * cos( azimuth ) ) ); + lat2 = std::atan2( ( sin( u1 ) * cos( sigma ) + cos( u1 ) * sin( sigma ) * + cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + + POW2( sin( u1 ) * sin( sigma ) - cos( u1 ) * cos( sigma ) * + cos( azimuth ) ) ) ) ); + lambda = std::atan2( ( sin( sigma ) * sin( azimuth ) ), ( cos( u1 ) * cos( sigma ) - + sin( u1 ) * sin( sigma ) * cos( azimuth ) ) ); C = ( f / 16.0 ) * cos_alphasq * ( 4.0 + f * ( 4.0 - 3.0 * cos_alphasq ) ); omega = lambda - ( 1.0 - C ) * f * sin_alpha * ( sigma + C * sin( sigma ) * ( cos( two_sigma_m ) + C * cos( sigma ) * ( -1.0 + 2.0 * POW2( cos( two_sigma_m ) ) ) ) ); @@ -525,7 +525,7 @@ double QgsDistanceArea::bearing( const QgsPointXY &p1, const QgsPointXY &p2 ) co { double dx = p2.x() - p1.x(); double dy = p2.y() - p1.y(); - bearing = atan2( dx, dy ); + bearing = std::atan2( dx, dy ); } return bearing; @@ -579,7 +579,7 @@ double QgsDistanceArea::computeDistanceBearing( tu2 = ( cosU1 * sinU2 - sinU1 * cosU2 * cosLambda ); sinSigma = sqrt( tu1 * tu1 + tu2 * tu2 ); cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; - sigma = atan2( sinSigma, cosSigma ); + sigma = std::atan2( sinSigma, cosSigma ); alpha = asin( cosU1 * cosU2 * sinLambda / sinSigma ); cosSqAlpha = cos( alpha ) * cos( alpha ); cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; @@ -601,12 +601,12 @@ double QgsDistanceArea::computeDistanceBearing( if ( course1 ) { - *course1 = atan2( tu1, tu2 ); + *course1 = std::atan2( tu1, tu2 ); } if ( course2 ) { // PI is added to return azimuth from P2 to P1 - *course2 = atan2( cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda ) + M_PI; + *course2 = std::atan2( cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda ) + M_PI; } return s; diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index b386881450a..e93e7e4b5ff 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -285,7 +285,7 @@ double QgsPointXY::azimuth( const QgsPointXY &other ) const { double dx = other.x() - mX; double dy = other.y() - mY; - return ( atan2( dx, dy ) * 180.0 / M_PI ); + return ( std::atan2( dx, dy ) * 180.0 / M_PI ); } QgsPointXY QgsPointXY::project( double distance, double bearing ) const diff --git a/src/core/qgsscalecalculator.cpp b/src/core/qgsscalecalculator.cpp index 6d5b66fb46b..34983358acc 100644 --- a/src/core/qgsscalecalculator.cpp +++ b/src/core/qgsscalecalculator.cpp @@ -117,7 +117,7 @@ double QgsScaleCalculator::calculateGeographicDistance( const QgsRectangle &mapE double lat = ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5; static const double RADS = ( 4.0 * atan( 1.0 ) ) / 180.0; double a = std::pow( cos( lat * RADS ), 2 ); - double c = 2.0 * atan2( sqrt( a ), sqrt( 1.0 - a ) ); + double c = 2.0 * std::atan2( sqrt( a ), sqrt( 1.0 - a ) ); static const double RA = 6378000; // [m] // The eccentricity. This comes from sqrt(1.0 - rb*rb/(ra*ra)) with rb set // to 6357000 m. diff --git a/src/core/qgsvector.cpp b/src/core/qgsvector.cpp index e14198db0b3..579c093901e 100644 --- a/src/core/qgsvector.cpp +++ b/src/core/qgsvector.cpp @@ -96,7 +96,7 @@ QgsVector QgsVector::perpVector() const double QgsVector::angle() const { - double angle = atan2( mY, mX ); + double angle = std::atan2( mY, mX ); return angle < 0.0 ? angle + 2.0 * M_PI : angle; } @@ -107,7 +107,7 @@ double QgsVector::angle( QgsVector v ) const QgsVector QgsVector::rotateBy( double rot ) const { - double angle = atan2( mY, mX ) + rot; + double angle = std::atan2( mY, mX ) + rot; double len = length(); return QgsVector( len * cos( angle ), len * sin( angle ) ); } diff --git a/src/core/raster/qgshillshaderenderer.cpp b/src/core/raster/qgshillshaderenderer.cpp index af4067fb90e..5f1a6e02d91 100644 --- a/src/core/raster/qgshillshaderenderer.cpp +++ b/src/core/raster/qgshillshaderenderer.cpp @@ -211,7 +211,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize ); double slopeRad = atan( mZFactor * sqrt( derX * derX + derY * derY ) ); - double aspectRad = atan2( derX, -derY ); + double aspectRad = std::atan2( derX, -derY ); double grayValue; diff --git a/src/core/symbology/qgsarrowsymbollayer.cpp b/src/core/symbology/qgsarrowsymbollayer.cpp index c11bcab8653..1133c8ee3fc 100644 --- a/src/core/symbology/qgsarrowsymbollayer.cpp +++ b/src/core/symbology/qgsarrowsymbollayer.cpp @@ -438,8 +438,8 @@ void spiralArcTo( QPainterPath &path, QPointF center, qreal startAngle, qreal st else { // angles in the new circle - qreal a1 = atan2( cCenter.y() - A.y(), A.x() - cCenter.x() ); - qreal a2 = atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() ); + qreal a1 = std::atan2( cCenter.y() - A.y(), A.x() - cCenter.x() ); + qreal a2 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() ); pathArcTo( path, cCenter, cRadius, a1, a2, direction ); } @@ -452,8 +452,8 @@ void spiralArcTo( QPainterPath &path, QPointF center, qreal startAngle, qreal st else { // angles in the new circle - qreal a1 = atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() ); - qreal a2 = atan2( cCenter.y() - B.y(), B.x() - cCenter.x() ); + qreal a1 = std::atan2( cCenter.y() - I2.y(), I2.x() - cCenter.x() ); + qreal a2 = std::atan2( cCenter.y() - B.y(), B.x() - cCenter.x() ); pathArcTo( path, cCenter, cRadius, a1, a2, direction ); } } @@ -473,9 +473,9 @@ QPolygonF curvedArrow( QPointF po, QPointF pm, QPointF pd, } // angles of each point - qreal angle_o = clampAngle( atan2( circleCenter.y() - po.y(), po.x() - circleCenter.x() ) ); - qreal angle_m = clampAngle( atan2( circleCenter.y() - pm.y(), pm.x() - circleCenter.x() ) ); - qreal angle_d = clampAngle( atan2( circleCenter.y() - pd.y(), pd.x() - circleCenter.x() ) ); + qreal angle_o = clampAngle( std::atan2( circleCenter.y() - po.y(), po.x() - circleCenter.x() ) ); + qreal angle_m = clampAngle( std::atan2( circleCenter.y() - pm.y(), pm.x() - circleCenter.x() ) ); + qreal angle_d = clampAngle( std::atan2( circleCenter.y() - pd.y(), pd.x() - circleCenter.x() ) ); // arc direction : 1 = counter-clockwise, -1 = clockwise int direction = clampAngle( angle_m - angle_o ) < clampAngle( angle_m - angle_d ) ? 1 : -1; diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index ce13de7cc2c..7bfe1b73383 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -2594,7 +2594,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & width = outputPixelDist / sin( lineAngle * M_PI / 180 ); // recalculate real angle and distance after rounding to pixels - lineAngle = 180 * atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI; + lineAngle = 180 * std::atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI; if ( lineAngle < 0 ) { lineAngle += 360.; diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 58b05f4a8b8..3b7ac43c2bd 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -1004,7 +1004,7 @@ static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt ) double a2 = MyLine( pt, nextPt ).angle(); double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 ); - return atan2( unitY, unitX ); + return std::atan2( unitY, unitX ); } void QgsMarkerLineSymbolLayer::renderPolylineVertex( const QPolygonF &points, QgsSymbolRenderContext &context, Placement placement ) diff --git a/src/gui/qgsadvanceddigitizingcanvasitem.cpp b/src/gui/qgsadvanceddigitizingcanvasitem.cpp index ea5f877edbd..c995dccbd67 100644 --- a/src/gui/qgsadvanceddigitizingcanvasitem.cpp +++ b/src/gui/qgsadvanceddigitizingcanvasitem.cpp @@ -122,7 +122,7 @@ void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter ) double a0, a; if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 ) { - a0 = qAtan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() ); + a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() ); } else { @@ -134,7 +134,7 @@ void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter ) } else { - a = qAtan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ); + a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ); } painter->setPen( mConstruction2Pen ); painter->drawArc( prevPointPix.x() - 20, diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index b6e03f434bd..594f92762a6 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -645,14 +645,14 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) double commonAngle = mCommonAngleConstraint * M_PI / 180; // see if soft common angle constraint should be performed // only if not in HardLock mode - double softAngle = qAtan2( point.y() - previousPt.y(), - point.x() - previousPt.x() ); + double softAngle = std::atan2( point.y() - previousPt.y(), + point.x() - previousPt.x() ); double deltaAngle = 0; if ( mAngleConstraint->relative() && mCapacities.testFlag( RelativeAngle ) ) { // compute the angle relative to the last segment (0° is aligned with last segment) - deltaAngle = qAtan2( previousPt.y() - penultimatePt.y(), - previousPt.x() - penultimatePt.x() ); + deltaAngle = std::atan2( previousPt.y() - penultimatePt.y(), + previousPt.x() - penultimatePt.x() ); softAngle -= deltaAngle; } int quo = std::round( softAngle / commonAngle ); @@ -677,8 +677,8 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) if ( mAngleConstraint->relative() && mCapacities.testFlag( RelativeAngle ) ) { // compute the angle relative to the last segment (0° is aligned with last segment) - angleValue += qAtan2( previousPt.y() - penultimatePt.y(), - previousPt.x() - penultimatePt.x() ); + angleValue += std::atan2( previousPt.y() - penultimatePt.y(), + previousPt.x() - penultimatePt.x() ); } double cosa = qCos( angleValue ); @@ -821,12 +821,12 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) if ( penulPointExist && mAngleConstraint->relative() ) { // previous angle - angle = qAtan2( previousPt.y() - penultimatePt.y(), - previousPt.x() - penultimatePt.x() ); + angle = std::atan2( previousPt.y() - penultimatePt.y(), + previousPt.x() - penultimatePt.x() ); } - angle = ( qAtan2( point.y() - previousPt.y(), - point.x() - previousPt.x() - ) - angle ) * 180 / M_PI; + angle = ( std::atan2( point.y() - previousPt.y(), + point.x() - previousPt.x() + ) - angle ) * 180 / M_PI; // modulus angle = fmod( angle, 360.0 ); mAngleConstraint->setValue( angle ); @@ -882,11 +882,11 @@ bool QgsAdvancedDigitizingDockWidget::alignToSegment( QgsMapMouseEvent *e, CadCo return false; } - double angle = qAtan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() ); + double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() ); if ( mAngleConstraint->relative() && penulPointExist ) { - angle -= qAtan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() ); + angle -= std::atan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() ); } if ( mAdditionalConstraint == Perpendicular ) From 7c5aa050d1c9106124d6ada81929fb083c88e842 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:01:48 +1000 Subject: [PATCH 119/364] (q)ceil -> std::ceil --- src/analysis/network/qgsvectorlayerdirector.cpp | 6 +++--- src/analysis/raster/qgsalignraster.cpp | 2 +- src/analysis/raster/qgskde.cpp | 4 ++-- src/core/expression/qgsexpressionfunction.cpp | 2 +- src/core/geometry/qgsgeos.cpp | 4 ++-- src/core/geometry/qgsrectangle.cpp | 2 +- src/core/pal/feature.cpp | 4 ++-- src/core/pal/layer.cpp | 2 +- src/core/qgscoordinatetransform.cpp | 4 ++-- src/core/qgscoordinateutils.cpp | 2 +- src/core/qgsfield.cpp | 2 +- src/core/raster/qgspalettedrasterrenderer.cpp | 2 +- src/core/raster/qgsrasterdataprovider.cpp | 4 ++-- src/core/raster/qgsrasterinterface.cpp | 6 +++--- src/core/raster/qgsrasterlayerrenderer.cpp | 4 ++-- src/core/raster/qgsrasterprojector.cpp | 4 ++-- src/core/symbology/qgsfillsymbollayer.cpp | 2 +- src/core/symbology/qgssymbollayerutils.cpp | 2 +- src/gui/qgscolorswatchgrid.cpp | 2 +- src/gui/qgscolorwidgets.cpp | 2 +- src/gui/qgsdial.cpp | 4 ++-- src/gui/qgsslider.cpp | 4 ++-- src/plugins/georeferencer/qgsgeorefplugingui.cpp | 2 +- src/providers/arcgisrest/qgsamsprovider.cpp | 4 ++-- src/providers/wms/qgswmscapabilities.cpp | 4 ++-- src/providers/wms/qgswmsprovider.cpp | 14 +++++++------- tests/src/core/testziplayer.cpp | 2 +- 27 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/analysis/network/qgsvectorlayerdirector.cpp b/src/analysis/network/qgsvectorlayerdirector.cpp index 5436647b3dc..a990a27c84d 100644 --- a/src/analysis/network/qgsvectorlayerdirector.cpp +++ b/src/analysis/network/qgsvectorlayerdirector.cpp @@ -47,10 +47,10 @@ class QgsPointCompare if ( mTolerance <= 0 ) return p1.x() == p2.x() ? p1.y() < p2.y() : p1.x() < p2.x(); - double tx1 = ceil( p1.x() / mTolerance ); - double tx2 = ceil( p2.x() / mTolerance ); + double tx1 = std::ceil( p1.x() / mTolerance ); + double tx2 = std::ceil( p2.x() / mTolerance ); if ( tx1 == tx2 ) - return ceil( p1.y() / mTolerance ) < ceil( p2.y() / mTolerance ); + return ceil( p1.y() / mTolerance ) < std::ceil( p2.y() / mTolerance ); return tx1 < tx2; } diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 8d605143404..401fffb0a51 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -33,7 +33,7 @@ static double ceil_with_tolerance( double value ) if ( std::fabs( value - std::round( value ) ) < 1e-6 ) return std::round( value ); else - return qCeil( value ); + return std::ceil( value ); } static double floor_with_tolerance( double value ) diff --git a/src/analysis/raster/qgskde.cpp b/src/analysis/raster/qgskde.cpp index 62f5a13fb7a..e234006a6df 100644 --- a/src/analysis/raster/qgskde.cpp +++ b/src/analysis/raster/qgskde.cpp @@ -87,8 +87,8 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::prepare() if ( mBounds.isNull() ) return InvalidParameters; - int rows = qMax( ceil( mBounds.height() / mPixelSize ) + 1, 1.0 ); - int cols = qMax( ceil( mBounds.width() / mPixelSize ) + 1, 1.0 ); + int rows = qMax( std::ceil( mBounds.height() / mPixelSize ) + 1, 1.0 ); + int cols = qMax( std::ceil( mBounds.width() / mPixelSize ) + 1, 1.0 ); if ( !createEmptyLayer( driver, mBounds, rows, cols ) ) return FileCreationError; diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 9102d06f1b0..c6922d8b5d4 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -865,7 +865,7 @@ static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( ceil( x ) ); + return QVariant( std::ceil( x ) ); } static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index 1fa75fa7208..cb3858eb0a5 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -2705,12 +2705,12 @@ int QgsGeos::geomDigits( const GEOSGeometry *geom ) GEOSCoordSeq_getX_r( geosinit.ctxt, bBoxCoordSeq, i, &t ); int digits; - digits = ceil( log10( std::fabs( t ) ) ); + digits = std::ceil( log10( std::fabs( t ) ) ); if ( digits > maxDigits ) maxDigits = digits; GEOSCoordSeq_getY_r( geosinit.ctxt, bBoxCoordSeq, i, &t ); - digits = ceil( log10( std::fabs( t ) ) ); + digits = std::ceil( log10( std::fabs( t ) ) ); if ( digits > maxDigits ) maxDigits = digits; } diff --git a/src/core/geometry/qgsrectangle.cpp b/src/core/geometry/qgsrectangle.cpp index 8e8ea6ad0f6..4ad6b3ac3e7 100644 --- a/src/core/geometry/qgsrectangle.cpp +++ b/src/core/geometry/qgsrectangle.cpp @@ -305,7 +305,7 @@ QString QgsRectangle::toString( int precision ) const precision = 0; if ( ( width() < 10 || height() < 10 ) && ( width() > 0 && height() > 0 ) ) { - precision = static_cast( ceil( -1.0 * log10( qMin( width(), height() ) ) ) ) + 1; + precision = static_cast( std::ceil( -1.0 * log10( qMin( width(), height() ) ) ) ) + 1; // sanity check if ( precision > 20 ) precision = 20; diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 885d7dde125..0cfa1528147 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -1460,8 +1460,8 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin px0 = box->width / 2.0; py0 = box->length / 2.0; - px0 -= ceil( px0 / dx ) * dx; - py0 -= ceil( py0 / dy ) * dy; + px0 -= std::ceil( px0 / dx ) * dx; + py0 -= std::ceil( py0 / dy ) * dy; for ( px = px0; px <= box->width; px += dx ) { diff --git a/src/core/pal/layer.cpp b/src/core/pal/layer.cpp index 0b3e0496fda..5fc6ee53794 100644 --- a/src/core/pal/layer.cpp +++ b/src/core/pal/layer.cpp @@ -427,7 +427,7 @@ void Layer::chopFeaturesAtRepeatDistance() double chopInterval = fpart->repeatDistance(); if ( chopInterval != 0. && GEOSGeomTypeId_r( geosctxt, geom ) == GEOS_LINESTRING ) { - chopInterval *= ceil( fpart->getLabelWidth() / fpart->repeatDistance() ); + chopInterval *= std::ceil( fpart->getLabelWidth() / fpart->repeatDistance() ); double bmin[2], bmax[2]; fpart->getBoundingBox( bmin, bmax ); diff --git a/src/core/qgscoordinatetransform.cpp b/src/core/qgscoordinatetransform.cpp index ef6587d1e7c..dc762e97645 100644 --- a/src/core/qgscoordinatetransform.cpp +++ b/src/core/qgscoordinatetransform.cpp @@ -353,8 +353,8 @@ QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &r // TODO: how to effectively and precisely reproject bounding box? const int nPoints = 1000; double d = sqrt( ( rect.width() * rect.height() ) / std::pow( sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) ); - int nXPoints = static_cast< int >( ceil( rect.width() / d ) ) + 1; - int nYPoints = static_cast< int >( ceil( rect.height() / d ) ) + 1; + int nXPoints = static_cast< int >( std::ceil( rect.width() / d ) ) + 1; + int nYPoints = static_cast< int >( std::ceil( rect.height() / d ) ) + 1; QgsRectangle bb_rect; bb_rect.setMinimal(); diff --git a/src/core/qgscoordinateutils.cpp b/src/core/qgscoordinateutils.cpp index 9794f1fd3a7..561c47837a6 100644 --- a/src/core/qgscoordinateutils.cpp +++ b/src/core/qgscoordinateutils.cpp @@ -45,7 +45,7 @@ int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, c // having enough decimal places to show the difference in position between adjacent pixels. // Also avoid taking the log of 0. if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) ) - dp = static_cast( ceil( -1.0 * log10( mapUnitsPerPixel ) ) ); + dp = static_cast( std::ceil( -1.0 * log10( mapUnitsPerPixel ) ) ); } else { diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 523712b8c87..93667c56a46 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -270,7 +270,7 @@ bool QgsField::convertCompatible( QVariant &v ) const { double s = std::pow( 10, d->precision ); double d = v.toDouble() * s; - v = QVariant( ( d < 0 ? ceil( d - 0.5 ) : floor( d + 0.5 ) ) / s ); + v = QVariant( ( d < 0 ? std::ceil( d - 0.5 ) : floor( d + 0.5 ) ) / s ); return true; } diff --git a/src/core/raster/qgspalettedrasterrenderer.cpp b/src/core/raster/qgspalettedrasterrenderer.cpp index af60d0e2562..07dae110b7b 100644 --- a/src/core/raster/qgspalettedrasterrenderer.cpp +++ b/src/core/raster/qgspalettedrasterrenderer.cpp @@ -408,7 +408,7 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromRas double min = stats.minimumValue; double max = stats.maximumValue; // need count of every individual value - int bins = ceil( max - min ) + 1; + int bins = std::ceil( max - min ) + 1; if ( bins <= 0 ) return ClassData(); diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index 75da35436db..75e10f7603b 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -123,14 +123,14 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b { int col = floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); - col = ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); + col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); - row = ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); + row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = std::round( tmpExtent.width() / tmpXRes ); diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index 66ada3f9575..274ace2710c 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -357,7 +357,7 @@ void QgsRasterInterface::initHistogram( QgsRasterHistogram &histogram, mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::UInt32 ) ) { if ( myBinCount > histogram.maximum - histogram.minimum + 1 ) - myBinCount = int( ceil( histogram.maximum - histogram.minimum + 1 ) ); + myBinCount = int( std::ceil( histogram.maximum - histogram.minimum + 1 ) ); } } } @@ -530,7 +530,7 @@ void QgsRasterInterface::cumulativeCut( int bandNo, return; // for byte bands make sure bin count == actual range - int myBinCount = ( mySrcDataType == Qgis::Byte ) ? int( ceil( stats.maximumValue - stats.minimumValue + 1 ) ) : 0; + int myBinCount = ( mySrcDataType == Qgis::Byte ) ? int( std::ceil( stats.maximumValue - stats.minimumValue + 1 ) ) : 0; QgsRasterHistogram myHistogram = histogram( bandNo, myBinCount, stats.minimumValue, stats.maximumValue, extent, sampleSize ); //QgsRasterHistogram myHistogram = histogram( bandNo, 0, std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), extent, sampleSize ); @@ -568,7 +568,7 @@ void QgsRasterInterface::cumulativeCut( int bandNo, if ( lowerValue != std::numeric_limits::quiet_NaN() ) lowerValue = floor( lowerValue ); if ( upperValue != std::numeric_limits::quiet_NaN() ) - upperValue = ceil( upperValue ); + upperValue = std::ceil( upperValue ); } } diff --git a/src/core/raster/qgsrasterlayerrenderer.cpp b/src/core/raster/qgsrasterlayerrenderer.cpp index d6b1e317c4b..5cb69653314 100644 --- a/src/core/raster/qgsrasterlayerrenderer.cpp +++ b/src/core/raster/qgsrasterlayerrenderer.cpp @@ -134,8 +134,8 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer *layer, QgsRender // the provider anyway mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) ); mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) ); - mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) ); - mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) ); + mRasterViewPort->mBottomRightPoint.setX( std::ceil( mRasterViewPort->mBottomRightPoint.x() ) ); + mRasterViewPort->mBottomRightPoint.setY( std::ceil( mRasterViewPort->mBottomRightPoint.y() ) ); // recalc myRasterExtent to aligned values myRasterExtent.set( mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(), diff --git a/src/core/raster/qgsrasterprojector.cpp b/src/core/raster/qgsrasterprojector.cpp index b57bdd0b06e..ee50c0e2a60 100644 --- a/src/core/raster/qgsrasterprojector.cpp +++ b/src/core/raster/qgsrasterprojector.cpp @@ -259,7 +259,7 @@ void ProjectorData::calcSrcExtent() double x = mExtent.xMinimum() + col * mMaxSrcXRes; mSrcExtent.setXMinimum( x ); - col = ceil( ( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes ); + col = std::ceil( ( mSrcExtent.xMaximum() - mExtent.xMinimum() ) / mMaxSrcXRes ); x = mExtent.xMinimum() + col * mMaxSrcXRes; mSrcExtent.setXMaximum( x ); } @@ -269,7 +269,7 @@ void ProjectorData::calcSrcExtent() double y = mExtent.yMaximum() - row * mMaxSrcYRes; mSrcExtent.setYMaximum( y ); - row = ceil( ( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes ); + row = std::ceil( ( mExtent.yMaximum() - mSrcExtent.yMinimum() ) / mMaxSrcYRes ); y = mExtent.yMaximum() - row * mMaxSrcYRes; mSrcExtent.setYMinimum( y ); } diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index 7bfe1b73383..3f25d4dfcb0 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -2617,7 +2617,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & // Add buffer based on bleed but keep precisely the height/width ratio (angle) // thus we add integer multiplications of width and height covering the bleed - int bufferMulti = qMax( qCeil( outputPixelBleed / width ), qCeil( outputPixelBleed / width ) ); + int bufferMulti = qMax( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ); // Always buffer at least once so that center of line marker in upper right corner // does not fall outside due to representation error diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 00746b95278..7dbdca2cb92 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3948,7 +3948,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, } // Maybe used to correct for the epsilon here?? int start = floor( minimum / unit + 1e-07 ); - int end = ceil( maximum / unit - 1e-07 ); + int end = std::ceil( maximum / unit - 1e-07 ); // Extend the range out beyond the data. Does this ever happen?? while ( start * unit > minimum + ( 1e-07 * unit ) ) diff --git a/src/gui/qgscolorswatchgrid.cpp b/src/gui/qgscolorswatchgrid.cpp index 9122282ab48..ecf8d193c87 100644 --- a/src/gui/qgscolorswatchgrid.cpp +++ b/src/gui/qgscolorswatchgrid.cpp @@ -240,7 +240,7 @@ void QgsColorSwatchGrid::focusOutEvent( QFocusEvent *event ) int QgsColorSwatchGrid::calculateHeight() const { - int numberRows = ceil( ( double )mColors.length() / NUMBER_COLORS_PER_ROW ); + int numberRows = std::ceil( ( double )mColors.length() / NUMBER_COLORS_PER_ROW ); return numberRows * ( mSwatchSize ) + ( numberRows - 1 ) * mSwatchSpacing + mSwatchMargin + mLabelHeight + 0.5 * mLabelMargin + mSwatchMargin; } diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index b5f624a4b35..5859b3f8a9d 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -1565,7 +1565,7 @@ void QgsColorPreviewWidget::drawColor( const QColor &color, QRect rect, QPainter opaqueColor.setAlpha( 255 ); QBrush opaqueBrush = QBrush( opaqueColor ); painter.setBrush( opaqueBrush ); - painter.drawRect( rect.left(), rect.top(), ceil( rect.width() / 2.0 ), rect.height() ); + painter.drawRect( rect.left(), rect.top(), std::ceil( rect.width() / 2.0 ), rect.height() ); } else { diff --git a/src/gui/qgsdial.cpp b/src/gui/qgsdial.cpp index 055df36d2a1..f4c62dce346 100644 --- a/src/gui/qgsdial.cpp +++ b/src/gui/qgsdial.cpp @@ -90,14 +90,14 @@ void QgsDial::update() if ( minimum() != 0 ) QDial::setMinimum( 0 ); - int max = qCeil( ( mMax.toDouble() - mMin.toDouble() ) / mStep.toDouble() ); + int max = std::ceil( ( mMax.toDouble() - mMin.toDouble() ) / mStep.toDouble() ); if ( maximum() != max ) QDial::setMaximum( max ); if ( singleStep() != 1 ) QDial::setSingleStep( 1 ); - QDial::setValue( qCeil( ( mValue.toDouble() - mMin.toDouble() ) / mStep.toDouble() ) ); + QDial::setValue( std::ceil( ( mValue.toDouble() - mMin.toDouble() ) / mStep.toDouble() ) ); } connect( this, static_cast < void ( QDial::* )( int ) > ( &QDial::valueChanged ), this, &QgsDial::onValueChanged ); diff --git a/src/gui/qgsslider.cpp b/src/gui/qgsslider.cpp index 61fe99d7f83..770bbbaaf11 100644 --- a/src/gui/qgsslider.cpp +++ b/src/gui/qgsslider.cpp @@ -95,14 +95,14 @@ void QgsSlider::update() if ( minimum() != 0 ) QSlider::setMinimum( 0 ); - int max = qCeil( ( mMax.toDouble() - mMin.toDouble() ) / mStep.toDouble() ); + int max = std::ceil( ( mMax.toDouble() - mMin.toDouble() ) / mStep.toDouble() ); if ( maximum() != max ) QSlider::setMaximum( max ); if ( singleStep() != 1 ) QSlider::setSingleStep( 1 ); - QSlider::setValue( qCeil( ( mValue.toDouble() - mMin.toDouble() ) / mStep.toDouble() ) ); + QSlider::setValue( std::ceil( ( mValue.toDouble() - mMin.toDouble() ) / mStep.toDouble() ) ); } connect( this, &QSlider::valueChanged, this, &QgsSlider::onValueChanged ); diff --git a/src/plugins/georeferencer/qgsgeorefplugingui.cpp b/src/plugins/georeferencer/qgsgeorefplugingui.cpp index 673d329103d..c5fe6f802e0 100644 --- a/src/plugins/georeferencer/qgsgeorefplugingui.cpp +++ b/src/plugins/georeferencer/qgsgeorefplugingui.cpp @@ -805,7 +805,7 @@ void QgsGeorefPluginGui::updateMouseCoordinatePrecision() // to show the difference in position between adjacent pixels. // Also avoid taking the log of 0. if ( mCanvas->mapUnitsPerPixel() != 0.0 ) - dp = static_cast( ceil( -1.0 * log10( mCanvas->mapUnitsPerPixel() ) ) ); + dp = static_cast( std::ceil( -1.0 * log10( mCanvas->mapUnitsPerPixel() ) ) ); } else dp = QgsProject::instance()->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) ); diff --git a/src/providers/arcgisrest/qgsamsprovider.cpp b/src/providers/arcgisrest/qgsamsprovider.cpp index e49adab2a7f..9417271a386 100644 --- a/src/providers/arcgisrest/qgsamsprovider.cpp +++ b/src/providers/arcgisrest/qgsamsprovider.cpp @@ -289,8 +289,8 @@ void QgsAmsProvider::draw( const QgsRectangle &viewExtent, int pixelWidth, int p // tile_y = oy - j * (resolution * tileHeight) int ixStart = qFloor( ( viewExtent.xMinimum() - ox ) / ( tileWidth * resolution ) ); int iyStart = qFloor( ( oy - viewExtent.yMaximum() ) / ( tileHeight * resolution ) ); - int ixEnd = qCeil( ( viewExtent.xMaximum() - ox ) / ( tileWidth * resolution ) ); - int iyEnd = qCeil( ( oy - viewExtent.yMinimum() ) / ( tileHeight * resolution ) ); + int ixEnd = std::ceil( ( viewExtent.xMaximum() - ox ) / ( tileWidth * resolution ) ); + int iyEnd = std::ceil( ( oy - viewExtent.yMinimum() ) / ( tileHeight * resolution ) ); double imX = ( viewExtent.xMinimum() - ox ) / resolution; double imY = ( oy - viewExtent.yMaximum() ) / resolution; diff --git a/src/providers/wms/qgswmscapabilities.cpp b/src/providers/wms/qgswmscapabilities.cpp index 8eeec9f316c..641b88b0821 100644 --- a/src/providers/wms/qgswmscapabilities.cpp +++ b/src/providers/wms/qgswmscapabilities.cpp @@ -1286,8 +1286,8 @@ void QgsWmsCapabilities::parseTileSetProfile( QDomElement const &e ) double r = rS.toDouble(); m.identifier = QString::number( i ); Q_ASSERT( l.boundingBoxes.size() == 1 ); - m.matrixWidth = ceil( l.boundingBoxes.at( 0 ).box.width() / m.tileWidth / r ); - m.matrixHeight = ceil( l.boundingBoxes.at( 0 ).box.height() / m.tileHeight / r ); + m.matrixWidth = std::ceil( l.boundingBoxes.at( 0 ).box.width() / m.tileWidth / r ); + m.matrixHeight = std::ceil( l.boundingBoxes.at( 0 ).box.height() / m.tileHeight / r ); m.topLeft = QgsPointXY( l.boundingBoxes.at( 0 ).box.xMinimum(), l.boundingBoxes.at( 0 ).box.yMinimum() + m.matrixHeight * m.tileHeight * r ); m.tres = r; ms.tileMatrices.insert( r, m ); diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index 8436c40a63b..84242f9f33e 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -659,8 +659,8 @@ QImage *QgsWmsProvider::draw( QgsRectangle const &viewExtent, int pixelWidth, in tempTm->topLeft = QgsPointXY( mLayerExtent.xMinimum(), mLayerExtent.yMaximum() ); tempTm->tileWidth = mSettings.mMaxWidth; tempTm->tileHeight = mSettings.mMaxHeight; - tempTm->matrixWidth = ceil( mLayerExtent.width() / mSettings.mMaxWidth / vres ); - tempTm->matrixHeight = ceil( mLayerExtent.height() / mSettings.mMaxHeight / vres ); + tempTm->matrixWidth = std::ceil( mLayerExtent.width() / mSettings.mMaxWidth / vres ); + tempTm->matrixHeight = std::ceil( mLayerExtent.height() / mSettings.mMaxHeight / vres ); tempTm->tres = vres; tm = tempTm.get(); @@ -2275,7 +2275,7 @@ QString QgsWmsProvider::metadata() if ( mLayerExtent.yMaximum() > r.yMaximum() ) { metadata += QStringLiteral( "%2\">%3" ) - .arg( tr( "%n missing row(s)", nullptr, ( int ) ceil( ( mLayerExtent.yMaximum() - r.yMaximum() ) / th ) ), + .arg( tr( "%n missing row(s)", nullptr, ( int ) std::ceil( ( mLayerExtent.yMaximum() - r.yMaximum() ) / th ) ), tr( "Layer's upper bound: %1" ).arg( mLayerExtent.yMaximum(), 0, 'f' ) ) .arg( r.yMaximum(), 0, 'f' ); } @@ -2288,7 +2288,7 @@ QString QgsWmsProvider::metadata() if ( mLayerExtent.xMinimum() < r.xMinimum() ) { metadata += QStringLiteral( "%2\">%3" ) - .arg( tr( "%n missing column(s)", nullptr, ( int ) ceil( ( r.xMinimum() - mLayerExtent.xMinimum() ) / tw ) ), + .arg( tr( "%n missing column(s)", nullptr, ( int ) std::ceil( ( r.xMinimum() - mLayerExtent.xMinimum() ) / tw ) ), tr( "Layer's left bound: %1" ).arg( mLayerExtent.xMinimum(), 0, 'f' ) ) .arg( r.xMinimum(), 0, 'f' ); } @@ -2301,7 +2301,7 @@ QString QgsWmsProvider::metadata() if ( mLayerExtent.yMaximum() > r.yMaximum() ) { metadata += QStringLiteral( "%2\">%3" ) - .arg( tr( "%n missing row(s)", nullptr, ( int ) ceil( ( mLayerExtent.yMaximum() - r.yMaximum() ) / th ) ), + .arg( tr( "%n missing row(s)", nullptr, ( int ) std::ceil( ( mLayerExtent.yMaximum() - r.yMaximum() ) / th ) ), tr( "Layer's lower bound: %1" ).arg( mLayerExtent.yMaximum(), 0, 'f' ) ) .arg( r.yMaximum(), 0, 'f' ); } @@ -2314,7 +2314,7 @@ QString QgsWmsProvider::metadata() if ( mLayerExtent.xMaximum() > r.xMaximum() ) { metadata += QStringLiteral( "%2\">%3" ) - .arg( tr( "%n missing column(s)", nullptr, ( int ) ceil( ( mLayerExtent.xMaximum() - r.xMaximum() ) / tw ) ), + .arg( tr( "%n missing column(s)", nullptr, ( int ) std::ceil( ( mLayerExtent.xMaximum() - r.xMaximum() ) / tw ) ), tr( "Layer's right bound: %1" ).arg( mLayerExtent.xMaximum(), 0, 'f' ) ) .arg( r.xMaximum(), 0, 'f' ); } @@ -4044,7 +4044,7 @@ static QString formatDouble( double x ) { if ( x == 0.0 ) return QStringLiteral( "0" ); - const int numberOfDecimals = qMax( 0, 19 - static_cast( ceil( log10( std::fabs( x ) ) ) ) ); + const int numberOfDecimals = qMax( 0, 19 - static_cast( std::ceil( log10( std::fabs( x ) ) ) ) ); return qgsDoubleToString( x, numberOfDecimals ); } diff --git a/tests/src/core/testziplayer.cpp b/tests/src/core/testziplayer.cpp index 6fc507537d6..4ab5e19baa3 100644 --- a/tests/src/core/testziplayer.cpp +++ b/tests/src/core/testziplayer.cpp @@ -248,7 +248,7 @@ int TestZipLayer::getLayerTransparency( const QString &myFileName, const QString QgsRasterLayer *layer = dynamic_cast( myLayer ); if ( layer && layer->renderer() ) { - myTransparency = ceil( layer->renderer()->opacity() * 255 ); + myTransparency = std::ceil( layer->renderer()->opacity() * 255 ); } } } From 2e5d1abbb18aa9c32de65068e3f8d300435cb99c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:05:22 +1000 Subject: [PATCH 120/364] (q)cos -> std::cos --- src/analysis/raster/qgshillshadefilter.cpp | 2 +- src/analysis/raster/qgsrastermatrix.cpp | 2 +- src/analysis/raster/qgsrastermatrix.h | 2 +- src/analysis/raster/qgsrelief.cpp | 2 +- src/app/dwg/libdxfrw/drw_entities.cpp | 6 +-- src/app/dwg/qgsdwgimporter.cpp | 12 ++--- src/app/nodetool/qgsnodetool.cpp | 2 +- src/app/qgsdecorationnortharrow.cpp | 4 +- src/app/qgsdecorationnortharrowdialog.cpp | 4 +- src/app/qgsmaptoollabel.cpp | 4 +- src/app/qgsmaptooloffsetpointsymbol.cpp | 2 +- src/app/qgsmaptoolrotatefeature.cpp | 8 +-- src/app/qgsmaptoolrotatelabel.cpp | 4 +- src/app/qgsmaptoolselectradius.cpp | 2 +- src/app/qgspointrotationitem.cpp | 2 +- src/app/qgsrastercalcdialog.cpp | 2 +- src/core/composer/qgscomposerarrow.cpp | 4 +- src/core/composer/qgscomposerutils.cpp | 14 +++--- src/core/composer/qgscomposition.cpp | 10 ++-- src/core/effects/qgsshadoweffect.cpp | 2 +- src/core/expression/qgsexpressionfunction.cpp | 2 +- src/core/geometry/qgsellipse.cpp | 10 ++-- src/core/geometry/qgsgeometryutils.cpp | 2 +- .../geometry/qgsinternalgeometryengine.cpp | 4 +- src/core/geometry/qgspoint.cpp | 6 +-- src/core/geometry/qgsregularpolygon.cpp | 4 +- src/core/geometry/qgsregularpolygon.h | 2 +- src/core/pal/feature.cpp | 50 +++++++++---------- src/core/pal/geomfunction.cpp | 4 +- src/core/pal/labelposition.cpp | 4 +- src/core/pal/pointset.cpp | 4 +- src/core/qgsdistancearea.cpp | 32 ++++++------ src/core/qgsmapsettingsutils.cpp | 8 +-- src/core/qgspallabeling.cpp | 4 +- src/core/qgspointxy.cpp | 2 +- src/core/qgsscalecalculator.cpp | 2 +- src/core/qgstextrenderer.cpp | 6 +-- src/core/qgsvector.cpp | 2 +- src/core/qgsvectorlayerlabelprovider.cpp | 4 +- src/core/raster/qgshillshaderenderer.cpp | 16 +++--- src/core/symbology/qgs25drenderer.cpp | 8 +-- src/core/symbology/qgsarrowsymbollayer.cpp | 2 +- src/core/symbology/qgsellipsesymbollayer.cpp | 2 +- src/core/symbology/qgsfillsymbollayer.cpp | 18 +++---- src/core/symbology/qgslinesymbollayer.cpp | 4 +- src/core/symbology/qgsmarkersymbollayer.cpp | 42 ++++++++-------- .../qgspointdisplacementrenderer.cpp | 4 +- src/core/symbology/qgssymbollayer.cpp | 2 +- .../symbology/qgsvectorfieldsymbollayer.cpp | 2 +- src/gui/qgsadvanceddigitizingcanvasitem.cpp | 6 +-- src/gui/qgsadvanceddigitizingdockwidget.cpp | 4 +- src/gui/qgscolorwidgets.cpp | 10 ++-- .../georeferencer/qgsgeorefplugingui.cpp | 4 +- .../georeferencer/qgsgeoreftransform.cpp | 8 +-- tests/src/core/testqgsgeometry.cpp | 2 +- 55 files changed, 188 insertions(+), 188 deletions(-) diff --git a/src/analysis/raster/qgshillshadefilter.cpp b/src/analysis/raster/qgshillshadefilter.cpp index a2221c12fef..0abd246d299 100644 --- a/src/analysis/raster/qgshillshadefilter.cpp +++ b/src/analysis/raster/qgshillshadefilter.cpp @@ -49,5 +49,5 @@ float QgsHillshadeFilter::processNineCellWindow( float *x11, float *x21, float * { aspect_rad = M_PI + std::atan2( derX, derY ); } - return qMax( 0.0, 255.0 * ( ( cos( zenith_rad ) * cos( slope_rad ) ) + ( sin( zenith_rad ) * sin( slope_rad ) * cos( azimuth_rad - aspect_rad ) ) ) ); + return qMax( 0.0, 255.0 * ( ( std::cos( zenith_rad ) * std::cos( slope_rad ) ) + ( sin( zenith_rad ) * sin( slope_rad ) * std::cos( azimuth_rad - aspect_rad ) ) ) ); } diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index f8b033ee865..b77b6ddfb94 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -223,7 +223,7 @@ bool QgsRasterMatrix::oneArgumentOperation( OneArgOperator op ) mData[i] = sin( value ); break; case opCOS: - mData[i] = cos( value ); + mData[i] = std::cos( value ); break; case opTAN: mData[i] = tan( value ); diff --git a/src/analysis/raster/qgsrastermatrix.h b/src/analysis/raster/qgsrastermatrix.h index d1f99802962..613a693f14c 100644 --- a/src/analysis/raster/qgsrastermatrix.h +++ b/src/analysis/raster/qgsrastermatrix.h @@ -124,7 +124,7 @@ class ANALYSIS_EXPORT QgsRasterMatrix bool twoArgumentOperation( TwoArgOperator op, const QgsRasterMatrix &other ); double calculateTwoArgumentOp( TwoArgOperator op, double arg1, double arg2 ) const; - /*sqrt, sin, cos, tan, asin, acos, atan*/ + /*sqrt, sin, std::cos, tan, asin, acos, atan*/ bool oneArgumentOperation( OneArgOperator op ); bool testPowerValidity( double base, double power ) const; }; diff --git a/src/analysis/raster/qgsrelief.cpp b/src/analysis/raster/qgsrelief.cpp index 7803e8b89e9..29a5ff89343 100644 --- a/src/analysis/raster/qgsrelief.cpp +++ b/src/analysis/raster/qgsrelief.cpp @@ -362,7 +362,7 @@ bool QgsRelief::processNineCellWindow( float *x1, float *x2, float *x3, float *x int r3, g3, b3; if ( angle_diff < 90 ) { - int aspectVal = ( 1 - cos( angle_diff * M_PI / 180 ) ) * 255; + int aspectVal = ( 1 - std::cos( angle_diff * M_PI / 180 ) ) * 255; r3 = 0.5 * 255 + hillShadeValue315 * 0.5; g3 = 0.5 * 255 + hillShadeValue315 * 0.5; b3 = 0.5 * aspectVal + hillShadeValue315 * 0.5; diff --git a/src/app/dwg/libdxfrw/drw_entities.cpp b/src/app/dwg/libdxfrw/drw_entities.cpp index f2a80a095cb..c60fd03e8e2 100644 --- a/src/app/dwg/libdxfrw/drw_entities.cpp +++ b/src/app/dwg/libdxfrw/drw_entities.cpp @@ -956,9 +956,9 @@ void DRW_Ellipse::toPolyline( DRW_Polyline *pol, int parts ) const { double radMajor = sqrt( secPoint.x * secPoint.x + secPoint.y * secPoint.y ); double radMinor = radMajor * ratio; - //calculate sin & cos of included angle + //calculate sin & std::cos of included angle double incAngle = std::atan2( secPoint.y, secPoint.x ); - double cosRot = cos( incAngle ); + double cosRot = std::cos( incAngle ); double sinRot = sin( incAngle ); incAngle = M_PIx2 / parts; @@ -969,7 +969,7 @@ void DRW_Ellipse::toPolyline( DRW_Polyline *pol, int parts ) const while ( curAngle < endAngle ) { - double cosCurr = cos( curAngle ); + double cosCurr = std::cos( curAngle ); double sinCurr = sin( curAngle ); double x = basePoint.x + cosCurr * cosRot * radMajor - sinCurr * sinRot * radMinor; double y = basePoint.y + cosCurr * sinRot * radMajor + sinCurr * cosRot * radMinor; diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index 493d6b9c230..f64c34c5679 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -1341,9 +1341,9 @@ void QgsDwgImporter::addArc( const DRW_Arc &data ) QgsCircularString c; c.setPoints( QgsPointSequence() - << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + cos( a0 ) * data.mRadius, data.basePoint.y + sin( a0 ) * data.mRadius ) - << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + cos( a1 ) * data.mRadius, data.basePoint.y + sin( a1 ) * data.mRadius ) - << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + cos( a2 ) * data.mRadius, data.basePoint.y + sin( a2 ) * data.mRadius ) + << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a0 ) * data.mRadius, data.basePoint.y + sin( a0 ) * data.mRadius ) + << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a1 ) * data.mRadius, data.basePoint.y + sin( a1 ) * data.mRadius ) + << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a2 ) * data.mRadius, data.basePoint.y + sin( a2 ) * data.mRadius ) ); if ( !createFeature( layer, f, c ) ) @@ -1446,7 +1446,7 @@ bool QgsDwgImporter::curveFromLWPolyline( const DRW_LWPolyline &data, QgsCompoun double dy = data.vertlist[i1]->y - data.vertlist[i0]->y; double c = sqrt( dx * dx + dy * dy ); double r = c / 2.0 / sin( a ); - double h = r * ( 1 - cos( a ) ); + double h = r * ( 1 - std::cos( a ) ); s << QgsPoint( QgsWkbTypes::PointZ, data.vertlist[i0]->x + 0.5 * dx + h * dy / c, @@ -1557,7 +1557,7 @@ void QgsDwgImporter::addLWPolyline( const DRW_LWPolyline &data ) double dy = p1.y() - p0.y(); double c = sqrt( dx * dx + dy * dy ); double r = c / 2.0 / sin( a ); - double h = r * ( 1 - cos( a ) ); + double h = r * ( 1 - std::cos( a ) ); s << QgsPoint( QgsWkbTypes::PointZ, p0.x() + 0.5 * dx + h * dy / c, @@ -1759,7 +1759,7 @@ void QgsDwgImporter::addPolyline( const DRW_Polyline &data ) double dz = p1.z() - p0.z(); double c = sqrt( dx * dx + dy * dy ); double r = c / 2.0 / sin( a ); - double h = r * ( 1 - cos( a ) ); + double h = r * ( 1 - std::cos( a ) ); s << QgsPoint( QgsWkbTypes::PointZ, p0.x() + 0.5 * dx + h * dy / c, diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index 561ae2bca06..546b1c899ac 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -682,7 +682,7 @@ QgsPointXY QgsNodeTool::positionForEndpointMarker( const QgsPointLocator::Match double dy = pt1.y() - pt0.y(); double dist = 15 * canvas()->mapSettings().mapUnitsPerPixel(); double angle = std::atan2( dy, dx ); // to the top: angle=0, to the right: angle=90, to the left: angle=-90 - double x = pt1.x() + cos( angle ) * dist; + double x = pt1.x() + std::cos( angle ) * dist; double y = pt1.y() + sin( angle ) * dist; return QgsPointXY( x, y ); } diff --git a/src/app/qgsdecorationnortharrow.cpp b/src/app/qgsdecorationnortharrow.cpp index 9a95c5f4142..47329a4faaa 100644 --- a/src/app/qgsdecorationnortharrow.cpp +++ b/src/app/qgsdecorationnortharrow.cpp @@ -137,12 +137,12 @@ void QgsDecorationNorthArrow::render( const QgsMapSettings &mapSettings, QgsRend double myRadiansDouble = mRotationInt * M_PI / 180.0; int xShift = static_cast( ( - ( centerXDouble * cos( myRadiansDouble ) ) + + ( centerXDouble * std::cos( myRadiansDouble ) ) + ( centerYDouble * sin( myRadiansDouble ) ) ) - centerXDouble ); int yShift = static_cast( ( ( -centerXDouble * sin( myRadiansDouble ) ) + - ( centerYDouble * cos( myRadiansDouble ) ) + ( centerYDouble * std::cos( myRadiansDouble ) ) ) - centerYDouble ); // need width/height of paint device diff --git a/src/app/qgsdecorationnortharrowdialog.cpp b/src/app/qgsdecorationnortharrowdialog.cpp index 8bfa6617b2c..dd4ea73af9e 100644 --- a/src/app/qgsdecorationnortharrowdialog.cpp +++ b/src/app/qgsdecorationnortharrowdialog.cpp @@ -155,12 +155,12 @@ void QgsDecorationNorthArrowDialog::drawNorthArrow() const double PI = 3.14159265358979323846; double myRadiansDouble = ( PI / 180 ) * rotation; int xShift = static_cast( ( - ( centerXDouble * cos( myRadiansDouble ) ) + + ( centerXDouble * std::cos( myRadiansDouble ) ) + ( centerYDouble * sin( myRadiansDouble ) ) ) - centerXDouble ); int yShift = static_cast( ( ( -centerXDouble * sin( myRadiansDouble ) ) + - ( centerYDouble * cos( myRadiansDouble ) ) + ( centerYDouble * std::cos( myRadiansDouble ) ) ) - centerYDouble ); //draw the pixmap in the proper position diff --git a/src/app/qgsmaptoollabel.cpp b/src/app/qgsmaptoollabel.cpp index e5ad41aff11..a3ad0665417 100644 --- a/src/app/qgsmaptoollabel.cpp +++ b/src/app/qgsmaptoollabel.cpp @@ -376,8 +376,8 @@ bool QgsMapToolLabel::currentLabelRotationPoint( QgsPointXY &pos, bool ignoreUps } double angle = mCurrentLabel.pos.rotation; - double xd = xdiff * cos( angle ) - ydiff * sin( angle ); - double yd = xdiff * sin( angle ) + ydiff * cos( angle ); + double xd = xdiff * std::cos( angle ) - ydiff * sin( angle ); + double yd = xdiff * sin( angle ) + ydiff * std::cos( angle ); if ( mCurrentLabel.pos.upsideDown && !ignoreUpsideDown ) { pos.setX( pos.x() - xd ); diff --git a/src/app/qgsmaptooloffsetpointsymbol.cpp b/src/app/qgsmaptooloffsetpointsymbol.cpp index 5d2399a1cd0..09e90f95c9a 100644 --- a/src/app/qgsmaptooloffsetpointsymbol.cpp +++ b/src/app/qgsmaptooloffsetpointsymbol.cpp @@ -270,7 +270,7 @@ QPointF QgsMapToolOffsetPointSymbol::calculateOffset( const QgsPointXY &startPoi QPointF QgsMapToolOffsetPointSymbol::rotatedOffset( QPointF offset, double angle ) const { angle = DEG2RAD( 360 - angle ); - double c = cos( angle ), s = sin( angle ); + double c = std::cos( angle ), s = sin( angle ); return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c ); } diff --git a/src/app/qgsmaptoolrotatefeature.cpp b/src/app/qgsmaptoolrotatefeature.cpp index d807b97b8d2..c07dd2669ee 100644 --- a/src/app/qgsmaptoolrotatefeature.cpp +++ b/src/app/qgsmaptoolrotatefeature.cpp @@ -351,12 +351,12 @@ void QgsMapToolRotateFeature::applyRotation( double rotation ) //calculations for affine transformation double angle = -1 * mRotation * ( PI / 180 ); QgsPointXY anchorPoint = toLayerCoordinates( vlayer, mStartPointMapCoords ); - double a = cos( angle ); + double a = std::cos( angle ); double b = -1 * sin( angle ); - double c = anchorPoint.x() - cos( angle ) * anchorPoint.x() + sin( angle ) * anchorPoint.y(); + double c = anchorPoint.x() - std::cos( angle ) * anchorPoint.x() + sin( angle ) * anchorPoint.y(); double d = sin( angle ); - double ee = cos( angle ); - double f = anchorPoint.y() - sin( angle ) * anchorPoint.x() - cos( angle ) * anchorPoint.y(); + double ee = std::cos( angle ); + double f = anchorPoint.y() - sin( angle ) * anchorPoint.x() - std::cos( angle ) * anchorPoint.y(); vlayer->beginEditCommand( tr( "Features Rotated" ) ); diff --git a/src/app/qgsmaptoolrotatelabel.cpp b/src/app/qgsmaptoolrotatelabel.cpp index bfff15d98aa..f71d61c40c1 100644 --- a/src/app/qgsmaptoolrotatelabel.cpp +++ b/src/app/qgsmaptoolrotatelabel.cpp @@ -230,8 +230,8 @@ QgsPointXY QgsMapToolRotateLabel::rotatePointCounterClockwise( const QgsPointXY double v1x = input.x() - centerPoint.x(); double v1y = input.y() - centerPoint.y(); - double v2x = cos( rad ) * v1x - sin( rad ) * v1y; - double v2y = sin( rad ) * v1x + cos( rad ) * v1y; + double v2x = std::cos( rad ) * v1x - sin( rad ) * v1y; + double v2y = sin( rad ) * v1x + std::cos( rad ) * v1y; return QgsPointXY( centerPoint.x() + v2x, centerPoint.y() + v2y ); } diff --git a/src/app/qgsmaptoolselectradius.cpp b/src/app/qgsmaptoolselectradius.cpp index 84ea673a5d8..59b0340c728 100644 --- a/src/app/qgsmaptoolselectradius.cpp +++ b/src/app/qgsmaptoolselectradius.cpp @@ -107,7 +107,7 @@ void QgsMapToolSelectRadius::setRadiusRubberBand( QgsPointXY &radiusEdge ) for ( int i = 0; i <= RADIUS_SEGMENTS; ++i ) { double theta = i * ( 2.0 * M_PI / RADIUS_SEGMENTS ); - QgsPointXY radiusPoint( mRadiusCenter.x() + r * cos( theta ), + QgsPointXY radiusPoint( mRadiusCenter.x() + r * std::cos( theta ), mRadiusCenter.y() + r * sin( theta ) ); mRubberBand->addPoint( radiusPoint, false ); } diff --git a/src/app/qgspointrotationitem.cpp b/src/app/qgspointrotationitem.cpp index d82435f882d..4220ae2fad1 100644 --- a/src/app/qgspointrotationitem.cpp +++ b/src/app/qgspointrotationitem.cpp @@ -56,7 +56,7 @@ void QgsPointRotationItem::paint( QPainter *painter ) { h = sqrt( ( double ) mPixmap.width() * mPixmap.width() + mPixmap.height() * mPixmap.height() ) / 2; //the half of the item diagonal dAngel = acos( mPixmap.width() / ( h * 2 ) ) * 180 / M_PI; //the diagonal angel of the original rect - x = h * cos( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); + x = h * std::cos( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); y = h * sin( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); } diff --git a/src/app/qgsrastercalcdialog.cpp b/src/app/qgsrastercalcdialog.cpp index e0f9d3994dc..73eab96b74b 100644 --- a/src/app/qgsrastercalcdialog.cpp +++ b/src/app/qgsrastercalcdialog.cpp @@ -355,7 +355,7 @@ void QgsRasterCalcDialog::on_mSqrtButton_clicked() void QgsRasterCalcDialog::on_mCosButton_clicked() { - mExpressionTextEdit->insertPlainText( QStringLiteral( " cos ( " ) ); + mExpressionTextEdit->insertPlainText( QStringLiteral( " std::cos ( " ) ); } void QgsRasterCalcDialog::on_mSinButton_clicked() diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp index e905053d22d..8ad50baa06f 100644 --- a/src/core/composer/qgscomposerarrow.cpp +++ b/src/core/composer/qgscomposerarrow.cpp @@ -280,8 +280,8 @@ void QgsComposerArrow::drawSVGMarker( QPainter *p, MarkerType type, const QStrin } QPointF rotatedFixPoint; double angleRad = angle / 180 * M_PI; - rotatedFixPoint.setX( fixPoint.x() * cos( angleRad ) + fixPoint.y() * -sin( angleRad ) ); - rotatedFixPoint.setY( fixPoint.x() * sin( angleRad ) + fixPoint.y() * cos( angleRad ) ); + rotatedFixPoint.setX( fixPoint.x() * std::cos( angleRad ) + fixPoint.y() * -sin( angleRad ) ); + rotatedFixPoint.setY( fixPoint.x() * sin( angleRad ) + fixPoint.y() * std::cos( angleRad ) ); p->translate( canvasPoint.x() - rotatedFixPoint.x(), canvasPoint.y() - rotatedFixPoint.y() ); } else diff --git a/src/core/composer/qgscomposerutils.cpp b/src/core/composer/qgscomposerutils.cpp index 737e2749ca8..8aa2425e517 100644 --- a/src/core/composer/qgscomposerutils.cpp +++ b/src/core/composer/qgscomposerutils.cpp @@ -42,10 +42,10 @@ void QgsComposerUtils::drawArrowHead( QPainter *p, const double x, const double QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth ); QPointF p1Rotated, p2Rotated; - p1Rotated.setX( p1.x() * cos( angleRad ) + p1.y() * -sin( angleRad ) ); - p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * cos( angleRad ) ); - p2Rotated.setX( p2.x() * cos( angleRad ) + p2.y() * -sin( angleRad ) ); - p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * cos( angleRad ) ); + p1Rotated.setX( p1.x() * std::cos( angleRad ) + p1.y() * -sin( angleRad ) ); + p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * std::cos( angleRad ) ); + p2Rotated.setX( p2.x() * std::cos( angleRad ) + p2.y() * -sin( angleRad ) ); + p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * std::cos( angleRad ) ); QPolygonF arrowHeadPoly; arrowHeadPoly << middlePoint; @@ -88,8 +88,8 @@ void QgsComposerUtils::rotate( const double angle, double &x, double &y ) { double rotToRad = angle * M_PI / 180.0; double xRot, yRot; - xRot = x * cos( rotToRad ) - y * sin( rotToRad ); - yRot = x * sin( rotToRad ) + y * cos( rotToRad ); + xRot = x * std::cos( rotToRad ) - y * sin( rotToRad ); + yRot = x * sin( rotToRad ) + y * std::cos( rotToRad ); x = xRot; y = yRot; } @@ -185,7 +185,7 @@ QRectF QgsComposerUtils::largestRotatedRectWithinBounds( const QRectF &originalR //convert angle to radians and flip double angleRad = -clippedRotation * M_DEG2RAD; - double cosAngle = cos( angleRad ); + double cosAngle = std::cos( angleRad ); double sinAngle = sin( angleRad ); //calculate size of bounds of rotated rectangle diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index bff1bfcf158..bdb5c80b9bf 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -3012,7 +3012,7 @@ double *QgsComposition::computeGeoTransform( const QgsComposerMap *map, const QR double mapYCenter = mapExtent.center().y(); double alpha = - map->mapRotation() / 180 * M_PI; double sinAlpha = sin( alpha ); - double cosAlpha = cos( alpha ); + double cosAlpha = std::cos( alpha ); // get the extent (in map units) for the exported region QPointF mapItemPos = map->pos(); @@ -3151,12 +3151,12 @@ void QgsComposition::computeWorldFileParameters( const QRectF &exportRegion, dou // rotation matrix double r[6]; - r[0] = cos( alpha ); + r[0] = std::cos( alpha ); r[1] = -sin( alpha ); - r[2] = xCenter * ( 1 - cos( alpha ) ) + yCenter * sin( alpha ); + r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * sin( alpha ); r[3] = sin( alpha ); - r[4] = cos( alpha ); - r[5] = - xCenter * sin( alpha ) + yCenter * ( 1 - cos( alpha ) ); + r[4] = std::cos( alpha ); + r[5] = - xCenter * sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) ); // result = rotation x scaling = rotation(scaling(X)) a = r[0] * s[0] + r[1] * s[3]; diff --git a/src/core/effects/qgsshadoweffect.cpp b/src/core/effects/qgsshadoweffect.cpp index 01efa61f335..90bb4733b51 100644 --- a/src/core/effects/qgsshadoweffect.cpp +++ b/src/core/effects/qgsshadoweffect.cpp @@ -56,7 +56,7 @@ void QgsShadowEffect::draw( QgsRenderContext &context ) double offsetDist = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale ); double angleRad = mOffsetAngle * M_PI / 180; // to radians - QPointF transPt( -offsetDist * cos( angleRad + M_PI / 2 ), + QPointF transPt( -offsetDist * std::cos( angleRad + M_PI / 2 ), -offsetDist * sin( angleRad + M_PI / 2 ) ); //transparency, scale diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index c6922d8b5d4..3101f5bb226 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -262,7 +262,7 @@ static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext * static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( cos( x ) ); + return QVariant( std::cos( x ) ); } static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index 363aff68f87..01dfa3c5df1 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -208,11 +208,11 @@ QgsPointSequence QgsEllipse::points( unsigned int segments ) const for ( QVector::const_iterator it = t.constBegin(); it != t.constEnd(); ++it ) { double x = mCenter.x() + - mSemiMajorAxis * cos( *it ) * cos( azimuth ) - + mSemiMajorAxis * std::cos( *it ) * std::cos( azimuth ) - mSemiMinorAxis * sin( *it ) * sin( azimuth ); double y = mCenter.y() + - mSemiMajorAxis * cos( *it ) * sin( azimuth ) + - mSemiMinorAxis * sin( *it ) * cos( azimuth ); + mSemiMajorAxis * std::cos( *it ) * sin( azimuth ) + + mSemiMinorAxis * sin( *it ) * std::cos( azimuth ); pts.push_back( QgsPoint( pType, x, y, z, m ) ); } @@ -257,10 +257,10 @@ QgsRectangle QgsEllipse::boundingBox() const double angle = mAzimuth * M_PI / 180.0; - double ux = mSemiMajorAxis * cos( angle ); + double ux = mSemiMajorAxis * std::cos( angle ); double uy = mSemiMinorAxis * sin( angle ); double vx = mSemiMajorAxis * sin( angle ); - double vy = mSemiMinorAxis * cos( angle ); + double vy = mSemiMinorAxis * std::cos( angle ); double halfHeight = sqrt( ux * ux + uy * uy ); double halfWidth = sqrt( vx * vx + vy * vy ); diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 59a3c104cc3..2ef3c2f1994 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -703,7 +703,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co addP2 = false; } - x = centerX + radius * cos( angle ); + x = centerX + radius * std::cos( angle ); y = centerY + radius * sin( angle ); if ( !hasZ && !hasM ) diff --git a/src/core/geometry/qgsinternalgeometryengine.cpp b/src/core/geometry/qgsinternalgeometryengine.cpp index c6b8261d0ec..0efe0c94f2d 100644 --- a/src/core/geometry/qgsinternalgeometryengine.cpp +++ b/src/core/geometry/qgsinternalgeometryengine.cpp @@ -491,8 +491,8 @@ QgsGeometry QgsInternalGeometryEngine::orthogonalize( double tolerance, int maxI return QgsGeometry(); } - double lowerThreshold = cos( ( 90 - angleThreshold ) * M_PI / 180.00 ); - double upperThreshold = cos( angleThreshold * M_PI / 180.0 ); + double lowerThreshold = std::cos( ( 90 - angleThreshold ) * M_PI / 180.00 ); + double upperThreshold = std::cos( angleThreshold * M_PI / 180.0 ); if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) ) { diff --git a/src/core/geometry/qgspoint.cpp b/src/core/geometry/qgspoint.cpp index db6a69c392b..1645723daf9 100644 --- a/src/core/geometry/qgspoint.cpp +++ b/src/core/geometry/qgspoint.cpp @@ -567,14 +567,14 @@ QgsPoint QgsPoint::project( double distance, double azimuth, double inclination if ( !is3D() && qgsDoubleNear( inclination, 90.0 ) ) { dx = distance * sin( radsXy ); - dy = distance * cos( radsXy ); + dy = distance * std::cos( radsXy ); } else { double radsZ = inclination * M_PI / 180.0; dx = distance * sin( radsZ ) * sin( radsXy ); - dy = distance * sin( radsZ ) * cos( radsXy ); - dz = distance * cos( radsZ ); + dy = distance * sin( radsZ ) * std::cos( radsXy ); + dz = distance * std::cos( radsZ ); } return QgsPoint( mX + dx, mY + dy, mZ + dz, mM, pType ); diff --git a/src/core/geometry/qgsregularpolygon.cpp b/src/core/geometry/qgsregularpolygon.cpp index 36bc799d8c1..fa4f5f5dc2e 100644 --- a/src/core/geometry/qgsregularpolygon.cpp +++ b/src/core/geometry/qgsregularpolygon.cpp @@ -113,7 +113,7 @@ QgsRegularPolygon::QgsRegularPolygon( const QgsPoint &pt1, const QgsPoint &pt2, double length = pt1.distance( pm ); double angle = ( 180 - ( 360 / numSides ) ) / 2.0; - double hypothenuse = length / cos( angle * M_PI / 180 ); + double hypothenuse = length / std::cos( angle * M_PI / 180 ); // TODO: inclination mCenter = pt1.project( hypothenuse, azimuth + angle ); @@ -327,7 +327,7 @@ double QgsRegularPolygon::length() const double QgsRegularPolygon::apothemToRadius( const double apothem, const unsigned int numSides ) const { - return apothem / cos( M_PI / numSides ); + return apothem / std::cos( M_PI / numSides ); } double QgsRegularPolygon::interiorAngle( const unsigned int nbSides ) const diff --git a/src/core/geometry/qgsregularpolygon.h b/src/core/geometry/qgsregularpolygon.h index 4b42c04ba49..a4e5759e538 100644 --- a/src/core/geometry/qgsregularpolygon.h +++ b/src/core/geometry/qgsregularpolygon.h @@ -104,7 +104,7 @@ class CORE_EXPORT QgsRegularPolygon * The apothem is the radius of the inscribed circle. * \see radius() */ - double apothem() const { return mRadius * cos( M_PI / mNumberSides ); } + double apothem() const { return mRadius * std::cos( M_PI / mNumberSides ); } /** Returns the number of sides of the regular polygon. * \see setNumberSides() diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 0cfa1528147..355ef504e2e 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -255,8 +255,8 @@ int FeaturePart::createCandidatesOverPoint( double x, double y, QList< LabelPosi { if ( !qgsDoubleNear( angle, 0.0 ) ) { - double xd = xdiff * cos( angle ) - ydiff * sin( angle ); - double yd = xdiff * sin( angle ) + ydiff * cos( angle ); + double xd = xdiff * std::cos( angle ) - ydiff * sin( angle ); + double yd = xdiff * sin( angle ) + ydiff * std::cos( angle ); xdiff = xd; ydiff = yd; } @@ -414,7 +414,7 @@ int FeaturePart::createCandidatesAtOrderedPositionsOverPoint( double x, double y } //have bearing, distance - calculate reference point - double referenceX = cos( alpha ) * distanceToLabel + x; + double referenceX = std::cos( alpha ) * distanceToLabel + x; double referenceY = sin( alpha ) * distanceToLabel + y; double labelX = referenceX + deltaX; @@ -498,7 +498,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo } else if ( angleToCandidate < a90 - gamma2 ) // top-right { - labelX += distanceToLabel * cos( angleToCandidate ); + labelX += distanceToLabel * std::cos( angleToCandidate ); labelY += distanceToLabel * sin( angleToCandidate ); quadrant = LabelPosition::QuadrantAboveRight; } @@ -511,7 +511,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo } else if ( angleToCandidate < a180 - gamma1 ) // top left { - labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth; + labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth; labelY += distanceToLabel * sin( angleToCandidate ); quadrant = LabelPosition::QuadrantAboveLeft; } @@ -524,7 +524,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo } else if ( angleToCandidate < a270 - gamma2 ) // down - left { - labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth; + labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth; labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight; quadrant = LabelPosition::QuadrantBelowLeft; } @@ -537,7 +537,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo } else if ( angleToCandidate < a360 ) // down - right { - labelX += distanceToLabel * cos( angleToCandidate ); + labelX += distanceToLabel * std::cos( angleToCandidate ); labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight; quadrant = LabelPosition::QuadrantBelowRight; } @@ -781,24 +781,24 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QListpermissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) { - lPos.append( new LabelPosition( i, candidateStartX - cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line + lPos.append( new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line placementCost += 0.001; } } if ( aboveLine ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) { - lPos.append( new LabelPosition( i, candidateStartX + cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line + lPos.append( new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line placementCost += 0.001; } } if ( flags & FLAG_ON_LINE ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle ) ) - lPos.append( new LabelPosition( i, candidateStartX - labelHeight * cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle ) ) + lPos.append( new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line } } else if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Horizontal ) @@ -925,18 +925,18 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList & if ( aboveLine ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) - positions.append( new LabelPosition( i, candidateStartX + cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) + positions.append( new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line } if ( belowLine ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) - positions.append( new LabelPosition( i, candidateStartX - cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) + positions.append( new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line } if ( flags & FLAG_ON_LINE ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle ) ) - positions.append( new LabelPosition( i, candidateStartX - labelHeight * cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle ) ) + positions.append( new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line } } else if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Horizontal ) @@ -1097,7 +1097,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d dist = -dist; flip = true; } - start_x += dist * cos( angle + M_PI_2 ); + start_x += dist * std::cos( angle + M_PI_2 ); start_y -= dist * sin( angle + M_PI_2 ); double render_angle = angle; @@ -1112,7 +1112,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d if ( orientation < 0 ) { // rotate in place - render_x += ci.width * cos( render_angle ); //- (string_height-2)*sin(render_angle); + render_x += ci.width * std::cos( render_angle ); //- (string_height-2)*sin(render_angle); render_y -= ci.width * sin( render_angle ); //+ (string_height-2)*cos(render_angle); render_angle += M_PI; } @@ -1140,7 +1140,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d static LabelPosition *_createCurvedCandidate( LabelPosition *lp, double angle, double dist ) { LabelPosition *newLp = new LabelPosition( *lp ); - newLp->offsetPosition( dist * cos( angle + M_PI / 2 ), dist * sin( angle + M_PI / 2 ) ); + newLp->offsetPosition( dist * std::cos( angle + M_PI / 2 ), dist * sin( angle + M_PI / 2 ) ); return newLp; } @@ -1230,7 +1230,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos } sin_avg += sin( tmp->getAlpha() ); - cos_avg += cos( tmp->getAlpha() ); + cos_avg += std::cos( tmp->getAlpha() ); angle_last = tmp->getAlpha(); tmp = tmp->getNextPart(); } @@ -1452,7 +1452,7 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin //alpha = box->alpha; // delta from label center and down-left corner - dlx = cos( beta ) * diago; + dlx = std::cos( beta ) * diago; dly = sin( beta ) * diago; double px0, py0; @@ -1468,7 +1468,7 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin for ( py = py0; py <= box->length; py += dy ) { - rx = cos( box->alpha ) * px + cos( box->alpha - M_PI / 2 ) * py; + rx = std::cos( box->alpha ) * px + std::cos( box->alpha - M_PI / 2 ) * py; ry = sin( box->alpha ) * px + sin( box->alpha - M_PI / 2 ) * py; rx += box->x[0]; diff --git a/src/core/pal/geomfunction.cpp b/src/core/pal/geomfunction.cpp index b61b5d0e3f0..216adbbe27c 100644 --- a/src/core/pal/geomfunction.cpp +++ b/src/core/pal/geomfunction.cpp @@ -330,9 +330,9 @@ bool GeomFunction::containsCandidate( const GEOSPreparedGeometry *geom, double x if ( !qgsDoubleNear( alpha, 0.0 ) ) { double beta = alpha + ( M_PI / 2 ); - double dx1 = cos( alpha ) * width; + double dx1 = std::cos( alpha ) * width; double dy1 = sin( alpha ) * width; - double dx2 = cos( beta ) * height; + double dx2 = std::cos( beta ) * height; double dy2 = sin( beta ) * height; GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + dx1 ); GEOSCoordSeq_setY_r( geosctxt, coord, 1, y + dy1 ); diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index 156d4bdc342..16c06773a12 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -78,10 +78,10 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double dx1, dx2, dy1, dy2; - dx1 = cos( this->alpha ) * w; + dx1 = std::cos( this->alpha ) * w; dy1 = sin( this->alpha ) * w; - dx2 = cos( beta ) * h; + dx2 = std::cos( beta ) * h; dy2 = sin( beta ) * h; x[0] = x1; diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index 8a0939462e5..54c1ab325e0 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -627,7 +627,7 @@ CHullBox *PointSet::compute_chull_bbox() for ( alpha_d = 0; alpha_d < 90; alpha_d++ ) { alpha = alpha_d * M_PI / 180.0; - d1 = cos( alpha ) * dref; + d1 = std::cos( alpha ) * dref; d2 = sin( alpha ) * dref; bb[0] = bbox[0]; @@ -670,7 +670,7 @@ CHullBox *PointSet::compute_chull_bbox() distNearestPoint = best_cp / dref; - d1 = cos( alpha_seg ) * distNearestPoint; + d1 = std::cos( alpha_seg ) * distNearestPoint; d2 = sin( alpha_seg ) * distNearestPoint; bb[i] += d1; // x diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 84581b342a0..edd328080c5 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -426,33 +426,33 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( { azimuth = azimuth - M_PI * 2.0; } - sigma1 = std::atan2( tan_u1, cos( azimuth ) ); - sin_alpha = cos( u1 ) * sin( azimuth ); + sigma1 = std::atan2( tan_u1, std::cos( azimuth ) ); + sin_alpha = std::cos( u1 ) * sin( azimuth ); alpha = asin( sin_alpha ); cos_alphasq = 1.0 - POW2( sin_alpha ); - u2 = POW2( cos( alpha ) ) * ( POW2( a ) - b2 ) / b2; // spheroid_mu2 + u2 = POW2( std::cos( alpha ) ) * ( POW2( a ) - b2 ) / b2; // spheroid_mu2 A = 1.0 + ( u2 / 16384.0 ) * ( 4096.0 + u2 * ( -768.0 + u2 * ( 320.0 - 175.0 * u2 ) ) ); B = ( u2 / 1024.0 ) * ( 256.0 + u2 * ( -128.0 + u2 * ( 74.0 - 47.0 * u2 ) ) ); sigma = ( distance / ( b * A ) ); do { two_sigma_m = 2.0 * sigma1 + sigma; - delta_sigma = B * sin( sigma ) * ( cos( two_sigma_m ) + ( B / 4.0 ) * ( cos( sigma ) * ( -1.0 + 2.0 * POW2( cos( two_sigma_m ) ) - ( B / 6.0 ) * cos( two_sigma_m ) * ( -3.0 + 4.0 * POW2( sin( sigma ) ) ) * ( -3.0 + 4.0 * POW2( cos( two_sigma_m ) ) ) ) ) ); + delta_sigma = B * sin( sigma ) * ( std::cos( two_sigma_m ) + ( B / 4.0 ) * ( std::cos( sigma ) * ( -1.0 + 2.0 * POW2( std::cos( two_sigma_m ) ) - ( B / 6.0 ) * std::cos( two_sigma_m ) * ( -3.0 + 4.0 * POW2( sin( sigma ) ) ) * ( -3.0 + 4.0 * POW2( std::cos( two_sigma_m ) ) ) ) ) ); last_sigma = sigma; sigma = ( distance / ( b * A ) ) + delta_sigma; i++; } while ( i < 999 && std::fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); - lat2 = std::atan2( ( sin( u1 ) * cos( sigma ) + cos( u1 ) * sin( sigma ) * - cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + - POW2( sin( u1 ) * sin( sigma ) - cos( u1 ) * cos( sigma ) * - cos( azimuth ) ) ) ) ); - lambda = std::atan2( ( sin( sigma ) * sin( azimuth ) ), ( cos( u1 ) * cos( sigma ) - - sin( u1 ) * sin( sigma ) * cos( azimuth ) ) ); + lat2 = std::atan2( ( sin( u1 ) * std::cos( sigma ) + std::cos( u1 ) * sin( sigma ) * + std::cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + + POW2( sin( u1 ) * sin( sigma ) - std::cos( u1 ) * std::cos( sigma ) * + std::cos( azimuth ) ) ) ) ); + lambda = std::atan2( ( sin( sigma ) * sin( azimuth ) ), ( std::cos( u1 ) * std::cos( sigma ) - + sin( u1 ) * sin( sigma ) * std::cos( azimuth ) ) ); C = ( f / 16.0 ) * cos_alphasq * ( 4.0 + f * ( 4.0 - 3.0 * cos_alphasq ) ); omega = lambda - ( 1.0 - C ) * f * sin_alpha * ( sigma + C * sin( sigma ) * - ( cos( two_sigma_m ) + C * cos( sigma ) * ( -1.0 + 2.0 * POW2( cos( two_sigma_m ) ) ) ) ); + ( std::cos( two_sigma_m ) + C * std::cos( sigma ) * ( -1.0 + 2.0 * POW2( std::cos( two_sigma_m ) ) ) ) ); lambda2 = radians_long + omega; return QgsPointXY( RAD2DEG( lambda2 ), RAD2DEG( lat2 ) ); } @@ -553,8 +553,8 @@ double QgsDistanceArea::computeDistanceBearing( double L = p2_lon - p1_lon; double U1 = atan( ( 1 - f ) * tan( p1_lat ) ); double U2 = atan( ( 1 - f ) * tan( p2_lat ) ); - double sinU1 = sin( U1 ), cosU1 = cos( U1 ); - double sinU2 = sin( U2 ), cosU2 = cos( U2 ); + double sinU1 = sin( U1 ), cosU1 = std::cos( U1 ); + double sinU2 = sin( U2 ), cosU2 = std::cos( U2 ); double lambda = L; double lambdaP = 2 * M_PI; @@ -574,14 +574,14 @@ double QgsDistanceArea::computeDistanceBearing( while ( std::fabs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 ) { sinLambda = sin( lambda ); - cosLambda = cos( lambda ); + cosLambda = std::cos( lambda ); tu1 = ( cosU2 * sinLambda ); tu2 = ( cosU1 * sinU2 - sinU1 * cosU2 * cosLambda ); sinSigma = sqrt( tu1 * tu1 + tu2 * tu2 ); cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = std::atan2( sinSigma, cosSigma ); alpha = asin( cosU1 * cosU2 * sinLambda / sinSigma ); - cosSqAlpha = cos( alpha ) * cos( alpha ); + cosSqAlpha = std::cos( alpha ) * std::cos( alpha ); cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; C = f / 16 * cosSqAlpha * ( 4 + f * ( 4 - 3 * cosSqAlpha ) ); lambdaP = lambda; @@ -632,7 +632,7 @@ double QgsDistanceArea::getQbar( double x ) const { double cosx, cosx2; - cosx = cos( x ); + cosx = std::cos( x ); cosx2 = cosx * cosx; return cosx * ( m_QbarA + cosx2 * ( m_QbarB + cosx2 * ( m_QbarC + cosx2 * m_QbarD ) ) ); diff --git a/src/core/qgsmapsettingsutils.cpp b/src/core/qgsmapsettingsutils.cpp index 277ed5da144..7012b3fd643 100644 --- a/src/core/qgsmapsettingsutils.cpp +++ b/src/core/qgsmapsettingsutils.cpp @@ -92,12 +92,12 @@ QString QgsMapSettingsUtils::worldFileContent( const QgsMapSettings &mapSettings // rotation matrix double r[6]; - r[0] = cos( alpha ); + r[0] = std::cos( alpha ); r[1] = -sin( alpha ); - r[2] = xCenter * ( 1 - cos( alpha ) ) + yCenter * sin( alpha ); + r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * sin( alpha ); r[3] = sin( alpha ); - r[4] = cos( alpha ); - r[5] = - xCenter * sin( alpha ) + yCenter * ( 1 - cos( alpha ) ); + r[4] = std::cos( alpha ); + r[5] = - xCenter * sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) ); // result = rotation x scaling = rotation(scaling(X)) double a = r[0] * s[0] + r[1] * s[3]; diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index c90695ecfc6..afc5e0ee2a4 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -1782,8 +1782,8 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont if ( dataDefinedRotation ) { //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center - double xd = xdiff * cos( angle ) - ydiff * sin( angle ); - double yd = xdiff * sin( angle ) + ydiff * cos( angle ); + double xd = xdiff * std::cos( angle ) - ydiff * sin( angle ); + double yd = xdiff * sin( angle ) + ydiff * std::cos( angle ); xdiff = xd; ydiff = yd; } diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index e93e7e4b5ff..47510f5a7a7 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -292,7 +292,7 @@ QgsPointXY QgsPointXY::project( double distance, double bearing ) const { double rads = bearing * M_PI / 180.0; double dx = distance * sin( rads ); - double dy = distance * cos( rads ); + double dy = distance * std::cos( rads ); return QgsPointXY( mX + dx, mY + dy ); } diff --git a/src/core/qgsscalecalculator.cpp b/src/core/qgsscalecalculator.cpp index 34983358acc..2bf8b3bd508 100644 --- a/src/core/qgsscalecalculator.cpp +++ b/src/core/qgsscalecalculator.cpp @@ -116,7 +116,7 @@ double QgsScaleCalculator::calculateGeographicDistance( const QgsRectangle &mapE // For a longitude change of 180 degrees double lat = ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5; static const double RADS = ( 4.0 * atan( 1.0 ) ) / 180.0; - double a = std::pow( cos( lat * RADS ), 2 ); + double a = std::pow( std::cos( lat * RADS ), 2 ); double c = 2.0 * std::atan2( sqrt( a ), sqrt( 1.0 - a ) ); static const double RA = 6378000; // [m] // The eccentricity. This comes from sqrt(1.0 - rb*rb/(ra*ra)) with rb set diff --git a/src/core/qgstextrenderer.cpp b/src/core/qgstextrenderer.cpp index c594cb1697f..48e65fbba5c 100644 --- a/src/core/qgstextrenderer.cpp +++ b/src/core/qgstextrenderer.cpp @@ -1745,8 +1745,8 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, HAlignment double yc = rect.height() / 2.0; double angle = -rotation; - double xd = xc * cos( angle ) - yc * sin( angle ); - double yd = xc * sin( angle ) + yc * cos( angle ); + double xd = xc * std::cos( angle ) - yc * sin( angle ); + double yd = xc * sin( angle ) + yc * std::cos( angle ); component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd ); } @@ -2388,7 +2388,7 @@ void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRender angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 ); } - QPointF transPt( -offsetDist * cos( angleRad + M_PI / 2 ), + QPointF transPt( -offsetDist * std::cos( angleRad + M_PI / 2 ), -offsetDist * sin( angleRad + M_PI / 2 ) ); p->save(); diff --git a/src/core/qgsvector.cpp b/src/core/qgsvector.cpp index 579c093901e..b70ac386851 100644 --- a/src/core/qgsvector.cpp +++ b/src/core/qgsvector.cpp @@ -109,7 +109,7 @@ QgsVector QgsVector::rotateBy( double rot ) const { double angle = std::atan2( mY, mX ) + rot; double len = length(); - return QgsVector( len * cos( angle ), len * sin( angle ) ); + return QgsVector( len * std::cos( angle ), len * sin( angle ) ); } QgsVector QgsVector::normalized() const diff --git a/src/core/qgsvectorlayerlabelprovider.cpp b/src/core/qgsvectorlayerlabelprovider.cpp index 1d6dd414135..0067d0ba80e 100644 --- a/src/core/qgsvectorlayerlabelprovider.cpp +++ b/src/core/qgsvectorlayerlabelprovider.cpp @@ -509,8 +509,8 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q double yc = outPt2.y() - outPt.y(); double angle = -component.rotation; - double xd = xc * cos( angle ) - yc * sin( angle ); - double yd = xc * sin( angle ) + yc * cos( angle ); + double xd = xc * std::cos( angle ) - yc * sin( angle ); + double yd = xc * sin( angle ) + yc * std::cos( angle ); centerPt.setX( centerPt.x() + xd ); centerPt.setY( centerPt.y() + yd ); diff --git a/src/core/raster/qgshillshaderenderer.cpp b/src/core/raster/qgshillshaderenderer.cpp index 5f1a6e02d91..668d040877d 100644 --- a/src/core/raster/qgshillshaderenderer.cpp +++ b/src/core/raster/qgshillshaderenderer.cpp @@ -126,7 +126,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext double cellYSize = extent.height() / double( height ); double zenithRad = qMax( 0.0, 90 - mLightAngle ) * M_PI / 180.0; double azimuthRad = -1 * mLightAzimuth * M_PI / 180.0; - double cosZenithRad = cos( zenithRad ); + double cosZenithRad = std::cos( zenithRad ); double sinZenithRad = sin( zenithRad ); // Multi direction hillshade: http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf @@ -218,9 +218,9 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext if ( !mMultiDirectional ) { // Standard single direction hillshade - grayValue = qBound( 0.0, 255.0 * ( cosZenithRad * cos( slopeRad ) + grayValue = qBound( 0.0, 255.0 * ( cosZenithRad * std::cos( slopeRad ) + sinZenithRad * sin( slopeRad ) - * cos( azimuthRad - aspectRad ) ), 255.0 ); + * std::cos( azimuthRad - aspectRad ) ), 255.0 ); } else { @@ -234,12 +234,12 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext weight2 = weight2 * weight2; weight3 = weight3 * weight3; - double cosSlope = cosZenithRad * cos( slopeRad ); + double cosSlope = cosZenithRad * std::cos( slopeRad ); double sinSlope = sinZenithRad * sin( slopeRad ); - double color0 = cosSlope + sinSlope * cos( angle0Rad - aspectRad ) ; - double color1 = cosSlope + sinSlope * cos( angle1Rad - aspectRad ) ; - double color2 = cosSlope + sinSlope * cos( angle2Rad - aspectRad ) ; - double color3 = cosSlope + sinSlope * cos( angle3Rad - aspectRad ) ; + double color0 = cosSlope + sinSlope * std::cos( angle0Rad - aspectRad ) ; + double color1 = cosSlope + sinSlope * std::cos( angle1Rad - aspectRad ) ; + double color2 = cosSlope + sinSlope * std::cos( angle2Rad - aspectRad ) ; + double color3 = cosSlope + sinSlope * std::cos( angle3Rad - aspectRad ) ; grayValue = qBound( 0.0, 255 * ( weight0 * color0 + weight1 * color1 + weight2 * color2 + weight3 * color3 ) * 0.5, 255.0 ); } diff --git a/src/core/symbology/qgs25drenderer.cpp b/src/core/symbology/qgs25drenderer.cpp index 726b72119fb..808f7a11f01 100644 --- a/src/core/symbology/qgs25drenderer.cpp +++ b/src/core/symbology/qgs25drenderer.cpp @@ -24,7 +24,7 @@ #define ROOF_EXPRESSION \ "translate(" \ " $geometry," \ - " cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ + " std::cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ " sin( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )" \ ")" @@ -32,10 +32,10 @@ "order_parts( "\ " extrude(" \ " segments_to_lines( $geometry )," \ - " cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ + " std::cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ " sin( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )" \ " )," \ - " 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 ) ), 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) ) ))'," \ + " 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * std::cos( radians( @qgis_25d_angle + 180 ) ), 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) ) ))'," \ " False" \ ")" @@ -44,7 +44,7 @@ " $geometry," \ " translate(" \ " @map_extent_center," \ - " 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 ) )," \ + " 1000 * @map_extent_width * std::cos( radians( @qgis_25d_angle + 180 ) )," \ " 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) )" \ " )" \ ")" diff --git a/src/core/symbology/qgsarrowsymbollayer.cpp b/src/core/symbology/qgsarrowsymbollayer.cpp index 1133c8ee3fc..7a30aaa11bc 100644 --- a/src/core/symbology/qgsarrowsymbollayer.cpp +++ b/src/core/symbology/qgsarrowsymbollayer.cpp @@ -387,7 +387,7 @@ bool pointsToCircle( QPointF a, QPointF b, QPointF c, QPointF ¢er, qreal &ra QPointF circlePoint( QPointF center, qreal radius, qreal angle ) { // Y is oriented downward - return QPointF( cos( -angle ) * radius + center.x(), sin( -angle ) * radius + center.y() ); + return QPointF( std::cos( -angle ) * radius + center.x(), sin( -angle ) * radius + center.y() ); } void pathArcTo( QPainterPath &path, QPointF circleCenter, qreal circleRadius, qreal angle_o, qreal angle_d, int direction ) diff --git a/src/core/symbology/qgsellipsesymbollayer.cpp b/src/core/symbology/qgsellipsesymbollayer.cpp index 4f6abf731c8..426fec09d1e 100644 --- a/src/core/symbology/qgsellipsesymbollayer.cpp +++ b/src/core/symbology/qgsellipsesymbollayer.cpp @@ -805,7 +805,7 @@ bool QgsEllipseSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFact for ( int i = 0; i < 39; ++i ) { double angle = stepsize * i; - double x = halfWidth * cos( angle ); + double x = halfWidth * std::cos( angle ); double y = halfHeight * sin( angle ); line << QgsPoint( t.map( QPointF( x, y ) ) ); } diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index 3f25d4dfcb0..c0bfd5ac851 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -2590,7 +2590,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & } else { - height = outputPixelDist / cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant + height = outputPixelDist / std::cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant width = outputPixelDist / sin( lineAngle * M_PI / 180 ); // recalculate real angle and distance after rounding to pixels @@ -2603,12 +2603,12 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & height = std::abs( height ); width = std::abs( width ); - outputPixelDist = height * cos( lineAngle * M_PI / 180 ); + outputPixelDist = height * std::cos( lineAngle * M_PI / 180 ); // Round offset to correspond to one pixel height, otherwise lines may // be shifted on tile border if offset falls close to pixel center - int offsetHeight = std::round( std::fabs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) ); - outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 ); + int offsetHeight = std::round( std::fabs( outputPixelOffset / std::cos( lineAngle * M_PI / 180 ) ) ); + outputPixelOffset = offsetHeight * std::cos( lineAngle * M_PI / 180 ); } //depending on the angle, we might need to render into a larger image and use a subset of it @@ -2656,7 +2656,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & } else if ( lineAngle > 0 && lineAngle < 90 ) { - dx = outputPixelDist * cos( ( 90 - lineAngle ) * M_PI / 180.0 ); + dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 ); dy = outputPixelDist * sin( ( 90 - lineAngle ) * M_PI / 180.0 ); p1 = QPointF( 0, height ); p2 = QPointF( width, 0 ); @@ -2667,7 +2667,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & } else if ( lineAngle > 180 && lineAngle < 270 ) { - dx = outputPixelDist * cos( ( 90 - lineAngle ) * M_PI / 180.0 ); + dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 ); dy = outputPixelDist * sin( ( 90 - lineAngle ) * M_PI / 180.0 ); p1 = QPointF( width, 0 ); p2 = QPointF( 0, height ); @@ -2678,7 +2678,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & } else if ( lineAngle > 90 && lineAngle < 180 ) { - dy = outputPixelDist * cos( ( 180 - lineAngle ) * M_PI / 180 ); + dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 ); dx = outputPixelDist * sin( ( 180 - lineAngle ) * M_PI / 180 ); p1 = QPointF( 0, 0 ); p2 = QPointF( width, height ); @@ -2689,7 +2689,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & } else if ( lineAngle > 270 && lineAngle < 360 ) { - dy = outputPixelDist * cos( ( 180 - lineAngle ) * M_PI / 180 ); + dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 ); dx = outputPixelDist * sin( ( 180 - lineAngle ) * M_PI / 180 ); p1 = QPointF( width, height ); p2 = QPointF( 0, 0 ); @@ -2880,7 +2880,7 @@ void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &eleme QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc ); // - QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset ); + QPointF lineOffset( sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset ); lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props ); QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset ); } diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 3b7ac43c2bd..220d44ab5f1 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -686,7 +686,7 @@ class MyLine return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) ); double alpha = atan( mT ); - double dx = cos( alpha ) * interval; + double dx = std::cos( alpha ) * interval; double dy = sin( alpha ) * interval; return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) ); } @@ -1002,7 +1002,7 @@ static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt ) // calc average angle between the previous and next point double a1 = MyLine( prevPt, pt ).angle(); double a2 = MyLine( pt, nextPt ).angle(); - double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 ); + double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = sin( a1 ) + sin( a2 ); return std::atan2( unitY, unitX ); } diff --git a/src/core/symbology/qgsmarkersymbollayer.cpp b/src/core/symbology/qgsmarkersymbollayer.cpp index 8603a863491..26646cc00ee 100644 --- a/src/core/symbology/qgsmarkersymbollayer.cpp +++ b/src/core/symbology/qgsmarkersymbollayer.cpp @@ -419,10 +419,10 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB case Pentagon: /* angular-representation of hardcoded values used - polygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) ) - << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) ) - << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) ) - << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) ) + polygon << QPointF( sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) ) + << QPointF( sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) + << QPointF( sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) + << QPointF( sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) << QPointF( 0, -1 ); */ polygon << QPointF( -0.9511, -0.3090 ) << QPointF( -0.5878, 0.8090 ) @@ -434,11 +434,11 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB case Hexagon: /* angular-representation of hardcoded values used - polygon << QPointF( sin( DEG2RAD( 300.0 ) ), - cos( DEG2RAD( 300.0 ) ) ) - << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) ) - << QPointF( sin( DEG2RAD( 180.0 ) ), - cos( DEG2RAD( 180.0 ) ) ) - << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) ) - << QPointF( sin( DEG2RAD( 60.0 ) ), - cos( DEG2RAD( 60.0 ) ) ) + polygon << QPointF( sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) ) + << QPointF( sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) ) + << QPointF( sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) ) + << QPointF( sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) ) + << QPointF( sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) ) << QPointF( 0, -1 ); */ polygon << QPointF( -0.8660, -0.5 ) << QPointF( -0.8660, 0.5 ) @@ -455,8 +455,8 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB case EquilateralTriangle: /* angular-representation of hardcoded values used - polygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) ) - << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) ) + polygon << QPointF( sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) ) + << QPointF( sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) ) << QPointF( 0, -1 ); */ polygon << QPointF( -0.8660, 0.5 ) << QPointF( 0.8660, 0.5 ) @@ -474,19 +474,19 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB case Star: { - double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) ); + double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) ); - polygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324 - << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288 ) ) ) // 288 - << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252 - << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) ) // 216 + polygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324 + << QPointF( sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288 + << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252 + << QPointF( sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216 << QPointF( 0, inner_r ) // 180 - << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) ) // 144 - << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108 - << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) ) // 72 - << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36 + << QPointF( sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144 + << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108 + << QPointF( sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72 + << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36 << QPointF( 0, -1 ) - << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ); // 324; // 0 + << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0 return true; } diff --git a/src/core/symbology/qgspointdisplacementrenderer.cpp b/src/core/symbology/qgspointdisplacementrenderer.cpp index fa3d3e237f0..8de7d1a8581 100644 --- a/src/core/symbology/qgspointdisplacementrenderer.cpp +++ b/src/core/symbology/qgspointdisplacementrenderer.cpp @@ -258,7 +258,7 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRe for ( double currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep ) { double sinusCurrentAngle = sin( currentAngle ); - double cosinusCurrentAngle = cos( currentAngle ); + double cosinusCurrentAngle = std::cos( currentAngle ); QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle ); QPointF labelShift( ( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle ); symbolPositions.append( centerPoint + positionShift ); @@ -287,7 +287,7 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRe for ( int i = 0; i < actualPointsCurrentRing; ++i ) { double sinusCurrentAngle = sin( currentAngle ); - double cosinusCurrentAngle = cos( currentAngle ); + double cosinusCurrentAngle = std::cos( currentAngle ); QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle ); QPointF labelShift( ( radiusCurrentRing + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radiusCurrentRing + symbolDiagonal / 2 ) * cosinusCurrentAngle ); symbolPositions.append( centerPoint + positionShift ); diff --git a/src/core/symbology/qgssymbollayer.cpp b/src/core/symbology/qgssymbollayer.cpp index d474c39c278..43725603565 100644 --- a/src/core/symbology/qgssymbollayer.cpp +++ b/src/core/symbology/qgssymbollayer.cpp @@ -508,7 +508,7 @@ void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double QPointF QgsMarkerSymbolLayer::_rotatedOffset( QPointF offset, double angle ) { angle = DEG2RAD( angle ); - double c = cos( angle ), s = sin( angle ); + double c = std::cos( angle ), s = sin( angle ); return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c ); } diff --git a/src/core/symbology/qgsvectorfieldsymbollayer.cpp b/src/core/symbology/qgsvectorfieldsymbollayer.cpp index d8858391a68..17604a74bbb 100644 --- a/src/core/symbology/qgsvectorfieldsymbollayer.cpp +++ b/src/core/symbology/qgsvectorfieldsymbollayer.cpp @@ -314,7 +314,7 @@ void QgsVectorFieldSymbolLayer::convertPolarToCartesian( double length, double a } x = length * sin( angle ); - y = length * cos( angle ); + y = length * std::cos( angle ); } void QgsVectorFieldSymbolLayer::setColor( const QColor &color ) diff --git a/src/gui/qgsadvanceddigitizingcanvasitem.cpp b/src/gui/qgsadvanceddigitizingcanvasitem.cpp index c995dccbd67..9aa3b76edcf 100644 --- a/src/gui/qgsadvanceddigitizingcanvasitem.cpp +++ b/src/gui/qgsadvanceddigitizingcanvasitem.cpp @@ -144,16 +144,16 @@ void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter ) ( int )16 * ( a0 - a ) * 180 / M_PI ); painter->drawLine( prevPointPix.x(), prevPointPix.y(), - prevPointPix.x() + 60 * qCos( a0 ), + prevPointPix.x() + 60 * std::cos( a0 ), prevPointPix.y() + 60 * qSin( a0 ) ); if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() ) { painter->setPen( mLockedPen ); double d = std::max( boundingRect().width(), boundingRect().height() ); - painter->drawLine( prevPointPix.x() - d * qCos( a ), + painter->drawLine( prevPointPix.x() - d * std::cos( a ), prevPointPix.y() - d * qSin( a ), - prevPointPix.x() + d * qCos( a ), + prevPointPix.x() + d * std::cos( a ), prevPointPix.y() + d * qSin( a ) ); } } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 594f92762a6..1930074d6cd 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -662,7 +662,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) softAngle = quo * commonAngle ; // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1 - const double dist = std::fabs( qCos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) + const double dist = std::fabs( std::cos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) - qSin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) ); if ( dist / mMapCanvas->mapSettings().mapUnitsPerPixel() < SOFT_CONSTRAINT_TOLERANCE_PIXEL ) { @@ -681,7 +681,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) previousPt.x() - penultimatePt.x() ); } - double cosa = qCos( angleValue ); + double cosa = std::cos( angleValue ); double sina = qSin( angleValue ); double v = ( point.x() - previousPt.x() ) * cosa + ( point.y() - previousPt.y() ) * sina ; if ( mXConstraint->isLocked() && mYConstraint->isLocked() ) diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index 5859b3f8a9d..99a1491dc97 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -455,7 +455,7 @@ void QgsColorWheel::paintEvent( QPaintEvent *event ) //adapted from equations at https://github.com/timjb/colortriangle/blob/master/colortriangle.js by Tim Baumann double lightness = mCurrentColor.lightnessF(); double hueRadians = ( h * M_PI / 180.0 ); - double hx = cos( hueRadians ) * triangleRadius; + double hx = std::cos( hueRadians ) * triangleRadius; double hy = -sin( hueRadians ) * triangleRadius; double sx = -cos( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; double sy = -sin( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; @@ -565,7 +565,7 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) double triangleSideLength = sqrt( 3.0 ) * triangleLength; double newL = ( ( -sin( rad0 ) * r ) / triangleSideLength ) + 0.5; double widthShare = 1.0 - ( std::fabs( newL - 0.5 ) * 2.0 ); - double newS = ( ( ( cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare; + double newS = ( ( ( std::cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare; s = qMin( static_cast< int >( std::round( qMax( 0.0, newS ) * 255.0 ) ), 255 ); l = qMin( static_cast< int >( std::round( qMax( 0.0, newL ) * 255.0 ) ), 255 ); newColor = QColor::fromHsl( h, s, l ); @@ -683,10 +683,10 @@ void QgsColorWheel::createTriangle() alphaColor.setAlpha( 0 ); //some rather ugly shortcuts to obtain corners and midpoints of triangle - QLineF line1 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() - triangleRadius * sin( M_PI / 3.0 ) ); + QLineF line1 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() - triangleRadius * sin( M_PI / 3.0 ) ); QLineF line2 = QLineF( center.x(), center.y(), center.x() + triangleRadius, center.y() ); - QLineF line3 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() + triangleRadius * sin( M_PI / 3.0 ) ); - QLineF line4 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() ); + QLineF line3 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() + triangleRadius * sin( M_PI / 3.0 ) ); + QLineF line4 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() ); QLineF line5 = QLineF( center.x(), center.y(), ( line2.p2().x() + line1.p2().x() ) / 2.0, ( line2.p2().y() + line1.p2().y() ) / 2.0 ); line1.setAngle( line1.angle() + angle ); line2.setAngle( line2.angle() + angle ); diff --git a/src/plugins/georeferencer/qgsgeorefplugingui.cpp b/src/plugins/georeferencer/qgsgeorefplugingui.cpp index c5fe6f802e0..bfa5cf6da63 100644 --- a/src/plugins/georeferencer/qgsgeorefplugingui.cpp +++ b/src/plugins/georeferencer/qgsgeorefplugingui.cpp @@ -1438,8 +1438,8 @@ bool QgsGeorefPluginGui::writeWorldFile( const QgsPointXY &origin, double pixelX { rotationX = pixelXSize * sin( rotation ); rotationY = pixelYSize * sin( rotation ); - pixelXSize *= cos( rotation ); - pixelYSize *= cos( rotation ); + pixelXSize *= std::cos( rotation ); + pixelYSize *= std::cos( rotation ); } QTextStream stream( &file ); diff --git a/src/plugins/georeferencer/qgsgeoreftransform.cpp b/src/plugins/georeferencer/qgsgeoreftransform.cpp index f44b9ba2859..335a1af0872 100644 --- a/src/plugins/georeferencer/qgsgeoreftransform.cpp +++ b/src/plugins/georeferencer/qgsgeoreftransform.cpp @@ -440,7 +440,7 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs if ( !t ) return false; - double a = cos( t->angle ), b = sin( t->angle ), x0 = t->origin.x(), y0 = t->origin.y(), s = t->scale; + double a = std::cos( t->angle ), b = sin( t->angle ), x0 = t->origin.x(), y0 = t->origin.y(), s = t->scale; if ( !bDstToSrc ) { a *= s; @@ -450,8 +450,8 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs double xT = x[i], yT = y[i]; // Because rotation parameters where estimated in a CS with negative y-axis ^= down. // we need to apply the rotation matrix and a change of base: - // |cos a,-sin a| |1, 0| | cos a, sin a| - // |sin a, cos a| |0,-1| = | sin a, -cos a| + // |cos a,-sin a| |1, 0| | std::cos a, sin a| + // |sin a, std::cos a| |0,-1| = | sin a, -cos a| x[i] = x0 + ( a * xT + b * yT ); y[i] = y0 + ( b * xT - a * yT ); panSuccess[i] = true; @@ -475,7 +475,7 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs double xT = x[i], yT = y[i]; xT -= x0; yT -= y0; - // | cos a, sin a |^-1 |cos a, sin a| + // | std::cos a, sin a |^-1 |cos a, sin a| // | sin a, -cos a | = |sin a, -cos a| x[i] = a * xT + b * yT; y[i] = b * xT - a * yT; diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 061966b90da..92ab26ce4bb 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -4177,7 +4177,7 @@ void TestQgsGeometry::circle() // byCenterPoint QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 5, 0 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 90 ) ); QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) ); - QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 5 * cos( 45 * M_PI / 180.0 ), 5 * sin( 45 * M_PI / 180.0 ) ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 45 ) ); + QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 5 * std::cos( 45 * M_PI / 180.0 ), 5 * sin( 45 * M_PI / 180.0 ) ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 45 ) ); // by3Tangents // Tangents from triangle tri1( 0,0 ; 0,5 ), tri2( 0,0 ; 5,0 ), tri3( 5,0 ; 0,5 ) QgsCircle circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 2, 0 ), QgsPoint( 3, 0 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) ); From 8c64d80a07af4000e786fb0d4c4721c90e838911 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:12:36 +1000 Subject: [PATCH 121/364] (q)floor -> std::floor --- src/analysis/raster/qgsalignraster.cpp | 2 +- src/analysis/raster/qgsrastermatrix.cpp | 2 +- src/analysis/vector/qgsgeometrysnapper.cpp | 32 +++++++++---------- src/app/qgsdecorationgrid.cpp | 6 ++-- src/app/qgsdecorationscalebar.cpp | 2 +- src/app/qgspluginregistry.h | 2 +- src/core/composer/qgscomposermultiframe.cpp | 2 +- src/core/composer/qgscomposerscalebar.cpp | 8 ++--- src/core/composer/qgscomposertablev2.cpp | 2 +- src/core/composer/qgscomposition.cpp | 2 +- src/core/expression/qgsexpressionfunction.cpp | 2 +- src/core/expression/qgsexpressionnodeimpl.cpp | 2 +- .../geometry/qgsinternalgeometryengine.cpp | 2 +- src/core/qgsfield.cpp | 2 +- src/core/raster/qgscubicrasterresampler.cpp | 4 +-- src/core/raster/qgsrasterblock.cpp | 2 +- src/core/raster/qgsrasterdataprovider.cpp | 12 +++---- src/core/raster/qgsrasterinterface.cpp | 4 +-- src/core/raster/qgsrasterlayerrenderer.cpp | 6 ++-- src/core/raster/qgsrasterprojector.cpp | 16 +++++----- .../symbology/qgsgraduatedsymbolrenderer.cpp | 2 +- .../qgspointdisplacementrenderer.cpp | 2 +- src/core/symbology/qgssymbollayerutils.cpp | 6 ++-- src/gui/layout/qgslayoutruler.cpp | 2 +- src/gui/qgscolorwidgets.cpp | 2 +- src/gui/qgscomposerruler.cpp | 2 +- src/providers/arcgisrest/qgsamsprovider.cpp | 4 +-- src/providers/gdal/qgsgdalprovider.cpp | 20 ++++++------ src/providers/wcs/qgswcsprovider.cpp | 4 +-- src/providers/wms/qgswmscapabilities.cpp | 8 ++--- src/providers/wms/qgswmsprovider.cpp | 10 +++--- 31 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 401fffb0a51..97a6eec3457 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -41,7 +41,7 @@ static double floor_with_tolerance( double value ) if ( std::fabs( value - std::round( value ) ) < 1e-6 ) return std::round( value ); else - return qFloor( value ); + return floor( value ); } static double fmod_with_tolerance( double num, double denom ) diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index b77b6ddfb94..0aa98e6ecbf 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -415,7 +415,7 @@ bool QgsRasterMatrix::twoArgumentOperation( TwoArgOperator op, const QgsRasterMa bool QgsRasterMatrix::testPowerValidity( double base, double power ) const { - if ( ( base == 0 && power < 0 ) || ( base < 0 && ( power - floor( power ) ) > 0 ) ) + if ( ( base == 0 && power < 0 ) || ( base < 0 && ( power - std::floor( power ) ) > 0 ) ) { return false; } diff --git a/src/analysis/vector/qgsgeometrysnapper.cpp b/src/analysis/vector/qgsgeometrysnapper.cpp index e2fb6dbb928..11fde6e8fb8 100644 --- a/src/analysis/vector/qgsgeometrysnapper.cpp +++ b/src/analysis/vector/qgsgeometrysnapper.cpp @@ -112,8 +112,8 @@ class Raytracer Raytracer( float x0, float y0, float x1, float y1 ) : m_dx( std::fabs( x1 - x0 ) ) , m_dy( std::fabs( y1 - y0 ) ) - , m_x( qFloor( x0 ) ) - , m_y( qFloor( y0 ) ) + , m_x( std::floor( x0 ) ) + , m_y( std::floor( y0 ) ) , m_n( 1 ) { if ( m_dx == 0. ) @@ -124,14 +124,14 @@ class Raytracer else if ( x1 > x0 ) { m_xInc = 1; - m_n += int( qFloor( x1 ) ) - m_x; - m_error = ( qFloor( x0 ) + 1 - x0 ) * m_dy; + m_n += int( std::floor( x1 ) ) - m_x; + m_error = ( std::floor( x0 ) + 1 - x0 ) * m_dy; } else { m_xInc = -1; - m_n += m_x - int( qFloor( x1 ) ); - m_error = ( x0 - qFloor( x0 ) ) * m_dy; + m_n += m_x - int( std::floor( x1 ) ); + m_error = ( x0 - std::floor( x0 ) ) * m_dy; } if ( m_dy == 0. ) { @@ -141,14 +141,14 @@ class Raytracer else if ( y1 > y0 ) { m_yInc = 1; - m_n += int( qFloor( y1 ) ) - m_y; - m_error -= ( qFloor( y0 ) + 1 - y0 ) * m_dx; + m_n += int( std::floor( y1 ) ) - m_y; + m_error -= ( std::floor( y0 ) + 1 - y0 ) * m_dx; } else { m_yInc = -1; - m_n += m_y - int( qFloor( y1 ) ); - m_error -= ( y0 - qFloor( y0 ) ) * m_dx; + m_n += m_y - int( std::floor( y1 ) ); + m_error -= ( y0 - std::floor( y0 ) ) * m_dx; } } int curCol() const { return m_x; } @@ -302,8 +302,8 @@ QgsSnapIndex::Cell &QgsSnapIndex::getCreateCell( int col, int row ) void QgsSnapIndex::addPoint( const CoordIdx *idx, bool isEndPoint ) { QgsPoint p = idx->point(); - int col = qFloor( ( p.x() - mOrigin.x() ) / mCellSize ); - int row = qFloor( ( p.y() - mOrigin.y() ) / mCellSize ); + int col = std::floor( ( p.x() - mOrigin.x() ) / mCellSize ); + int row = std::floor( ( p.y() - mOrigin.y() ) / mCellSize ); getCreateCell( col, row ).append( new PointSnapItem( idx, isEndPoint ) ); } @@ -400,10 +400,10 @@ QgsPoint QgsSnapIndex::getClosestSnapToPoint( const QgsPoint &p, const QgsPoint QgsSnapIndex::SnapItem *QgsSnapIndex::getSnapItem( const QgsPoint &pos, double tol, QgsSnapIndex::PointSnapItem **pSnapPoint, QgsSnapIndex::SegmentSnapItem **pSnapSegment, bool endPointOnly ) const { - int colStart = qFloor( ( pos.x() - tol - mOrigin.x() ) / mCellSize ); - int rowStart = qFloor( ( pos.y() - tol - mOrigin.y() ) / mCellSize ); - int colEnd = qFloor( ( pos.x() + tol - mOrigin.x() ) / mCellSize ); - int rowEnd = qFloor( ( pos.y() + tol - mOrigin.y() ) / mCellSize ); + int colStart = std::floor( ( pos.x() - tol - mOrigin.x() ) / mCellSize ); + int rowStart = std::floor( ( pos.y() - tol - mOrigin.y() ) / mCellSize ); + int colEnd = std::floor( ( pos.x() + tol - mOrigin.x() ) / mCellSize ); + int rowEnd = std::floor( ( pos.y() + tol - mOrigin.y() ) / mCellSize ); rowStart = qMax( rowStart, mRowsStartIdx ); rowEnd = qMin( rowEnd, mRowsStartIdx + mGridRows.size() - 1 ); diff --git a/src/app/qgsdecorationgrid.cpp b/src/app/qgsdecorationgrid.cpp index 512a55acbcd..868cbbd5164 100644 --- a/src/app/qgsdecorationgrid.cpp +++ b/src/app/qgsdecorationgrid.cpp @@ -787,7 +787,7 @@ bool QgsDecorationGrid::getIntervalFromExtent( double *values, bool useXAxis ) if ( !qgsDoubleNear( interval, 0.0 ) ) { double interval2 = 0; - int factor = std::pow( 10, floor( log10( interval ) ) ); + int factor = std::pow( 10, std::floor( log10( interval ) ) ); if ( factor != 0 ) { interval2 = std::round( interval / factor ) * factor; @@ -842,9 +842,9 @@ bool QgsDecorationGrid::getIntervalFromCurrentLayer( double *values ) // calculate offset - when using very high resolution rasters in geographic CRS // there seems to be a small shift, but this may be due to rendering issues and depends on zoom double ratio = extent.xMinimum() / values[0]; - values[2] = ( ratio - floor( ratio ) ) * values[0]; + values[2] = ( ratio - std::floor( ratio ) ) * values[0]; ratio = extent.yMinimum() / values[1]; - values[3] = ( ratio - floor( ratio ) ) * values[1]; + values[3] = ( ratio - std::floor( ratio ) ) * values[1]; QgsDebugMsg( QString( "xmax: %1 xmin: %2 width: %3 xInterval: %4 xOffset: %5" ).arg( extent.xMaximum() ).arg( extent.xMinimum() ).arg( rlayer->width() ).arg( values[0] ).arg( values[2] ) ); diff --git a/src/app/qgsdecorationscalebar.cpp b/src/app/qgsdecorationscalebar.cpp index 2f717cb8cb7..a98773265b9 100644 --- a/src/app/qgsdecorationscalebar.cpp +++ b/src/app/qgsdecorationscalebar.cpp @@ -158,7 +158,7 @@ void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRender // Work out the exponent for the number - e.g, 1234 will give 3, // and .001234 will give -3 - double myPowerOf10 = floor( log10( myActualSize ) ); + double myPowerOf10 = std::floor( log10( myActualSize ) ); // snap to integer < 10 times power of 10 if ( mSnapping ) diff --git a/src/app/qgspluginregistry.h b/src/app/qgspluginregistry.h index d855f03c678..8694dea53f4 100644 --- a/src/app/qgspluginregistry.h +++ b/src/app/qgspluginregistry.h @@ -104,7 +104,7 @@ class APP_EXPORT QgsPluginRegistry bool checkPythonPlugin( const QString &packageName ); //! Check current QGIS version against requested minimal and optionally maximal QGIS version - //! if maxVersion not specified, the default value is assumed: floor(minVersion) + 0.99.99 + //! if maxVersion not specified, the default value is assumed: std::floor(minVersion) + 0.99.99 bool checkQgisVersion( const QString &minVersion, const QString &maxVersion = "" ) const; private: diff --git a/src/core/composer/qgscomposermultiframe.cpp b/src/core/composer/qgscomposermultiframe.cpp index 7a131956016..c9bc0ac7866 100644 --- a/src/core/composer/qgscomposermultiframe.cpp +++ b/src/core/composer/qgscomposermultiframe.cpp @@ -124,7 +124,7 @@ void QgsComposerMultiFrame::recalculateFrameSizes() while ( ( mResizeMode == RepeatOnEveryPage ) || currentY < totalHeight ) { //find out on which page the lower left point of the last frame is - int page = qFloor( ( currentItem->pos().y() + currentItem->rect().height() ) / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() ) ) + 1; + int page = std::floor( ( currentItem->pos().y() + currentItem->rect().height() ) / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() ) ) + 1; if ( mResizeMode == RepeatOnEveryPage ) { diff --git a/src/core/composer/qgscomposerscalebar.cpp b/src/core/composer/qgscomposerscalebar.cpp index 94d72063b44..91c0699cb07 100644 --- a/src/core/composer/qgscomposerscalebar.cpp +++ b/src/core/composer/qgscomposerscalebar.cpp @@ -262,14 +262,14 @@ void QgsComposerScaleBar::refreshDataDefinedProperty( const QgsComposerObject::D // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc inline double nextNiceNumber( double a, double d = 1 ) { - double s = std::pow( 10.0, floor( log10( a ) ) ) / d; + double s = std::pow( 10.0, std::floor( log10( a ) ) ) / d; return ceil( a / s ) * s; } // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc inline double prevNiceNumber( double a, double d = 1 ) { - double s = std::pow( 10.0, floor( log10( a ) ) ) / d; + double s = std::pow( 10.0, std::floor( log10( a ) ) ) / d; return floor( a / s ) * s; } @@ -473,9 +473,9 @@ void QgsComposerScaleBar::applyDefaultSize( QgsUnitTypes::DistanceUnit u ) } double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier; - int segmentMagnitude = floor( log10( segmentWidth ) ); + int segmentMagnitude = std::floor( log10( segmentWidth ) ); double unitsPerSegment = upperMagnitudeMultiplier * ( std::pow( 10.0, segmentMagnitude ) ); - double multiplier = floor( ( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5; + double multiplier = std::floor( ( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5; if ( multiplier > 0 ) { diff --git a/src/core/composer/qgscomposertablev2.cpp b/src/core/composer/qgscomposertablev2.cpp index 7151d8c2448..fc1d29dac42 100644 --- a/src/core/composer/qgscomposertablev2.cpp +++ b/src/core/composer/qgscomposertablev2.cpp @@ -278,7 +278,7 @@ int QgsComposerTableV2::rowsVisible( double frameHeight, int firstRow, bool incl if ( includeEmptyRows && contentHeight > 0 ) { double rowHeight = ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont ); - currentRow += qMax( floor( contentHeight / rowHeight ), 0.0 ); + currentRow += qMax( std::floor( contentHeight / rowHeight ), 0.0 ); } return currentRow - firstRow - 1; diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index bdb5c80b9bf..831811fbab6 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -572,7 +572,7 @@ QPointF QgsComposition::positionOnPage( QPointF position ) const int QgsComposition::pageNumberForPoint( QPointF position ) const { - int pageNumber = qFloor( position.y() / ( paperHeight() + spaceBetweenPages() ) ) + 1; + int pageNumber = std::floor( position.y() / ( paperHeight() + spaceBetweenPages() ) ) + 1; pageNumber = pageNumber < 1 ? 1 : pageNumber; pageNumber = pageNumber > mPages.size() ? mPages.size() : pageNumber; return pageNumber; diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 3101f5bb226..437e61f9b45 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -859,7 +859,7 @@ static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( floor( x ) ); + return QVariant( std::floor( x ) ); } static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) diff --git a/src/core/expression/qgsexpressionnodeimpl.cpp b/src/core/expression/qgsexpressionnodeimpl.cpp index 9cf3f640475..8e33114cb78 100644 --- a/src/core/expression/qgsexpressionnodeimpl.cpp +++ b/src/core/expression/qgsexpressionnodeimpl.cpp @@ -268,7 +268,7 @@ QVariant QgsExpressionNodeBinaryOperator::evalNode( QgsExpression *parent, const ENSURE_NO_EVAL_ERROR; if ( fR == 0. ) return QVariant(); // silently handle division by zero and return NULL - return QVariant( qFloor( fL / fR ) ); + return QVariant( qlonglong( std::floor( fL / fR ) ) ); } case boPow: if ( QgsExpressionUtils::isNull( vL ) || QgsExpressionUtils::isNull( vR ) ) diff --git a/src/core/geometry/qgsinternalgeometryengine.cpp b/src/core/geometry/qgsinternalgeometryengine.cpp index 0efe0c94f2d..f388b5f2837 100644 --- a/src/core/geometry/qgsinternalgeometryengine.cpp +++ b/src/core/geometry/qgsinternalgeometryengine.cpp @@ -575,7 +575,7 @@ QgsLineString *doDensify( QgsLineString *ring, int extraNodesPerSegment = -1, do if ( extraNodesPerSegment < 0 ) { // distance mode - extraNodesThisSegment = floor( sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ) / distance ); + extraNodesThisSegment = std::floor( sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ) / distance ); if ( extraNodesThisSegment >= 1 ) multiplier = 1.0 / ( extraNodesThisSegment + 1 ); } diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 93667c56a46..432c0c9c314 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -270,7 +270,7 @@ bool QgsField::convertCompatible( QVariant &v ) const { double s = std::pow( 10, d->precision ); double d = v.toDouble() * s; - v = QVariant( ( d < 0 ? std::ceil( d - 0.5 ) : floor( d + 0.5 ) ) / s ); + v = QVariant( ( d < 0 ? std::ceil( d - 0.5 ) : std::floor( d + 0.5 ) ) / s ); return true; } diff --git a/src/core/raster/qgscubicrasterresampler.cpp b/src/core/raster/qgscubicrasterresampler.cpp index 85615373a0b..a1dda4546c0 100644 --- a/src/core/raster/qgscubicrasterresampler.cpp +++ b/src/core/raster/qgscubicrasterresampler.cpp @@ -105,7 +105,7 @@ void QgsCubicRasterResampler::resample( const QImage &srcImage, QImage &dstImage for ( int y = 0; y < dstImage.height(); ++y ) { - currentSrcRowInt = floor( currentSrcRow ); + currentSrcRowInt = std::floor( currentSrcRow ); v = currentSrcRow - currentSrcRowInt; currentSrcCol = nSrcPerDstX / 2.0 - 0.5; @@ -113,7 +113,7 @@ void QgsCubicRasterResampler::resample( const QImage &srcImage, QImage &dstImage QRgb *scanLine = ( QRgb * )dstImage.scanLine( y ); for ( int x = 0; x < dstImage.width(); ++x ) { - currentSrcColInt = floor( currentSrcCol ); + currentSrcColInt = std::floor( currentSrcCol ); u = currentSrcCol - currentSrcColInt; //handle eight edge-cases diff --git a/src/core/raster/qgsrasterblock.cpp b/src/core/raster/qgsrasterblock.cpp index 29813294d9b..39944842fe9 100644 --- a/src/core/raster/qgsrasterblock.cpp +++ b/src/core/raster/qgsrasterblock.cpp @@ -279,7 +279,7 @@ double QgsRasterBlock::value( int row, int column ) const QRgb QgsRasterBlock::color( qgssize index ) const { - int row = floor( static_cast< double >( index ) / mWidth ); + int row = std::floor( static_cast< double >( index ) / mWidth ); int column = index % mWidth; return color( row, column ); } diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index 75e10f7603b..0e1ecf23627 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -121,14 +121,14 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { - int col = floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); + int col = std::floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { - int row = floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); + int row = std::floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); @@ -159,12 +159,12 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; - int tmpRow = floor( ( tmpYMax - y ) / tmpYRes ); + int tmpRow = std::floor( ( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; - int tmpCol = floor( ( x - tmpXMin ) / tmpXRes ); + int tmpCol = std::floor( ( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { @@ -307,8 +307,8 @@ QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPointXY &point double xres = ( finalExtent.width() ) / width; double yres = ( finalExtent.height() ) / height; - int col = static_cast< int >( floor( ( point.x() - finalExtent.xMinimum() ) / xres ) ); - int row = static_cast< int >( floor( ( finalExtent.yMaximum() - point.y() ) / yres ) ); + int col = static_cast< int >( std::floor( ( point.x() - finalExtent.xMinimum() ) / xres ) ); + int row = static_cast< int >( std::floor( ( finalExtent.yMaximum() - point.y() ) / yres ) ); double xMin = finalExtent.xMinimum() + col * xres; double xMax = xMin + xres; diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index 274ace2710c..fb8427413a6 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -479,7 +479,7 @@ QgsRasterHistogram QgsRasterInterface::histogram( int bandNo, } double myValue = blk->value( i ); - int myBinIndex = static_cast ( qFloor( ( myValue - myMinimum ) / myBinSize ) ); + int myBinIndex = static_cast ( std::floor( ( myValue - myMinimum ) / myBinSize ) ); if ( ( myBinIndex < 0 || myBinIndex > ( myBinCount - 1 ) ) && !includeOutOfRange ) { @@ -566,7 +566,7 @@ void QgsRasterInterface::cumulativeCut( int bandNo, mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::UInt32 ) { if ( lowerValue != std::numeric_limits::quiet_NaN() ) - lowerValue = floor( lowerValue ); + lowerValue = std::floor( lowerValue ); if ( upperValue != std::numeric_limits::quiet_NaN() ) upperValue = std::ceil( upperValue ); } diff --git a/src/core/raster/qgsrasterlayerrenderer.cpp b/src/core/raster/qgsrasterlayerrenderer.cpp index 5cb69653314..837a6a55633 100644 --- a/src/core/raster/qgsrasterlayerrenderer.cpp +++ b/src/core/raster/qgsrasterlayerrenderer.cpp @@ -126,14 +126,14 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer *layer, QgsRender mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() ); mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() ); - // align to output device grid, i.e. floor/ceil to integers + // align to output device grid, i.e. std::floor/ceil to integers // TODO: this should only be done if paint device is raster - screen, image // for other devices (pdf) it can have floating point origin // we could use floating point for raster devices as well, but respecting the // output device grid should make it more effective as the resampling is done in // the provider anyway - mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) ); - mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) ); + mRasterViewPort->mTopLeftPoint.setX( std::floor( mRasterViewPort->mTopLeftPoint.x() ) ); + mRasterViewPort->mTopLeftPoint.setY( std::floor( mRasterViewPort->mTopLeftPoint.y() ) ); mRasterViewPort->mBottomRightPoint.setX( std::ceil( mRasterViewPort->mBottomRightPoint.x() ) ); mRasterViewPort->mBottomRightPoint.setY( std::ceil( mRasterViewPort->mBottomRightPoint.y() ) ); // recalc myRasterExtent to aligned values diff --git a/src/core/raster/qgsrasterprojector.cpp b/src/core/raster/qgsrasterprojector.cpp index ee50c0e2a60..4b1d9d07d87 100644 --- a/src/core/raster/qgsrasterprojector.cpp +++ b/src/core/raster/qgsrasterprojector.cpp @@ -255,7 +255,7 @@ void ProjectorData::calcSrcExtent() if ( mMaxSrcXRes > 0 ) { // with floor/ceil it should work correctly also for mSrcExtent.xMinimum() < mExtent.xMinimum() - double col = floor( ( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes ); + double col = std::floor( ( mSrcExtent.xMinimum() - mExtent.xMinimum() ) / mMaxSrcXRes ); double x = mExtent.xMinimum() + col * mMaxSrcXRes; mSrcExtent.setXMinimum( x ); @@ -265,7 +265,7 @@ void ProjectorData::calcSrcExtent() } if ( mMaxSrcYRes > 0 ) { - double row = floor( ( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes ); + double row = std::floor( ( mExtent.yMaximum() - mSrcExtent.yMaximum() ) / mMaxSrcYRes ); double y = mExtent.yMaximum() - row * mMaxSrcYRes; mSrcExtent.setYMaximum( y ); @@ -384,11 +384,11 @@ inline void ProjectorData::destPointOnCPMatrix( int row, int col, double *theX, inline int ProjectorData::matrixRow( int destRow ) { - return static_cast< int >( floor( ( destRow + 0.5 ) / mDestRowsPerMatrixRow ) ); + return static_cast< int >( std::floor( ( destRow + 0.5 ) / mDestRowsPerMatrixRow ) ); } inline int ProjectorData::matrixCol( int destCol ) { - return static_cast< int >( floor( ( destCol + 0.5 ) / mDestColsPerMatrixCol ) ); + return static_cast< int >( std::floor( ( destCol + 0.5 ) / mDestColsPerMatrixCol ) ); } void ProjectorData::calcHelper( int matrixRow, QgsPointXY *points ) @@ -470,8 +470,8 @@ bool ProjectorData::preciseSrcRowCol( int destRow, int destCol, int *srcRow, int return false; } // Get source row col - *srcRow = static_cast< int >( floor( ( mSrcExtent.yMaximum() - y ) / mSrcYRes ) ); - *srcCol = static_cast< int >( floor( ( x - mSrcExtent.xMinimum() ) / mSrcXRes ) ); + *srcRow = static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - y ) / mSrcYRes ) ); + *srcCol = static_cast< int >( std::floor( ( x - mSrcExtent.xMinimum() ) / mSrcXRes ) ); #ifdef QGISDEBUG QgsDebugMsgLevel( QString( "mSrcExtent.yMinimum() = %1 mSrcExtent.yMaximum() = %2 mSrcYRes = %3" ).arg( mSrcExtent.yMinimum() ).arg( mSrcExtent.yMaximum() ).arg( mSrcYRes ), 5 ); QgsDebugMsgLevel( QString( "theSrcRow = %1 srcCol = %2" ).arg( *srcRow ).arg( *srcCol ), 5 ); @@ -532,8 +532,8 @@ bool ProjectorData::approximateSrcRowCol( int destRow, int destCol, int *srcRow, // TODO: check again cell selection (coor is in the middle) - *srcRow = static_cast< int >( floor( ( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes ) ); - *srcCol = static_cast< int >( floor( ( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes ) ); + *srcRow = static_cast< int >( std::floor( ( mSrcExtent.yMaximum() - mySrcY ) / mSrcYRes ) ); + *srcCol = static_cast< int >( std::floor( ( mySrcX - mSrcExtent.xMinimum() ) / mSrcXRes ) ); // For now silently correct limits to avoid crashes // TODO: review diff --git a/src/core/symbology/qgsgraduatedsymbolrenderer.cpp b/src/core/symbology/qgsgraduatedsymbolrenderer.cpp index fb2ba6b28e4..f91d8217677 100644 --- a/src/core/symbology/qgsgraduatedsymbolrenderer.cpp +++ b/src/core/symbology/qgsgraduatedsymbolrenderer.cpp @@ -699,7 +699,7 @@ static QList _calcJenksBreaks( QList values, int classes, { // pick a random integer from 0 to n double r = qrand(); - int j = floor( r / RAND_MAX * ( values.size() - 1 ) ); + int j = std::floor( r / RAND_MAX * ( values.size() - 1 ) ); sample[ i ] = values[ j ]; } } diff --git a/src/core/symbology/qgspointdisplacementrenderer.cpp b/src/core/symbology/qgspointdisplacementrenderer.cpp index 8de7d1a8581..8cca104c085 100644 --- a/src/core/symbology/qgspointdisplacementrenderer.cpp +++ b/src/core/symbology/qgspointdisplacementrenderer.cpp @@ -279,7 +279,7 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRe while ( pointsRemaining > 0 ) { double radiusCurrentRing = qMax( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 ); - int maxPointsCurrentRing = qMax( floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 ); + int maxPointsCurrentRing = qMax( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 ); int actualPointsCurrentRing = qMin( maxPointsCurrentRing, pointsRemaining ); double angleStep = 2 * M_PI / actualPointsCurrentRing; diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 7dbdca2cb92..a14769d6f84 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3932,7 +3932,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, cell = 20 * 1e-07; } - double base = std::pow( 10.0, floor( log10( cell ) ) ); + double base = std::pow( 10.0, std::floor( log10( cell ) ) ); double unit = base; if ( ( 2 * base ) - cell < h * ( cell - unit ) ) { @@ -3947,7 +3947,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, } } // Maybe used to correct for the epsilon here?? - int start = floor( minimum / unit + 1e-07 ); + int start = std::floor( minimum / unit + 1e-07 ); int end = std::ceil( maximum / unit - 1e-07 ); // Extend the range out beyond the data. Does this ever happen?? @@ -3963,7 +3963,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, // If we don't have quite enough labels, extend the range out // to make more (these labels are beyond the data :() - int k = floor( 0.5 + end - start ); + int k = std::floor( 0.5 + end - start ); if ( k < minimumCount ) { k = minimumCount - k; diff --git a/src/gui/layout/qgslayoutruler.cpp b/src/gui/layout/qgslayoutruler.cpp index 3cc239ec009..a8ed5602747 100644 --- a/src/gui/layout/qgslayoutruler.cpp +++ b/src/gui/layout/qgslayoutruler.cpp @@ -125,7 +125,7 @@ void QgsLayoutRuler::paintEvent( QPaintEvent *event ) double endX = t.map( QPointF( width(), 0 ) ).x(); //start marker position in mm - double markerPos = ( floor( startX / mmDisplay ) + 1 ) * mmDisplay; + double markerPos = ( std::floor( startX / mmDisplay ) + 1 ) * mmDisplay; //draw minor ticks marks which occur before first major tick drawSmallDivisions( &p, markerPos, numSmallDivisions, -mmDisplay ); diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index 99a1491dc97..24464d0df70 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -1559,7 +1559,7 @@ void QgsColorPreviewWidget::drawColor( const QColor &color, QRect rect, QPainter //ensure at least a 1px overlap to avoid artifacts QBrush colorBrush = QBrush( color ); painter.setBrush( colorBrush ); - painter.drawRect( floor( rect.width() / 2.0 ) + rect.left(), rect.top(), rect.width() - floor( rect.width() / 2.0 ), rect.height() ); + painter.drawRect( std::floor( rect.width() / 2.0 ) + rect.left(), rect.top(), rect.width() - std::floor( rect.width() / 2.0 ), rect.height() ); QColor opaqueColor = QColor( color ); opaqueColor.setAlpha( 255 ); diff --git a/src/gui/qgscomposerruler.cpp b/src/gui/qgscomposerruler.cpp index bc798b5859c..745f2bfa3d3 100644 --- a/src/gui/qgscomposerruler.cpp +++ b/src/gui/qgscomposerruler.cpp @@ -103,7 +103,7 @@ void QgsComposerRuler::paintEvent( QPaintEvent *event ) double endX = t.map( QPointF( width(), 0 ) ).x(); //start marker position in mm - double markerPos = ( floor( startX / mmDisplay ) + 1 ) * mmDisplay; + double markerPos = ( std::floor( startX / mmDisplay ) + 1 ) * mmDisplay; //draw minor ticks marks which occur before first major tick drawSmallDivisions( &p, markerPos, numSmallDivisions, -mmDisplay ); diff --git a/src/providers/arcgisrest/qgsamsprovider.cpp b/src/providers/arcgisrest/qgsamsprovider.cpp index 9417271a386..f8e5a5711dc 100644 --- a/src/providers/arcgisrest/qgsamsprovider.cpp +++ b/src/providers/arcgisrest/qgsamsprovider.cpp @@ -287,8 +287,8 @@ void QgsAmsProvider::draw( const QgsRectangle &viewExtent, int pixelWidth, int p // Get necessary tiles to fill extent // tile_x = ox + i * (resolution * tileWidth) // tile_y = oy - j * (resolution * tileHeight) - int ixStart = qFloor( ( viewExtent.xMinimum() - ox ) / ( tileWidth * resolution ) ); - int iyStart = qFloor( ( oy - viewExtent.yMaximum() ) / ( tileHeight * resolution ) ); + int ixStart = std::floor( ( viewExtent.xMinimum() - ox ) / ( tileWidth * resolution ) ); + int iyStart = std::floor( ( oy - viewExtent.yMaximum() ) / ( tileHeight * resolution ) ); int ixEnd = std::ceil( ( viewExtent.xMaximum() - ox ) / ( tileWidth * resolution ) ); int iyEnd = std::ceil( ( oy - viewExtent.yMinimum() ) / ( tileHeight * resolution ) ); double imX = ( viewExtent.xMinimum() - ox ) / resolution; diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 11ce0732fb9..b24cff7e2c0 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -85,7 +85,7 @@ int CPL_STDCALL progressCallback( double dfComplete, sDfLastComplete = dfComplete; } - if ( floor( sDfLastComplete * 10 ) != floor( dfComplete * 10 ) ) + if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) ) { if ( prog->feedback ) prog->feedback->setProgress( dfComplete * 100 ); @@ -556,21 +556,21 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi // Get necessary src extent aligned to src resolution if ( mExtent.xMinimum() < myRasterExtent.xMinimum() ) { - srcLeft = static_cast( floor( ( myRasterExtent.xMinimum() - mExtent.xMinimum() ) / srcXRes ) ); + srcLeft = static_cast( std::floor( ( myRasterExtent.xMinimum() - mExtent.xMinimum() ) / srcXRes ) ); } if ( mExtent.xMaximum() > myRasterExtent.xMaximum() ) { - srcRight = static_cast( floor( ( myRasterExtent.xMaximum() - mExtent.xMinimum() ) / srcXRes ) ); + srcRight = static_cast( std::floor( ( myRasterExtent.xMaximum() - mExtent.xMinimum() ) / srcXRes ) ); } // GDAL states that mGeoTransform[3] is top, may it also be bottom and mGeoTransform[5] positive? if ( mExtent.yMaximum() > myRasterExtent.yMaximum() ) { - srcTop = static_cast( floor( -1. * ( mExtent.yMaximum() - myRasterExtent.yMaximum() ) / srcYRes ) ); + srcTop = static_cast( std::floor( -1. * ( mExtent.yMaximum() - myRasterExtent.yMaximum() ) / srcYRes ) ); } if ( mExtent.yMinimum() < myRasterExtent.yMinimum() ) { - srcBottom = static_cast( floor( -1. * ( mExtent.yMaximum() - myRasterExtent.yMinimum() ) / srcYRes ) ); + srcBottom = static_cast( std::floor( -1. * ( mExtent.yMaximum() - myRasterExtent.yMinimum() ) / srcYRes ) ); } QgsDebugMsg( QString( "srcTop = %1 srcBottom = %2 srcLeft = %3 srcRight = %4" ).arg( srcTop ).arg( srcBottom ).arg( srcLeft ).arg( srcRight ) ); @@ -626,7 +626,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi double y = myRasterExtent.yMaximum() - 0.5 * yRes; for ( int row = 0; row < height; row++ ) { - int tmpRow = static_cast( floor( -1. * ( tmpYMax - y ) / tmpYRes ) ); + int tmpRow = static_cast( std::floor( -1. * ( tmpYMax - y ) / tmpYRes ) ); char *srcRowBlock = tmpBlock + dataSize * tmpRow * tmpWidth; char *dstRowBlock = ( char * )block + dataSize * ( top + row ) * pixelWidth; @@ -640,7 +640,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi int lastCol = 0; for ( int col = 0; col < width; ++col ) { - // floor() is quite slow! Use just cast to int. + // std::floor() is quite slow! Use just cast to int. tmpCol = static_cast( x ); if ( tmpCol > lastCol ) { @@ -1002,9 +1002,9 @@ QgsRasterIdentifyResult QgsGdalProvider::identify( const QgsPointXY &point, QgsR double xres = ( finalExtent.width() ) / width; double yres = ( finalExtent.height() ) / height; - // Offset, not the cell index -> floor - int col = ( int ) floor( ( point.x() - finalExtent.xMinimum() ) / xres ); - int row = ( int ) floor( ( finalExtent.yMaximum() - point.y() ) / yres ); + // Offset, not the cell index -> std::floor + int col = ( int ) std::floor( ( point.x() - finalExtent.xMinimum() ) / xres ); + int row = ( int ) std::floor( ( finalExtent.yMaximum() - point.y() ) / yres ); QgsDebugMsg( QString( "row = %1 col = %2" ).arg( row ).arg( col ) ); diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp index b3e23e5593d..b2e0606fc3c 100644 --- a/src/providers/wcs/qgswcsprovider.cpp +++ b/src/providers/wcs/qgswcsprovider.cpp @@ -1479,8 +1479,8 @@ QgsRasterIdentifyResult QgsWcsProvider::identify( const QgsPointXY &point, QgsRa double yRes = mCachedViewExtent.height() / mCachedViewHeight; // Offset, not the cell index -> flor - int col = ( int ) floor( ( x - mCachedViewExtent.xMinimum() ) / xRes ); - int row = ( int ) floor( ( mCachedViewExtent.yMaximum() - y ) / yRes ); + int col = ( int ) std::floor( ( x - mCachedViewExtent.xMinimum() ) / xRes ); + int row = ( int ) std::floor( ( mCachedViewExtent.yMaximum() - y ) / yRes ); QgsDebugMsg( "row = " + QString::number( row ) + " col = " + QString::number( col ) ); diff --git a/src/providers/wms/qgswmscapabilities.cpp b/src/providers/wms/qgswmscapabilities.cpp index 641b88b0821..185ff5a5d65 100644 --- a/src/providers/wms/qgswmscapabilities.cpp +++ b/src/providers/wms/qgswmscapabilities.cpp @@ -2146,10 +2146,10 @@ void QgsWmtsTileMatrix::viewExtentIntersection( const QgsRectangle &viewExtent, // .arg( minTileRow ).arg( maxTileRow ) ); } - col0 = qBound( minTileCol, ( int ) floor( ( viewExtent.xMinimum() - topLeft.x() ) / twMap ), maxTileCol ); - row0 = qBound( minTileRow, ( int ) floor( ( topLeft.y() - viewExtent.yMaximum() ) / thMap ), maxTileRow ); - col1 = qBound( minTileCol, ( int ) floor( ( viewExtent.xMaximum() - topLeft.x() ) / twMap ), maxTileCol ); - row1 = qBound( minTileRow, ( int ) floor( ( topLeft.y() - viewExtent.yMinimum() ) / thMap ), maxTileRow ); + col0 = qBound( minTileCol, ( int ) std::floor( ( viewExtent.xMinimum() - topLeft.x() ) / twMap ), maxTileCol ); + row0 = qBound( minTileRow, ( int ) std::floor( ( topLeft.y() - viewExtent.yMaximum() ) / thMap ), maxTileRow ); + col1 = qBound( minTileCol, ( int ) std::floor( ( viewExtent.xMaximum() - topLeft.x() ) / twMap ), maxTileCol ); + row1 = qBound( minTileRow, ( int ) std::floor( ( topLeft.y() - viewExtent.yMinimum() ) / thMap ), maxTileRow ); } const QgsWmtsTileMatrix *QgsWmtsTileMatrixSet::findNearestResolution( double vres ) const diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index 84242f9f33e..fac436e8316 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -1521,7 +1521,7 @@ QStringList QgsWmsProvider::subLayerStyles() const bool QgsWmsProvider::calculateExtent() const { - //! \todo Make this handle non-geographic CRSs (e.g. floor plans) as per WMS spec + //! \todo Make this handle non-geographic CRSs (e.g. std::floor plans) as per WMS spec if ( mSettings.mTiled ) @@ -2471,8 +2471,8 @@ QgsRasterIdentifyResult QgsWmsProvider::identify( const QgsPointXY &point, QgsRa QgsDebugMsg( QString( "xRes = %1 yRes = %2" ).arg( xRes ).arg( yRes ) ); QgsPointXY finalPoint; - finalPoint.setX( floor( ( finalPoint.x() - myExtent.xMinimum() ) / xRes ) ); - finalPoint.setY( floor( ( myExtent.yMaximum() - finalPoint.y() ) / yRes ) ); + finalPoint.setX( std::floor( ( finalPoint.x() - myExtent.xMinimum() ) / xRes ) ); + finalPoint.setY( std::floor( ( myExtent.yMaximum() - finalPoint.y() ) / yRes ) ); QgsDebugMsg( QString( "point = %1 %2" ).arg( finalPoint.x() ).arg( finalPoint.y() ) ); @@ -2622,8 +2622,8 @@ QgsRasterIdentifyResult QgsWmsProvider::identify( const QgsPointXY &point, QgsRa double thMap = tm->tileHeight * tres; QgsDebugMsg( QString( "tile map size: %1,%2" ).arg( qgsDoubleToString( twMap ), qgsDoubleToString( thMap ) ) ); - int col = ( int ) floor( ( point.x() - tm->topLeft.x() ) / twMap ); - int row = ( int ) floor( ( tm->topLeft.y() - point.y() ) / thMap ); + int col = ( int ) std::floor( ( point.x() - tm->topLeft.x() ) / twMap ); + int row = ( int ) std::floor( ( tm->topLeft.y() - point.y() ) / thMap ); double tx = tm->topLeft.x() + col * twMap; double ty = tm->topLeft.y() - row * thMap; int i = ( point.x() - tx ) / tres; From fec03ca42300d67b84e520a569cd34f5f4885746 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:18:05 +1000 Subject: [PATCH 122/364] (q)sin -> std::sin --- src/analysis/raster/qgshillshadefilter.cpp | 2 +- src/analysis/raster/qgsrastermatrix.cpp | 2 +- src/analysis/raster/qgsrastermatrix.h | 2 +- src/app/dwg/libdxfrw/drw_entities.cpp | 4 +- src/app/dwg/qgsdwgimporter.cpp | 12 ++--- src/app/nodetool/qgsnodetool.cpp | 2 +- src/app/qgsdecorationnortharrow.cpp | 4 +- src/app/qgsdecorationnortharrowdialog.cpp | 4 +- src/app/qgsmaptoollabel.cpp | 4 +- src/app/qgsmaptooloffsetpointsymbol.cpp | 2 +- src/app/qgsmaptoolrotatefeature.cpp | 8 +-- src/app/qgsmaptoolrotatelabel.cpp | 4 +- src/app/qgsmaptoolselectradius.cpp | 2 +- src/app/qgspointrotationitem.cpp | 2 +- src/core/composer/qgscomposerarrow.cpp | 2 +- src/core/composer/qgscomposerutils.cpp | 10 ++-- src/core/composer/qgscomposition.cpp | 8 +-- src/core/effects/qgsshadoweffect.cpp | 2 +- src/core/expression/qgsexpressionfunction.cpp | 2 +- src/core/geometry/qgsellipse.cpp | 10 ++-- src/core/geometry/qgsgeometryutils.cpp | 2 +- src/core/geometry/qgspoint.cpp | 6 +-- src/core/geometry/qgsregularpolygon.cpp | 4 +- src/core/pal/feature.cpp | 50 +++++++++---------- src/core/pal/geomfunction.cpp | 4 +- src/core/pal/labelposition.cpp | 4 +- src/core/pal/pointset.cpp | 4 +- src/core/qgsdistancearea.cpp | 24 ++++----- src/core/qgsmapsettingsutils.cpp | 6 +-- src/core/qgspallabeling.cpp | 4 +- src/core/qgspointxy.cpp | 2 +- src/core/qgsscalecalculator.cpp | 2 +- src/core/qgstextrenderer.cpp | 6 +-- src/core/qgsvector.cpp | 2 +- src/core/qgsvectorlayerlabelprovider.cpp | 4 +- src/core/raster/qgshillshaderenderer.cpp | 14 +++--- src/core/symbology/qgs25drenderer.cpp | 10 ++-- src/core/symbology/qgsarrowsymbollayer.cpp | 2 +- src/core/symbology/qgsellipsesymbollayer.cpp | 2 +- src/core/symbology/qgsfillsymbollayer.cpp | 12 ++--- src/core/symbology/qgslinesymbollayer.cpp | 4 +- src/core/symbology/qgsmarkersymbollayer.cpp | 40 +++++++-------- .../qgspointdisplacementrenderer.cpp | 4 +- src/core/symbology/qgssymbollayer.cpp | 2 +- .../symbology/qgsvectorfieldsymbollayer.cpp | 2 +- src/gui/qgsadvanceddigitizingcanvasitem.cpp | 6 +-- src/gui/qgsadvanceddigitizingdockwidget.cpp | 4 +- src/gui/qgscolorwidgets.cpp | 6 +-- .../georeferencer/qgsgeorefplugingui.cpp | 4 +- .../georeferencer/qgsgeoreftransform.cpp | 10 ++-- tests/src/core/testqgsgeometry.cpp | 4 +- 51 files changed, 169 insertions(+), 169 deletions(-) diff --git a/src/analysis/raster/qgshillshadefilter.cpp b/src/analysis/raster/qgshillshadefilter.cpp index 0abd246d299..1168f50d289 100644 --- a/src/analysis/raster/qgshillshadefilter.cpp +++ b/src/analysis/raster/qgshillshadefilter.cpp @@ -49,5 +49,5 @@ float QgsHillshadeFilter::processNineCellWindow( float *x11, float *x21, float * { aspect_rad = M_PI + std::atan2( derX, derY ); } - return qMax( 0.0, 255.0 * ( ( std::cos( zenith_rad ) * std::cos( slope_rad ) ) + ( sin( zenith_rad ) * sin( slope_rad ) * std::cos( azimuth_rad - aspect_rad ) ) ) ); + return qMax( 0.0, 255.0 * ( ( std::cos( zenith_rad ) * std::cos( slope_rad ) ) + ( std::sin( zenith_rad ) * std::sin( slope_rad ) * std::cos( azimuth_rad - aspect_rad ) ) ) ); } diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index 0aa98e6ecbf..8890fef4558 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -220,7 +220,7 @@ bool QgsRasterMatrix::oneArgumentOperation( OneArgOperator op ) } break; case opSIN: - mData[i] = sin( value ); + mData[i] = std::sin( value ); break; case opCOS: mData[i] = std::cos( value ); diff --git a/src/analysis/raster/qgsrastermatrix.h b/src/analysis/raster/qgsrastermatrix.h index 613a693f14c..2967eaa812d 100644 --- a/src/analysis/raster/qgsrastermatrix.h +++ b/src/analysis/raster/qgsrastermatrix.h @@ -124,7 +124,7 @@ class ANALYSIS_EXPORT QgsRasterMatrix bool twoArgumentOperation( TwoArgOperator op, const QgsRasterMatrix &other ); double calculateTwoArgumentOp( TwoArgOperator op, double arg1, double arg2 ) const; - /*sqrt, sin, std::cos, tan, asin, acos, atan*/ + /*sqrt, std::sin, std::cos, tan, asin, acos, atan*/ bool oneArgumentOperation( OneArgOperator op ); bool testPowerValidity( double base, double power ) const; }; diff --git a/src/app/dwg/libdxfrw/drw_entities.cpp b/src/app/dwg/libdxfrw/drw_entities.cpp index c60fd03e8e2..6f136dd50dc 100644 --- a/src/app/dwg/libdxfrw/drw_entities.cpp +++ b/src/app/dwg/libdxfrw/drw_entities.cpp @@ -959,7 +959,7 @@ void DRW_Ellipse::toPolyline( DRW_Polyline *pol, int parts ) const //calculate sin & std::cos of included angle double incAngle = std::atan2( secPoint.y, secPoint.x ); double cosRot = std::cos( incAngle ); - double sinRot = sin( incAngle ); + double sinRot = std::sin( incAngle ); incAngle = M_PIx2 / parts; double curAngle = staparam; @@ -970,7 +970,7 @@ void DRW_Ellipse::toPolyline( DRW_Polyline *pol, int parts ) const while ( curAngle < endAngle ) { double cosCurr = std::cos( curAngle ); - double sinCurr = sin( curAngle ); + double sinCurr = std::sin( curAngle ); double x = basePoint.x + cosCurr * cosRot * radMajor - sinCurr * sinRot * radMinor; double y = basePoint.y + cosCurr * sinRot * radMajor + sinCurr * cosRot * radMinor; pol->addVertex( DRW_Vertex( x, y, 0.0, 0.0 ) ); diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index f64c34c5679..6763275ef1b 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -1341,9 +1341,9 @@ void QgsDwgImporter::addArc( const DRW_Arc &data ) QgsCircularString c; c.setPoints( QgsPointSequence() - << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a0 ) * data.mRadius, data.basePoint.y + sin( a0 ) * data.mRadius ) - << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a1 ) * data.mRadius, data.basePoint.y + sin( a1 ) * data.mRadius ) - << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a2 ) * data.mRadius, data.basePoint.y + sin( a2 ) * data.mRadius ) + << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a0 ) * data.mRadius, data.basePoint.y + std::sin( a0 ) * data.mRadius ) + << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a1 ) * data.mRadius, data.basePoint.y + std::sin( a1 ) * data.mRadius ) + << QgsPoint( QgsWkbTypes::PointZ, data.basePoint.x + std::cos( a2 ) * data.mRadius, data.basePoint.y + std::sin( a2 ) * data.mRadius ) ); if ( !createFeature( layer, f, c ) ) @@ -1445,7 +1445,7 @@ bool QgsDwgImporter::curveFromLWPolyline( const DRW_LWPolyline &data, QgsCompoun double dx = data.vertlist[i1]->x - data.vertlist[i0]->x; double dy = data.vertlist[i1]->y - data.vertlist[i0]->y; double c = sqrt( dx * dx + dy * dy ); - double r = c / 2.0 / sin( a ); + double r = c / 2.0 / std::sin( a ); double h = r * ( 1 - std::cos( a ) ); s << QgsPoint( QgsWkbTypes::PointZ, @@ -1556,7 +1556,7 @@ void QgsDwgImporter::addLWPolyline( const DRW_LWPolyline &data ) double dx = p1.x() - p0.x(); double dy = p1.y() - p0.y(); double c = sqrt( dx * dx + dy * dy ); - double r = c / 2.0 / sin( a ); + double r = c / 2.0 / std::sin( a ); double h = r * ( 1 - std::cos( a ) ); s << QgsPoint( QgsWkbTypes::PointZ, @@ -1758,7 +1758,7 @@ void QgsDwgImporter::addPolyline( const DRW_Polyline &data ) double dy = p1.y() - p0.y(); double dz = p1.z() - p0.z(); double c = sqrt( dx * dx + dy * dy ); - double r = c / 2.0 / sin( a ); + double r = c / 2.0 / std::sin( a ); double h = r * ( 1 - std::cos( a ) ); s << QgsPoint( QgsWkbTypes::PointZ, diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index 546b1c899ac..f6c0280814d 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -683,7 +683,7 @@ QgsPointXY QgsNodeTool::positionForEndpointMarker( const QgsPointLocator::Match double dist = 15 * canvas()->mapSettings().mapUnitsPerPixel(); double angle = std::atan2( dy, dx ); // to the top: angle=0, to the right: angle=90, to the left: angle=-90 double x = pt1.x() + std::cos( angle ) * dist; - double y = pt1.y() + sin( angle ) * dist; + double y = pt1.y() + std::sin( angle ) * dist; return QgsPointXY( x, y ); } diff --git a/src/app/qgsdecorationnortharrow.cpp b/src/app/qgsdecorationnortharrow.cpp index 47329a4faaa..642010bab2a 100644 --- a/src/app/qgsdecorationnortharrow.cpp +++ b/src/app/qgsdecorationnortharrow.cpp @@ -138,10 +138,10 @@ void QgsDecorationNorthArrow::render( const QgsMapSettings &mapSettings, QgsRend double myRadiansDouble = mRotationInt * M_PI / 180.0; int xShift = static_cast( ( ( centerXDouble * std::cos( myRadiansDouble ) ) + - ( centerYDouble * sin( myRadiansDouble ) ) + ( centerYDouble * std::sin( myRadiansDouble ) ) ) - centerXDouble ); int yShift = static_cast( ( - ( -centerXDouble * sin( myRadiansDouble ) ) + + ( -centerXDouble * std::sin( myRadiansDouble ) ) + ( centerYDouble * std::cos( myRadiansDouble ) ) ) - centerYDouble ); diff --git a/src/app/qgsdecorationnortharrowdialog.cpp b/src/app/qgsdecorationnortharrowdialog.cpp index dd4ea73af9e..e8378752d84 100644 --- a/src/app/qgsdecorationnortharrowdialog.cpp +++ b/src/app/qgsdecorationnortharrowdialog.cpp @@ -156,10 +156,10 @@ void QgsDecorationNorthArrowDialog::drawNorthArrow() double myRadiansDouble = ( PI / 180 ) * rotation; int xShift = static_cast( ( ( centerXDouble * std::cos( myRadiansDouble ) ) + - ( centerYDouble * sin( myRadiansDouble ) ) + ( centerYDouble * std::sin( myRadiansDouble ) ) ) - centerXDouble ); int yShift = static_cast( ( - ( -centerXDouble * sin( myRadiansDouble ) ) + + ( -centerXDouble * std::sin( myRadiansDouble ) ) + ( centerYDouble * std::cos( myRadiansDouble ) ) ) - centerYDouble ); diff --git a/src/app/qgsmaptoollabel.cpp b/src/app/qgsmaptoollabel.cpp index a3ad0665417..ff7b19b8b43 100644 --- a/src/app/qgsmaptoollabel.cpp +++ b/src/app/qgsmaptoollabel.cpp @@ -376,8 +376,8 @@ bool QgsMapToolLabel::currentLabelRotationPoint( QgsPointXY &pos, bool ignoreUps } double angle = mCurrentLabel.pos.rotation; - double xd = xdiff * std::cos( angle ) - ydiff * sin( angle ); - double yd = xdiff * sin( angle ) + ydiff * std::cos( angle ); + double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle ); + double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle ); if ( mCurrentLabel.pos.upsideDown && !ignoreUpsideDown ) { pos.setX( pos.x() - xd ); diff --git a/src/app/qgsmaptooloffsetpointsymbol.cpp b/src/app/qgsmaptooloffsetpointsymbol.cpp index 09e90f95c9a..946851b2a9e 100644 --- a/src/app/qgsmaptooloffsetpointsymbol.cpp +++ b/src/app/qgsmaptooloffsetpointsymbol.cpp @@ -270,7 +270,7 @@ QPointF QgsMapToolOffsetPointSymbol::calculateOffset( const QgsPointXY &startPoi QPointF QgsMapToolOffsetPointSymbol::rotatedOffset( QPointF offset, double angle ) const { angle = DEG2RAD( 360 - angle ); - double c = std::cos( angle ), s = sin( angle ); + double c = std::cos( angle ), s = std::sin( angle ); return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c ); } diff --git a/src/app/qgsmaptoolrotatefeature.cpp b/src/app/qgsmaptoolrotatefeature.cpp index c07dd2669ee..fbd05b01a79 100644 --- a/src/app/qgsmaptoolrotatefeature.cpp +++ b/src/app/qgsmaptoolrotatefeature.cpp @@ -352,11 +352,11 @@ void QgsMapToolRotateFeature::applyRotation( double rotation ) double angle = -1 * mRotation * ( PI / 180 ); QgsPointXY anchorPoint = toLayerCoordinates( vlayer, mStartPointMapCoords ); double a = std::cos( angle ); - double b = -1 * sin( angle ); - double c = anchorPoint.x() - std::cos( angle ) * anchorPoint.x() + sin( angle ) * anchorPoint.y(); - double d = sin( angle ); + double b = -1 * std::sin( angle ); + double c = anchorPoint.x() - std::cos( angle ) * anchorPoint.x() + std::sin( angle ) * anchorPoint.y(); + double d = std::sin( angle ); double ee = std::cos( angle ); - double f = anchorPoint.y() - sin( angle ) * anchorPoint.x() - std::cos( angle ) * anchorPoint.y(); + double f = anchorPoint.y() - std::sin( angle ) * anchorPoint.x() - std::cos( angle ) * anchorPoint.y(); vlayer->beginEditCommand( tr( "Features Rotated" ) ); diff --git a/src/app/qgsmaptoolrotatelabel.cpp b/src/app/qgsmaptoolrotatelabel.cpp index f71d61c40c1..ff6a9ff15b7 100644 --- a/src/app/qgsmaptoolrotatelabel.cpp +++ b/src/app/qgsmaptoolrotatelabel.cpp @@ -230,8 +230,8 @@ QgsPointXY QgsMapToolRotateLabel::rotatePointCounterClockwise( const QgsPointXY double v1x = input.x() - centerPoint.x(); double v1y = input.y() - centerPoint.y(); - double v2x = std::cos( rad ) * v1x - sin( rad ) * v1y; - double v2y = sin( rad ) * v1x + std::cos( rad ) * v1y; + double v2x = std::cos( rad ) * v1x - std::sin( rad ) * v1y; + double v2y = std::sin( rad ) * v1x + std::cos( rad ) * v1y; return QgsPointXY( centerPoint.x() + v2x, centerPoint.y() + v2y ); } diff --git a/src/app/qgsmaptoolselectradius.cpp b/src/app/qgsmaptoolselectradius.cpp index 59b0340c728..c647cd0d326 100644 --- a/src/app/qgsmaptoolselectradius.cpp +++ b/src/app/qgsmaptoolselectradius.cpp @@ -108,7 +108,7 @@ void QgsMapToolSelectRadius::setRadiusRubberBand( QgsPointXY &radiusEdge ) { double theta = i * ( 2.0 * M_PI / RADIUS_SEGMENTS ); QgsPointXY radiusPoint( mRadiusCenter.x() + r * std::cos( theta ), - mRadiusCenter.y() + r * sin( theta ) ); + mRadiusCenter.y() + r * std::sin( theta ) ); mRubberBand->addPoint( radiusPoint, false ); } mRubberBand->closePoints( true ); diff --git a/src/app/qgspointrotationitem.cpp b/src/app/qgspointrotationitem.cpp index 4220ae2fad1..fde389aea02 100644 --- a/src/app/qgspointrotationitem.cpp +++ b/src/app/qgspointrotationitem.cpp @@ -57,7 +57,7 @@ void QgsPointRotationItem::paint( QPainter *painter ) h = sqrt( ( double ) mPixmap.width() * mPixmap.width() + mPixmap.height() * mPixmap.height() ) / 2; //the half of the item diagonal dAngel = acos( mPixmap.width() / ( h * 2 ) ) * 180 / M_PI; //the diagonal angel of the original rect x = h * std::cos( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); - y = h * sin( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); + y = h * std::sin( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); } painter->rotate( painterRotation( mRotation ) ); diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp index 8ad50baa06f..ea960ba1035 100644 --- a/src/core/composer/qgscomposerarrow.cpp +++ b/src/core/composer/qgscomposerarrow.cpp @@ -281,7 +281,7 @@ void QgsComposerArrow::drawSVGMarker( QPainter *p, MarkerType type, const QStrin QPointF rotatedFixPoint; double angleRad = angle / 180 * M_PI; rotatedFixPoint.setX( fixPoint.x() * std::cos( angleRad ) + fixPoint.y() * -sin( angleRad ) ); - rotatedFixPoint.setY( fixPoint.x() * sin( angleRad ) + fixPoint.y() * std::cos( angleRad ) ); + rotatedFixPoint.setY( fixPoint.x() * std::sin( angleRad ) + fixPoint.y() * std::cos( angleRad ) ); p->translate( canvasPoint.x() - rotatedFixPoint.x(), canvasPoint.y() - rotatedFixPoint.y() ); } else diff --git a/src/core/composer/qgscomposerutils.cpp b/src/core/composer/qgscomposerutils.cpp index 8aa2425e517..8ca95102b23 100644 --- a/src/core/composer/qgscomposerutils.cpp +++ b/src/core/composer/qgscomposerutils.cpp @@ -43,9 +43,9 @@ void QgsComposerUtils::drawArrowHead( QPainter *p, const double x, const double QPointF p1Rotated, p2Rotated; p1Rotated.setX( p1.x() * std::cos( angleRad ) + p1.y() * -sin( angleRad ) ); - p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * std::cos( angleRad ) ); + p1Rotated.setY( p1.x() * std::sin( angleRad ) + p1.y() * std::cos( angleRad ) ); p2Rotated.setX( p2.x() * std::cos( angleRad ) + p2.y() * -sin( angleRad ) ); - p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * std::cos( angleRad ) ); + p2Rotated.setY( p2.x() * std::sin( angleRad ) + p2.y() * std::cos( angleRad ) ); QPolygonF arrowHeadPoly; arrowHeadPoly << middlePoint; @@ -88,8 +88,8 @@ void QgsComposerUtils::rotate( const double angle, double &x, double &y ) { double rotToRad = angle * M_PI / 180.0; double xRot, yRot; - xRot = x * std::cos( rotToRad ) - y * sin( rotToRad ); - yRot = x * sin( rotToRad ) + y * std::cos( rotToRad ); + xRot = x * std::cos( rotToRad ) - y * std::sin( rotToRad ); + yRot = x * std::sin( rotToRad ) + y * std::cos( rotToRad ); x = xRot; y = yRot; } @@ -186,7 +186,7 @@ QRectF QgsComposerUtils::largestRotatedRectWithinBounds( const QRectF &originalR //convert angle to radians and flip double angleRad = -clippedRotation * M_DEG2RAD; double cosAngle = std::cos( angleRad ); - double sinAngle = sin( angleRad ); + double sinAngle = std::sin( angleRad ); //calculate size of bounds of rotated rectangle double widthBoundsRotatedRect = originalWidth * std::fabs( cosAngle ) + originalHeight * std::fabs( sinAngle ); diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index 831811fbab6..778d36c03ce 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -3011,7 +3011,7 @@ double *QgsComposition::computeGeoTransform( const QgsComposerMap *map, const QR double mapXCenter = mapExtent.center().x(); double mapYCenter = mapExtent.center().y(); double alpha = - map->mapRotation() / 180 * M_PI; - double sinAlpha = sin( alpha ); + double sinAlpha = std::sin( alpha ); double cosAlpha = std::cos( alpha ); // get the extent (in map units) for the exported region @@ -3153,10 +3153,10 @@ void QgsComposition::computeWorldFileParameters( const QRectF &exportRegion, dou double r[6]; r[0] = std::cos( alpha ); r[1] = -sin( alpha ); - r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * sin( alpha ); - r[3] = sin( alpha ); + r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha ); + r[3] = std::sin( alpha ); r[4] = std::cos( alpha ); - r[5] = - xCenter * sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) ); + r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) ); // result = rotation x scaling = rotation(scaling(X)) a = r[0] * s[0] + r[1] * s[3]; diff --git a/src/core/effects/qgsshadoweffect.cpp b/src/core/effects/qgsshadoweffect.cpp index 90bb4733b51..d9052ef2dfb 100644 --- a/src/core/effects/qgsshadoweffect.cpp +++ b/src/core/effects/qgsshadoweffect.cpp @@ -57,7 +57,7 @@ void QgsShadowEffect::draw( QgsRenderContext &context ) double angleRad = mOffsetAngle * M_PI / 180; // to radians QPointF transPt( -offsetDist * std::cos( angleRad + M_PI / 2 ), - -offsetDist * sin( angleRad + M_PI / 2 ) ); + -offsetDist * std::sin( angleRad + M_PI / 2 ) ); //transparency, scale QgsImageOperation::multiplyOpacity( colorisedIm, mOpacity ); diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 437e61f9b45..1c496d362a1 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -257,7 +257,7 @@ static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionConte static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( sin( x ) ); + return QVariant( std::sin( x ) ); } static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index 01dfa3c5df1..5fc00b0f91f 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -209,10 +209,10 @@ QgsPointSequence QgsEllipse::points( unsigned int segments ) const { double x = mCenter.x() + mSemiMajorAxis * std::cos( *it ) * std::cos( azimuth ) - - mSemiMinorAxis * sin( *it ) * sin( azimuth ); + mSemiMinorAxis * std::sin( *it ) * std::sin( azimuth ); double y = mCenter.y() + - mSemiMajorAxis * std::cos( *it ) * sin( azimuth ) + - mSemiMinorAxis * sin( *it ) * std::cos( azimuth ); + mSemiMajorAxis * std::cos( *it ) * std::sin( azimuth ) + + mSemiMinorAxis * std::sin( *it ) * std::cos( azimuth ); pts.push_back( QgsPoint( pType, x, y, z, m ) ); } @@ -258,8 +258,8 @@ QgsRectangle QgsEllipse::boundingBox() const double angle = mAzimuth * M_PI / 180.0; double ux = mSemiMajorAxis * std::cos( angle ); - double uy = mSemiMinorAxis * sin( angle ); - double vx = mSemiMajorAxis * sin( angle ); + double uy = mSemiMinorAxis * std::sin( angle ); + double vx = mSemiMajorAxis * std::sin( angle ); double vy = mSemiMinorAxis * std::cos( angle ); double halfHeight = sqrt( ux * ux + uy * uy ); diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 2ef3c2f1994..f1f6d351608 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -704,7 +704,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co } x = centerX + radius * std::cos( angle ); - y = centerY + radius * sin( angle ); + y = centerY + radius * std::sin( angle ); if ( !hasZ && !hasM ) { diff --git a/src/core/geometry/qgspoint.cpp b/src/core/geometry/qgspoint.cpp index 1645723daf9..e0102083c7f 100644 --- a/src/core/geometry/qgspoint.cpp +++ b/src/core/geometry/qgspoint.cpp @@ -566,14 +566,14 @@ QgsPoint QgsPoint::project( double distance, double azimuth, double inclination if ( !is3D() && qgsDoubleNear( inclination, 90.0 ) ) { - dx = distance * sin( radsXy ); + dx = distance * std::sin( radsXy ); dy = distance * std::cos( radsXy ); } else { double radsZ = inclination * M_PI / 180.0; - dx = distance * sin( radsZ ) * sin( radsXy ); - dy = distance * sin( radsZ ) * std::cos( radsXy ); + dx = distance * std::sin( radsZ ) * std::sin( radsXy ); + dy = distance * std::sin( radsZ ) * std::cos( radsXy ); dz = distance * std::cos( radsZ ); } diff --git a/src/core/geometry/qgsregularpolygon.cpp b/src/core/geometry/qgsregularpolygon.cpp index fa4f5f5dc2e..3a08be35f5a 100644 --- a/src/core/geometry/qgsregularpolygon.cpp +++ b/src/core/geometry/qgsregularpolygon.cpp @@ -302,7 +302,7 @@ double QgsRegularPolygon::area() const return 0.0; } - return ( mRadius * mRadius * mNumberSides * sin( centralAngle() * M_PI / 180.0 ) ) / 2 ; + return ( mRadius * mRadius * mNumberSides * std::sin( centralAngle() * M_PI / 180.0 ) ) / 2 ; } double QgsRegularPolygon::perimeter() const @@ -322,7 +322,7 @@ double QgsRegularPolygon::length() const return 0.0; } - return mRadius * 2 * sin( M_PI / mNumberSides ); + return mRadius * 2 * std::sin( M_PI / mNumberSides ); } double QgsRegularPolygon::apothemToRadius( const double apothem, const unsigned int numSides ) const diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 355ef504e2e..93bab79357d 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -255,8 +255,8 @@ int FeaturePart::createCandidatesOverPoint( double x, double y, QList< LabelPosi { if ( !qgsDoubleNear( angle, 0.0 ) ) { - double xd = xdiff * std::cos( angle ) - ydiff * sin( angle ); - double yd = xdiff * sin( angle ) + ydiff * std::cos( angle ); + double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle ); + double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle ); xdiff = xd; ydiff = yd; } @@ -415,7 +415,7 @@ int FeaturePart::createCandidatesAtOrderedPositionsOverPoint( double x, double y //have bearing, distance - calculate reference point double referenceX = std::cos( alpha ) * distanceToLabel + x; - double referenceY = sin( alpha ) * distanceToLabel + y; + double referenceY = std::sin( alpha ) * distanceToLabel + y; double labelX = referenceX + deltaX; double labelY = referenceY + deltaY; @@ -499,7 +499,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo else if ( angleToCandidate < a90 - gamma2 ) // top-right { labelX += distanceToLabel * std::cos( angleToCandidate ); - labelY += distanceToLabel * sin( angleToCandidate ); + labelY += distanceToLabel * std::sin( angleToCandidate ); quadrant = LabelPosition::QuadrantAboveRight; } else if ( angleToCandidate < a90 + gamma2 ) // top @@ -512,7 +512,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo else if ( angleToCandidate < a180 - gamma1 ) // top left { labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth; - labelY += distanceToLabel * sin( angleToCandidate ); + labelY += distanceToLabel * std::sin( angleToCandidate ); quadrant = LabelPosition::QuadrantAboveLeft; } else if ( angleToCandidate < a180 + gamma1 ) // left @@ -525,7 +525,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo else if ( angleToCandidate < a270 - gamma2 ) // down - left { labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth; - labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight; + labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight; quadrant = LabelPosition::QuadrantBelowLeft; } else if ( angleToCandidate < a270 + gamma2 ) // down @@ -538,7 +538,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo else if ( angleToCandidate < a360 ) // down - right { labelX += distanceToLabel * std::cos( angleToCandidate ); - labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight; + labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight; quadrant = LabelPosition::QuadrantBelowRight; } @@ -781,24 +781,24 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QListpermissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) { - lPos.append( new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line + lPos.append( new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line placementCost += 0.001; } } if ( aboveLine ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) { - lPos.append( new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line + lPos.append( new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line placementCost += 0.001; } } if ( flags & FLAG_ON_LINE ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle ) ) - lPos.append( new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle ) ) + lPos.append( new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, cost + placementCost, this, isRightToLeft ) ); // Line } } else if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Horizontal ) @@ -925,18 +925,18 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList & if ( aboveLine ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) - positions.append( new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) ) + positions.append( new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line } if ( belowLine ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) - positions.append( new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) ) + positions.append( new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line } if ( flags & FLAG_ON_LINE ) { - if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle ) ) - positions.append( new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * sin( beta ) / 2, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line + if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle ) ) + positions.append( new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, cost, this, isRightToLeft ) ); // Line } } else if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Horizontal ) @@ -1098,7 +1098,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d flip = true; } start_x += dist * std::cos( angle + M_PI_2 ); - start_y -= dist * sin( angle + M_PI_2 ); + start_y -= dist * std::sin( angle + M_PI_2 ); double render_angle = angle; @@ -1113,7 +1113,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d { // rotate in place render_x += ci.width * std::cos( render_angle ); //- (string_height-2)*sin(render_angle); - render_y -= ci.width * sin( render_angle ); //+ (string_height-2)*cos(render_angle); + render_y -= ci.width * std::sin( render_angle ); //+ (string_height-2)*cos(render_angle); render_angle += M_PI; } @@ -1140,7 +1140,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d static LabelPosition *_createCurvedCandidate( LabelPosition *lp, double angle, double dist ) { LabelPosition *newLp = new LabelPosition( *lp ); - newLp->offsetPosition( dist * std::cos( angle + M_PI / 2 ), dist * sin( angle + M_PI / 2 ) ); + newLp->offsetPosition( dist * std::cos( angle + M_PI / 2 ), dist * std::sin( angle + M_PI / 2 ) ); return newLp; } @@ -1229,7 +1229,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos angle_diff += diff; } - sin_avg += sin( tmp->getAlpha() ); + sin_avg += std::sin( tmp->getAlpha() ); cos_avg += std::cos( tmp->getAlpha() ); angle_last = tmp->getAlpha(); tmp = tmp->getNextPart(); @@ -1453,7 +1453,7 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin // delta from label center and down-left corner dlx = std::cos( beta ) * diago; - dly = sin( beta ) * diago; + dly = std::sin( beta ) * diago; double px0, py0; @@ -1469,7 +1469,7 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin { rx = std::cos( box->alpha ) * px + std::cos( box->alpha - M_PI / 2 ) * py; - ry = sin( box->alpha ) * px + sin( box->alpha - M_PI / 2 ) * py; + ry = std::sin( box->alpha ) * px + std::sin( box->alpha - M_PI / 2 ) * py; rx += box->x[0]; ry += box->y[0]; diff --git a/src/core/pal/geomfunction.cpp b/src/core/pal/geomfunction.cpp index 216adbbe27c..1fab4bb27f8 100644 --- a/src/core/pal/geomfunction.cpp +++ b/src/core/pal/geomfunction.cpp @@ -331,9 +331,9 @@ bool GeomFunction::containsCandidate( const GEOSPreparedGeometry *geom, double x { double beta = alpha + ( M_PI / 2 ); double dx1 = std::cos( alpha ) * width; - double dy1 = sin( alpha ) * width; + double dy1 = std::sin( alpha ) * width; double dx2 = std::cos( beta ) * height; - double dy2 = sin( beta ) * height; + double dy2 = std::sin( beta ) * height; GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + dx1 ); GEOSCoordSeq_setY_r( geosctxt, coord, 1, y + dy1 ); GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + dx1 + dx2 ); diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index 16c06773a12..b43384b58f1 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -79,10 +79,10 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double dx1, dx2, dy1, dy2; dx1 = std::cos( this->alpha ) * w; - dy1 = sin( this->alpha ) * w; + dy1 = std::sin( this->alpha ) * w; dx2 = std::cos( beta ) * h; - dy2 = sin( beta ) * h; + dy2 = std::sin( beta ) * h; x[0] = x1; y[0] = y1; diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index 54c1ab325e0..f48586fe6e5 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -628,7 +628,7 @@ CHullBox *PointSet::compute_chull_bbox() { alpha = alpha_d * M_PI / 180.0; d1 = std::cos( alpha ) * dref; - d2 = sin( alpha ) * dref; + d2 = std::sin( alpha ) * dref; bb[0] = bbox[0]; bb[1] = bbox[3]; // ax, ay @@ -671,7 +671,7 @@ CHullBox *PointSet::compute_chull_bbox() distNearestPoint = best_cp / dref; d1 = std::cos( alpha_seg ) * distNearestPoint; - d2 = sin( alpha_seg ) * distNearestPoint; + d2 = std::sin( alpha_seg ) * distNearestPoint; bb[i] += d1; // x bb[i + 1] += d2; // y diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index edd328080c5..8d5720cc405 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -427,7 +427,7 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( azimuth = azimuth - M_PI * 2.0; } sigma1 = std::atan2( tan_u1, std::cos( azimuth ) ); - sin_alpha = std::cos( u1 ) * sin( azimuth ); + sin_alpha = std::cos( u1 ) * std::sin( azimuth ); alpha = asin( sin_alpha ); cos_alphasq = 1.0 - POW2( sin_alpha ); u2 = POW2( std::cos( alpha ) ) * ( POW2( a ) - b2 ) / b2; // spheroid_mu2 @@ -437,21 +437,21 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( do { two_sigma_m = 2.0 * sigma1 + sigma; - delta_sigma = B * sin( sigma ) * ( std::cos( two_sigma_m ) + ( B / 4.0 ) * ( std::cos( sigma ) * ( -1.0 + 2.0 * POW2( std::cos( two_sigma_m ) ) - ( B / 6.0 ) * std::cos( two_sigma_m ) * ( -3.0 + 4.0 * POW2( sin( sigma ) ) ) * ( -3.0 + 4.0 * POW2( std::cos( two_sigma_m ) ) ) ) ) ); + delta_sigma = B * std::sin( sigma ) * ( std::cos( two_sigma_m ) + ( B / 4.0 ) * ( std::cos( sigma ) * ( -1.0 + 2.0 * POW2( std::cos( two_sigma_m ) ) - ( B / 6.0 ) * std::cos( two_sigma_m ) * ( -3.0 + 4.0 * POW2( std::sin( sigma ) ) ) * ( -3.0 + 4.0 * POW2( std::cos( two_sigma_m ) ) ) ) ) ); last_sigma = sigma; sigma = ( distance / ( b * A ) ) + delta_sigma; i++; } while ( i < 999 && std::fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); - lat2 = std::atan2( ( sin( u1 ) * std::cos( sigma ) + std::cos( u1 ) * sin( sigma ) * + lat2 = std::atan2( ( std::sin( u1 ) * std::cos( sigma ) + std::cos( u1 ) * std::sin( sigma ) * std::cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + - POW2( sin( u1 ) * sin( sigma ) - std::cos( u1 ) * std::cos( sigma ) * + POW2( std::sin( u1 ) * std::sin( sigma ) - std::cos( u1 ) * std::cos( sigma ) * std::cos( azimuth ) ) ) ) ); - lambda = std::atan2( ( sin( sigma ) * sin( azimuth ) ), ( std::cos( u1 ) * std::cos( sigma ) - - sin( u1 ) * sin( sigma ) * std::cos( azimuth ) ) ); + lambda = std::atan2( ( std::sin( sigma ) * std::sin( azimuth ) ), ( std::cos( u1 ) * std::cos( sigma ) - + std::sin( u1 ) * std::sin( sigma ) * std::cos( azimuth ) ) ); C = ( f / 16.0 ) * cos_alphasq * ( 4.0 + f * ( 4.0 - 3.0 * cos_alphasq ) ); - omega = lambda - ( 1.0 - C ) * f * sin_alpha * ( sigma + C * sin( sigma ) * + omega = lambda - ( 1.0 - C ) * f * sin_alpha * ( sigma + C * std::sin( sigma ) * ( std::cos( two_sigma_m ) + C * std::cos( sigma ) * ( -1.0 + 2.0 * POW2( std::cos( two_sigma_m ) ) ) ) ); lambda2 = radians_long + omega; return QgsPointXY( RAD2DEG( lambda2 ), RAD2DEG( lat2 ) ); @@ -553,8 +553,8 @@ double QgsDistanceArea::computeDistanceBearing( double L = p2_lon - p1_lon; double U1 = atan( ( 1 - f ) * tan( p1_lat ) ); double U2 = atan( ( 1 - f ) * tan( p2_lat ) ); - double sinU1 = sin( U1 ), cosU1 = std::cos( U1 ); - double sinU2 = sin( U2 ), cosU2 = std::cos( U2 ); + double sinU1 = std::sin( U1 ), cosU1 = std::cos( U1 ); + double sinU2 = std::sin( U2 ), cosU2 = std::cos( U2 ); double lambda = L; double lambdaP = 2 * M_PI; @@ -573,7 +573,7 @@ double QgsDistanceArea::computeDistanceBearing( int iterLimit = 20; while ( std::fabs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 ) { - sinLambda = sin( lambda ); + sinLambda = std::sin( lambda ); cosLambda = std::cos( lambda ); tu1 = ( cosU2 * sinLambda ); tu2 = ( cosU1 * sinU2 - sinU1 * cosU2 * cosLambda ); @@ -585,7 +585,7 @@ double QgsDistanceArea::computeDistanceBearing( cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; C = f / 16 * cosSqAlpha * ( 4 + f * ( 4 - 3 * cosSqAlpha ) ); lambdaP = lambda; - lambda = L + ( 1 - C ) * f * sin( alpha ) * + lambda = L + ( 1 - C ) * f * std::sin( alpha ) * ( sigma + C * sinSigma * ( cos2SigmaM + C * cosSigma * ( -1 + 2 * cos2SigmaM * cos2SigmaM ) ) ); } @@ -621,7 +621,7 @@ double QgsDistanceArea::getQ( double x ) const { double sinx, sinx2; - sinx = sin( x ); + sinx = std::sin( x ); sinx2 = sinx * sinx; return sinx * ( 1 + sinx2 * ( m_QA + sinx2 * ( m_QB + sinx2 * m_QC ) ) ); diff --git a/src/core/qgsmapsettingsutils.cpp b/src/core/qgsmapsettingsutils.cpp index 7012b3fd643..6bccd819a1f 100644 --- a/src/core/qgsmapsettingsutils.cpp +++ b/src/core/qgsmapsettingsutils.cpp @@ -94,10 +94,10 @@ QString QgsMapSettingsUtils::worldFileContent( const QgsMapSettings &mapSettings double r[6]; r[0] = std::cos( alpha ); r[1] = -sin( alpha ); - r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * sin( alpha ); - r[3] = sin( alpha ); + r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha ); + r[3] = std::sin( alpha ); r[4] = std::cos( alpha ); - r[5] = - xCenter * sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) ); + r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) ); // result = rotation x scaling = rotation(scaling(X)) double a = r[0] * s[0] + r[1] * s[3]; diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index afc5e0ee2a4..f90a650b1d5 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -1782,8 +1782,8 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont if ( dataDefinedRotation ) { //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center - double xd = xdiff * std::cos( angle ) - ydiff * sin( angle ); - double yd = xdiff * sin( angle ) + ydiff * std::cos( angle ); + double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle ); + double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle ); xdiff = xd; ydiff = yd; } diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index 47510f5a7a7..a7ffde758cb 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -291,7 +291,7 @@ double QgsPointXY::azimuth( const QgsPointXY &other ) const QgsPointXY QgsPointXY::project( double distance, double bearing ) const { double rads = bearing * M_PI / 180.0; - double dx = distance * sin( rads ); + double dx = distance * std::sin( rads ); double dy = distance * std::cos( rads ); return QgsPointXY( mX + dx, mY + dy ); } diff --git a/src/core/qgsscalecalculator.cpp b/src/core/qgsscalecalculator.cpp index 2bf8b3bd508..9ff82857b52 100644 --- a/src/core/qgsscalecalculator.cpp +++ b/src/core/qgsscalecalculator.cpp @@ -123,7 +123,7 @@ double QgsScaleCalculator::calculateGeographicDistance( const QgsRectangle &mapE // to 6357000 m. static const double E = 0.0810820288; double radius = RA * ( 1.0 - E * E ) / - std::pow( 1.0 - E * E * sin( lat * RADS ) * sin( lat * RADS ), 1.5 ); + std::pow( 1.0 - E * E * std::sin( lat * RADS ) * std::sin( lat * RADS ), 1.5 ); double meters = ( mapExtent.xMaximum() - mapExtent.xMinimum() ) / 180.0 * radius * c; QgsDebugMsgLevel( "Distance across map extent (m): " + QString::number( meters ), 4 ); diff --git a/src/core/qgstextrenderer.cpp b/src/core/qgstextrenderer.cpp index 48e65fbba5c..c1e2bcc8a3b 100644 --- a/src/core/qgstextrenderer.cpp +++ b/src/core/qgstextrenderer.cpp @@ -1745,8 +1745,8 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, HAlignment double yc = rect.height() / 2.0; double angle = -rotation; - double xd = xc * std::cos( angle ) - yc * sin( angle ); - double yd = xc * sin( angle ) + yc * std::cos( angle ); + double xd = xc * std::cos( angle ) - yc * std::sin( angle ); + double yd = xc * std::sin( angle ) + yc * std::cos( angle ); component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd ); } @@ -2389,7 +2389,7 @@ void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRender } QPointF transPt( -offsetDist * std::cos( angleRad + M_PI / 2 ), - -offsetDist * sin( angleRad + M_PI / 2 ) ); + -offsetDist * std::sin( angleRad + M_PI / 2 ) ); p->save(); p->setRenderHint( QPainter::SmoothPixmapTransform ); diff --git a/src/core/qgsvector.cpp b/src/core/qgsvector.cpp index b70ac386851..ac2c48fddd8 100644 --- a/src/core/qgsvector.cpp +++ b/src/core/qgsvector.cpp @@ -109,7 +109,7 @@ QgsVector QgsVector::rotateBy( double rot ) const { double angle = std::atan2( mY, mX ) + rot; double len = length(); - return QgsVector( len * std::cos( angle ), len * sin( angle ) ); + return QgsVector( len * std::cos( angle ), len * std::sin( angle ) ); } QgsVector QgsVector::normalized() const diff --git a/src/core/qgsvectorlayerlabelprovider.cpp b/src/core/qgsvectorlayerlabelprovider.cpp index 0067d0ba80e..283e545934d 100644 --- a/src/core/qgsvectorlayerlabelprovider.cpp +++ b/src/core/qgsvectorlayerlabelprovider.cpp @@ -509,8 +509,8 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q double yc = outPt2.y() - outPt.y(); double angle = -component.rotation; - double xd = xc * std::cos( angle ) - yc * sin( angle ); - double yd = xc * sin( angle ) + yc * std::cos( angle ); + double xd = xc * std::cos( angle ) - yc * std::sin( angle ); + double yd = xc * std::sin( angle ) + yc * std::cos( angle ); centerPt.setX( centerPt.x() + xd ); centerPt.setY( centerPt.y() + yd ); diff --git a/src/core/raster/qgshillshaderenderer.cpp b/src/core/raster/qgshillshaderenderer.cpp index 668d040877d..257b6385d34 100644 --- a/src/core/raster/qgshillshaderenderer.cpp +++ b/src/core/raster/qgshillshaderenderer.cpp @@ -127,7 +127,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext double zenithRad = qMax( 0.0, 90 - mLightAngle ) * M_PI / 180.0; double azimuthRad = -1 * mLightAzimuth * M_PI / 180.0; double cosZenithRad = std::cos( zenithRad ); - double sinZenithRad = sin( zenithRad ); + double sinZenithRad = std::sin( zenithRad ); // Multi direction hillshade: http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf double angle0Rad = ( -1 * mLightAzimuth - 45 - 45 * 0.5 ) * M_PI / 180.0; @@ -219,23 +219,23 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext { // Standard single direction hillshade grayValue = qBound( 0.0, 255.0 * ( cosZenithRad * std::cos( slopeRad ) - + sinZenithRad * sin( slopeRad ) + + sinZenithRad * std::sin( slopeRad ) * std::cos( azimuthRad - aspectRad ) ), 255.0 ); } else { // Weighted multi direction as in http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf - double weight0 = sin( aspectRad - angle0Rad ); - double weight1 = sin( aspectRad - angle1Rad ); - double weight2 = sin( aspectRad - angle2Rad ); - double weight3 = sin( aspectRad - angle3Rad ); + double weight0 = std::sin( aspectRad - angle0Rad ); + double weight1 = std::sin( aspectRad - angle1Rad ); + double weight2 = std::sin( aspectRad - angle2Rad ); + double weight3 = std::sin( aspectRad - angle3Rad ); weight0 = weight0 * weight0; weight1 = weight1 * weight1; weight2 = weight2 * weight2; weight3 = weight3 * weight3; double cosSlope = cosZenithRad * std::cos( slopeRad ); - double sinSlope = sinZenithRad * sin( slopeRad ); + double sinSlope = sinZenithRad * std::sin( slopeRad ); double color0 = cosSlope + sinSlope * std::cos( angle0Rad - aspectRad ) ; double color1 = cosSlope + sinSlope * std::cos( angle1Rad - aspectRad ) ; double color2 = cosSlope + sinSlope * std::cos( angle2Rad - aspectRad ) ; diff --git a/src/core/symbology/qgs25drenderer.cpp b/src/core/symbology/qgs25drenderer.cpp index 808f7a11f01..cca1b31628d 100644 --- a/src/core/symbology/qgs25drenderer.cpp +++ b/src/core/symbology/qgs25drenderer.cpp @@ -24,7 +24,7 @@ #define ROOF_EXPRESSION \ "translate(" \ " $geometry," \ - " std::cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ + " cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ " sin( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )" \ ")" @@ -32,10 +32,10 @@ "order_parts( "\ " extrude(" \ " segments_to_lines( $geometry )," \ - " std::cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ + " cos( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )," \ " sin( radians( eval( @qgis_25d_angle ) ) ) * eval( @qgis_25d_height )" \ " )," \ - " 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * std::cos( radians( @qgis_25d_angle + 180 ) ), 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) ) ))'," \ + " 'distance( $geometry, translate( @map_extent_center, 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 ) ), 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) ) ))'," \ " False" \ ")" @@ -44,7 +44,7 @@ " $geometry," \ " translate(" \ " @map_extent_center," \ - " 1000 * @map_extent_width * std::cos( radians( @qgis_25d_angle + 180 ) )," \ + " 1000 * @map_extent_width * cos( radians( @qgis_25d_angle + 180 ) )," \ " 1000 * @map_extent_width * sin( radians( @qgis_25d_angle + 180 ) )" \ " )" \ ")" @@ -53,7 +53,7 @@ "set_color_part( " \ " @symbol_color," \ " 'value'," \ - " 40 + 19 * std::abs( $pi - azimuth( " \ + " 40 + 19 * abs( $pi - azimuth( " \ " point_n( geometry_n($geometry, @geometry_part_num) , 1 ), " \ " point_n( geometry_n($geometry, @geometry_part_num) , 2 )" \ " ) ) " \ diff --git a/src/core/symbology/qgsarrowsymbollayer.cpp b/src/core/symbology/qgsarrowsymbollayer.cpp index 7a30aaa11bc..e2702cdefad 100644 --- a/src/core/symbology/qgsarrowsymbollayer.cpp +++ b/src/core/symbology/qgsarrowsymbollayer.cpp @@ -387,7 +387,7 @@ bool pointsToCircle( QPointF a, QPointF b, QPointF c, QPointF ¢er, qreal &ra QPointF circlePoint( QPointF center, qreal radius, qreal angle ) { // Y is oriented downward - return QPointF( std::cos( -angle ) * radius + center.x(), sin( -angle ) * radius + center.y() ); + return QPointF( std::cos( -angle ) * radius + center.x(), std::sin( -angle ) * radius + center.y() ); } void pathArcTo( QPainterPath &path, QPointF circleCenter, qreal circleRadius, qreal angle_o, qreal angle_d, int direction ) diff --git a/src/core/symbology/qgsellipsesymbollayer.cpp b/src/core/symbology/qgsellipsesymbollayer.cpp index 426fec09d1e..13c9126b033 100644 --- a/src/core/symbology/qgsellipsesymbollayer.cpp +++ b/src/core/symbology/qgsellipsesymbollayer.cpp @@ -806,7 +806,7 @@ bool QgsEllipseSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFact { double angle = stepsize * i; double x = halfWidth * std::cos( angle ); - double y = halfHeight * sin( angle ); + double y = halfHeight * std::sin( angle ); line << QgsPoint( t.map( QPointF( x, y ) ) ); } //close ellipse with first point diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index c0bfd5ac851..6bcd4122394 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -2591,7 +2591,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & else { height = outputPixelDist / std::cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant - width = outputPixelDist / sin( lineAngle * M_PI / 180 ); + width = outputPixelDist / std::sin( lineAngle * M_PI / 180 ); // recalculate real angle and distance after rounding to pixels lineAngle = 180 * std::atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI; @@ -2657,7 +2657,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & else if ( lineAngle > 0 && lineAngle < 90 ) { dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 ); - dy = outputPixelDist * sin( ( 90 - lineAngle ) * M_PI / 180.0 ); + dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 ); p1 = QPointF( 0, height ); p2 = QPointF( width, 0 ); p3 = QPointF( -dx, height - dy ); @@ -2668,7 +2668,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & else if ( lineAngle > 180 && lineAngle < 270 ) { dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 ); - dy = outputPixelDist * sin( ( 90 - lineAngle ) * M_PI / 180.0 ); + dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 ); p1 = QPointF( width, 0 ); p2 = QPointF( 0, height ); p3 = QPointF( width - dx, -dy ); @@ -2679,7 +2679,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & else if ( lineAngle > 90 && lineAngle < 180 ) { dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 ); - dx = outputPixelDist * sin( ( 180 - lineAngle ) * M_PI / 180 ); + dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 ); p1 = QPointF( 0, 0 ); p2 = QPointF( width, height ); p5 = QPointF( dx, -dy ); @@ -2690,7 +2690,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & else if ( lineAngle > 270 && lineAngle < 360 ) { dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 ); - dx = outputPixelDist * sin( ( 180 - lineAngle ) * M_PI / 180 ); + dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 ); p1 = QPointF( width, height ); p2 = QPointF( 0, 0 ); p5 = QPointF( width + dx, height - dy ); @@ -2880,7 +2880,7 @@ void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &eleme QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc ); // - QPointF lineOffset( sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset ); + QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset ); lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props ); QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset ); } diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 220d44ab5f1..b9b06c4b4a3 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -687,7 +687,7 @@ class MyLine double alpha = atan( mT ); double dx = std::cos( alpha ) * interval; - double dy = sin( alpha ) * interval; + double dy = std::sin( alpha ) * interval; return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) ); } @@ -1002,7 +1002,7 @@ static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt ) // calc average angle between the previous and next point double a1 = MyLine( prevPt, pt ).angle(); double a2 = MyLine( pt, nextPt ).angle(); - double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = sin( a1 ) + sin( a2 ); + double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = std::sin( a1 ) + std::sin( a2 ); return std::atan2( unitY, unitX ); } diff --git a/src/core/symbology/qgsmarkersymbollayer.cpp b/src/core/symbology/qgsmarkersymbollayer.cpp index 26646cc00ee..07dcdba31ae 100644 --- a/src/core/symbology/qgsmarkersymbollayer.cpp +++ b/src/core/symbology/qgsmarkersymbollayer.cpp @@ -419,10 +419,10 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB case Pentagon: /* angular-representation of hardcoded values used - polygon << QPointF( sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) ) - << QPointF( sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) - << QPointF( sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) - << QPointF( sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) + polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) << QPointF( 0, -1 ); */ polygon << QPointF( -0.9511, -0.3090 ) << QPointF( -0.5878, 0.8090 ) @@ -434,11 +434,11 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB case Hexagon: /* angular-representation of hardcoded values used - polygon << QPointF( sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) ) - << QPointF( sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) ) - << QPointF( sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) ) - << QPointF( sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) ) - << QPointF( sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) ) + polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) ) << QPointF( 0, -1 ); */ polygon << QPointF( -0.8660, -0.5 ) << QPointF( -0.8660, 0.5 ) @@ -455,8 +455,8 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB case EquilateralTriangle: /* angular-representation of hardcoded values used - polygon << QPointF( sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) ) - << QPointF( sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) ) + polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) ) + << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) ) << QPointF( 0, -1 ); */ polygon << QPointF( -0.8660, 0.5 ) << QPointF( 0.8660, 0.5 ) @@ -476,17 +476,17 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB { double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) ); - polygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324 - << QPointF( sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288 - << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252 - << QPointF( sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216 + polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324 + << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288 + << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252 + << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216 << QPointF( 0, inner_r ) // 180 - << QPointF( sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144 - << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108 - << QPointF( sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72 - << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36 + << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144 + << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108 + << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72 + << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36 << QPointF( 0, -1 ) - << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0 + << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0 return true; } diff --git a/src/core/symbology/qgspointdisplacementrenderer.cpp b/src/core/symbology/qgspointdisplacementrenderer.cpp index 8cca104c085..d929f0c7197 100644 --- a/src/core/symbology/qgspointdisplacementrenderer.cpp +++ b/src/core/symbology/qgspointdisplacementrenderer.cpp @@ -257,7 +257,7 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRe double angleStep = fullPerimeter / nPosition; for ( double currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep ) { - double sinusCurrentAngle = sin( currentAngle ); + double sinusCurrentAngle = std::sin( currentAngle ); double cosinusCurrentAngle = std::cos( currentAngle ); QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle ); QPointF labelShift( ( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle ); @@ -286,7 +286,7 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRe double currentAngle = 0.0; for ( int i = 0; i < actualPointsCurrentRing; ++i ) { - double sinusCurrentAngle = sin( currentAngle ); + double sinusCurrentAngle = std::sin( currentAngle ); double cosinusCurrentAngle = std::cos( currentAngle ); QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle ); QPointF labelShift( ( radiusCurrentRing + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radiusCurrentRing + symbolDiagonal / 2 ) * cosinusCurrentAngle ); diff --git a/src/core/symbology/qgssymbollayer.cpp b/src/core/symbology/qgssymbollayer.cpp index 43725603565..48f6a55b2bb 100644 --- a/src/core/symbology/qgssymbollayer.cpp +++ b/src/core/symbology/qgssymbollayer.cpp @@ -508,7 +508,7 @@ void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double QPointF QgsMarkerSymbolLayer::_rotatedOffset( QPointF offset, double angle ) { angle = DEG2RAD( angle ); - double c = std::cos( angle ), s = sin( angle ); + double c = std::cos( angle ), s = std::sin( angle ); return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c ); } diff --git a/src/core/symbology/qgsvectorfieldsymbollayer.cpp b/src/core/symbology/qgsvectorfieldsymbollayer.cpp index 17604a74bbb..c0cc309cfc4 100644 --- a/src/core/symbology/qgsvectorfieldsymbollayer.cpp +++ b/src/core/symbology/qgsvectorfieldsymbollayer.cpp @@ -313,7 +313,7 @@ void QgsVectorFieldSymbolLayer::convertPolarToCartesian( double length, double a angle = angle * M_PI / 180.0; } - x = length * sin( angle ); + x = length * std::sin( angle ); y = length * std::cos( angle ); } diff --git a/src/gui/qgsadvanceddigitizingcanvasitem.cpp b/src/gui/qgsadvanceddigitizingcanvasitem.cpp index 9aa3b76edcf..00c9e90cd1d 100644 --- a/src/gui/qgsadvanceddigitizingcanvasitem.cpp +++ b/src/gui/qgsadvanceddigitizingcanvasitem.cpp @@ -145,16 +145,16 @@ void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter ) painter->drawLine( prevPointPix.x(), prevPointPix.y(), prevPointPix.x() + 60 * std::cos( a0 ), - prevPointPix.y() + 60 * qSin( a0 ) ); + prevPointPix.y() + 60 * std::sin( a0 ) ); if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() ) { painter->setPen( mLockedPen ); double d = std::max( boundingRect().width(), boundingRect().height() ); painter->drawLine( prevPointPix.x() - d * std::cos( a ), - prevPointPix.y() - d * qSin( a ), + prevPointPix.y() - d * std::sin( a ), prevPointPix.x() + d * std::cos( a ), - prevPointPix.y() + d * qSin( a ) ); + prevPointPix.y() + d * std::sin( a ) ); } } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 1930074d6cd..639c8cc18cf 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -663,7 +663,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1 const double dist = std::fabs( std::cos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) - - qSin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) ); + - std::sin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) ); if ( dist / mMapCanvas->mapSettings().mapUnitsPerPixel() < SOFT_CONSTRAINT_TOLERANCE_PIXEL ) { mAngleConstraint->setLockMode( CadConstraint::SoftLock ); @@ -682,7 +682,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) } double cosa = std::cos( angleValue ); - double sina = qSin( angleValue ); + double sina = std::sin( angleValue ); double v = ( point.x() - previousPt.x() ) * cosa + ( point.y() - previousPt.y() ) * sina ; if ( mXConstraint->isLocked() && mYConstraint->isLocked() ) { diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index 24464d0df70..f43ae1e651f 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -460,7 +460,7 @@ void QgsColorWheel::paintEvent( QPaintEvent *event ) double sx = -cos( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; double sy = -sin( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; double vx = -cos( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; - double vy = sin( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; + double vy = std::sin( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; double mx = ( sx + vx ) / 2.0; double my = ( sy + vy ) / 2.0; @@ -683,9 +683,9 @@ void QgsColorWheel::createTriangle() alphaColor.setAlpha( 0 ); //some rather ugly shortcuts to obtain corners and midpoints of triangle - QLineF line1 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() - triangleRadius * sin( M_PI / 3.0 ) ); + QLineF line1 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() - triangleRadius * std::sin( M_PI / 3.0 ) ); QLineF line2 = QLineF( center.x(), center.y(), center.x() + triangleRadius, center.y() ); - QLineF line3 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() + triangleRadius * sin( M_PI / 3.0 ) ); + QLineF line3 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() + triangleRadius * std::sin( M_PI / 3.0 ) ); QLineF line4 = QLineF( center.x(), center.y(), center.x() - triangleRadius * std::cos( M_PI / 3.0 ), center.y() ); QLineF line5 = QLineF( center.x(), center.y(), ( line2.p2().x() + line1.p2().x() ) / 2.0, ( line2.p2().y() + line1.p2().y() ) / 2.0 ); line1.setAngle( line1.angle() + angle ); diff --git a/src/plugins/georeferencer/qgsgeorefplugingui.cpp b/src/plugins/georeferencer/qgsgeorefplugingui.cpp index bfa5cf6da63..641ddd569b8 100644 --- a/src/plugins/georeferencer/qgsgeorefplugingui.cpp +++ b/src/plugins/georeferencer/qgsgeorefplugingui.cpp @@ -1436,8 +1436,8 @@ bool QgsGeorefPluginGui::writeWorldFile( const QgsPointXY &origin, double pixelX if ( !qgsDoubleNear( rotation, 0.0 ) ) { - rotationX = pixelXSize * sin( rotation ); - rotationY = pixelYSize * sin( rotation ); + rotationX = pixelXSize * std::sin( rotation ); + rotationY = pixelYSize * std::sin( rotation ); pixelXSize *= std::cos( rotation ); pixelYSize *= std::cos( rotation ); } diff --git a/src/plugins/georeferencer/qgsgeoreftransform.cpp b/src/plugins/georeferencer/qgsgeoreftransform.cpp index 335a1af0872..81ff4eb4a91 100644 --- a/src/plugins/georeferencer/qgsgeoreftransform.cpp +++ b/src/plugins/georeferencer/qgsgeoreftransform.cpp @@ -440,7 +440,7 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs if ( !t ) return false; - double a = std::cos( t->angle ), b = sin( t->angle ), x0 = t->origin.x(), y0 = t->origin.y(), s = t->scale; + double a = std::cos( t->angle ), b = std::sin( t->angle ), x0 = t->origin.x(), y0 = t->origin.y(), s = t->scale; if ( !bDstToSrc ) { a *= s; @@ -450,8 +450,8 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs double xT = x[i], yT = y[i]; // Because rotation parameters where estimated in a CS with negative y-axis ^= down. // we need to apply the rotation matrix and a change of base: - // |cos a,-sin a| |1, 0| | std::cos a, sin a| - // |sin a, std::cos a| |0,-1| = | sin a, -cos a| + // |cos a,-sin a| |1, 0| | std::cos a, std::sin a| + // |sin a, std::cos a| |0,-1| = | std::sin a, -cos a| x[i] = x0 + ( a * xT + b * yT ); y[i] = y0 + ( b * xT - a * yT ); panSuccess[i] = true; @@ -475,8 +475,8 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs double xT = x[i], yT = y[i]; xT -= x0; yT -= y0; - // | std::cos a, sin a |^-1 |cos a, sin a| - // | sin a, -cos a | = |sin a, -cos a| + // | std::cos a, std::sin a |^-1 |cos a, std::sin a| + // | std::sin a, -cos a | = |sin a, -cos a| x[i] = a * xT + b * yT; y[i] = b * xT - a * yT; panSuccess[i] = true; diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 92ab26ce4bb..ebec8caaf75 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -4177,7 +4177,7 @@ void TestQgsGeometry::circle() // byCenterPoint QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 5, 0 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 90 ) ); QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) ); - QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 5 * std::cos( 45 * M_PI / 180.0 ), 5 * sin( 45 * M_PI / 180.0 ) ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 45 ) ); + QVERIFY( QgsCircle().fromCenterPoint( QgsPoint( 0, 0 ), QgsPoint( 5 * std::cos( 45 * M_PI / 180.0 ), 5 * std::sin( 45 * M_PI / 180.0 ) ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 45 ) ); // by3Tangents // Tangents from triangle tri1( 0,0 ; 0,5 ), tri2( 0,0 ; 5,0 ), tri3( 5,0 ; 0,5 ) QgsCircle circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 2, 0 ), QgsPoint( 3, 0 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) ); @@ -4228,7 +4228,7 @@ void TestQgsGeometry::circle() // oriented //45 - double val = 5 * sin( M_PI / 4 ); + double val = 5 * std::sin( M_PI / 4 ); pol.reset( QgsCircle( QgsPoint( 0, 0 ), 5, 45 ).toPolygon( 4 ) ); QCOMPARE( pol->numInteriorRings(), 0 ); QCOMPARE( pol->exteriorRing()->numPoints(), 5 ); From b5ca4b4ea8331e7b2c208e83d1a9971c170b01e3 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:22:15 +1000 Subject: [PATCH 123/364] (q)sqrt -> std::sqrt --- .../interpolation/DualEdgeTriangulation.cc | 24 +++++++------- .../interpolation/LinTriangleInterpolator.cc | 2 +- src/analysis/interpolation/MathUtils.cc | 32 +++++++++---------- .../interpolation/qgsidwinterpolator.cpp | 2 +- src/analysis/raster/qgshillshadefilter.cpp | 2 +- src/analysis/raster/qgskde.cpp | 2 +- src/analysis/raster/qgsrastermatrix.cpp | 2 +- src/analysis/raster/qgsslopefilter.cpp | 2 +- src/analysis/vector/qgstransectsample.cpp | 10 +++--- src/app/dwg/libdxfrw/drw_base.h | 2 +- src/app/dwg/libdxfrw/drw_entities.cpp | 2 +- src/app/dwg/qgsdwgimporter.cpp | 6 ++-- src/app/nodetool/qgsnodeeditor.cpp | 2 +- src/app/nodetool/qgsnodetool.cpp | 6 ++-- src/app/qgisapp.cpp | 6 ++-- src/app/qgsmaptoolcircularstringradius.cpp | 2 +- src/app/qgsmaptoollabel.cpp | 4 +-- src/app/qgsmaptooloffsetcurve.cpp | 2 +- src/app/qgsmaptoolselectradius.cpp | 2 +- src/app/qgsmeasuredialog.cpp | 2 +- src/app/qgspointrotationitem.cpp | 2 +- src/app/qgsrastercalcdialog.cpp | 2 +- src/core/annotations/qgsannotation.cpp | 4 +-- src/core/composer/qgscomposerutils.cpp | 2 +- src/core/diagram/qgsdiagram.cpp | 6 ++-- src/core/diagram/qgstextdiagram.cpp | 2 +- src/core/effects/qgsimageoperation.cpp | 6 ++-- src/core/expression/qgsexpressionfunction.cpp | 2 +- src/core/geometry/qgscircularstring.cpp | 4 +-- src/core/geometry/qgsellipse.cpp | 8 ++--- src/core/geometry/qgsgeometryutils.cpp | 14 ++++---- .../geometry/qgsinternalgeometryengine.cpp | 2 +- src/core/geometry/qgslinestring.cpp | 14 ++++---- src/core/geometry/qgspolygon.cpp | 2 +- src/core/pal/feature.cpp | 16 +++++----- src/core/pal/geomfunction.cpp | 2 +- src/core/pal/labelposition.cpp | 2 +- src/core/pal/pointset.cpp | 4 +-- src/core/pal/rtree.hpp | 2 +- src/core/qgscoordinatetransform.cpp | 2 +- src/core/qgsdistancearea.cpp | 4 +-- src/core/qgspallabeling.cpp | 2 +- src/core/qgspointlocator.cpp | 4 +-- src/core/qgsscalecalculator.cpp | 2 +- src/core/qgssnappingutils.cpp | 4 +-- src/core/qgstextrenderer.cpp | 6 ++-- src/core/qgstolerance.cpp | 2 +- src/core/qgstracer.cpp | 2 +- src/core/qgsvectorlayerlabeling.cpp | 2 +- src/core/qgsvectorlayerrenderer.cpp | 4 +-- src/core/raster/qgshillshaderenderer.cpp | 2 +- src/core/raster/qgsrasterinterface.cpp | 6 ++-- src/core/raster/qgsrasterprojector.cpp | 4 +-- src/core/simplify/effectivearea.cpp | 2 +- src/core/symbology/qgsarrowsymbollayer.cpp | 2 +- src/core/symbology/qgsfillsymbollayer.cpp | 6 ++-- .../symbology/qgsgraduatedsymbolrenderer.cpp | 2 +- src/core/symbology/qgsheatmaprenderer.cpp | 2 +- src/core/symbology/qgslinesymbollayer.cpp | 10 +++--- src/core/symbology/qgsmarkersymbollayer.cpp | 10 +++--- src/core/symbology/qgssymbollayerutils.cpp | 2 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 14 ++++---- src/gui/qgscolorwidgets.cpp | 8 ++--- .../checks/qgsgeometrysegmentlengthcheck.cpp | 4 +-- .../utils/qgsgeometrycheckerutils.cpp | 4 +-- src/plugins/georeferencer/qgsgcplistmodel.cpp | 2 +- .../georeferencer/qgsgeorefplugingui.cpp | 4 +-- src/plugins/georeferencer/qgsleastsquares.cpp | 4 +-- .../georeferencer/qgsresidualplotitem.cpp | 2 +- src/plugins/grass/qgsgrassmapcalc.cpp | 20 ++++++------ src/plugins/topology/topolTest.cpp | 6 ++-- src/server/qgswmsprojectparser.cpp | 2 +- .../services/wms/qgswmsgetcapabilities.cpp | 2 +- tests/bench/qgsbench.cpp | 2 +- tests/src/core/testqgsgeometry.cpp | 6 ++-- tests/src/core/testqgsgeometryutils.cpp | 2 +- tests/src/core/testqgspoint.cpp | 2 +- tests/src/core/testqgspointlocator.cpp | 4 +-- 78 files changed, 190 insertions(+), 190 deletions(-) diff --git a/src/analysis/interpolation/DualEdgeTriangulation.cc b/src/analysis/interpolation/DualEdgeTriangulation.cc index 4e15fb7663b..06ddd24f005 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.cc +++ b/src/analysis/interpolation/DualEdgeTriangulation.cc @@ -1271,8 +1271,8 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) p3 = mHalfEdge[mHalfEdge[actedge]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[actedge]->getNext()]->getDual()]->getPoint(); MathUtils::lineIntersection( mPointVector[p1], mPointVector[p2], mPointVector[p3], mPointVector[p4], &crosspoint ); - double dista = sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); - double distb = sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); + double dista = std::sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); + double distb = std::sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); if ( dista <= distb ) { insertForcedSegment( p1, p3, breakline ); @@ -1293,8 +1293,8 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) p3 = mHalfEdge[mHalfEdge[actedge]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[actedge]->getNext()]->getDual()]->getPoint(); MathUtils::lineIntersection( mPointVector[p1], mPointVector[p2], mPointVector[p3], mPointVector[p4], &crosspoint ); - double distpart = sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); - double disttot = sqrt( ( mPointVector[p3]->x() - mPointVector[p4]->x() ) * ( mPointVector[p3]->x() - mPointVector[p4]->x() ) + ( mPointVector[p3]->y() - mPointVector[p4]->y() ) * ( mPointVector[p3]->y() - mPointVector[p4]->y() ) ); + double distpart = std::sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); + double disttot = std::sqrt( ( mPointVector[p3]->x() - mPointVector[p4]->x() ) * ( mPointVector[p3]->x() - mPointVector[p4]->x() ) + ( mPointVector[p3]->y() - mPointVector[p4]->y() ) * ( mPointVector[p3]->y() - mPointVector[p4]->y() ) ); float frac = distpart / disttot; if ( frac == 0 || frac == 1 )//just in case MathUtils::lineIntersection does not work as expected @@ -1359,8 +1359,8 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) p3 = mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); MathUtils::lineIntersection( mPointVector[p1], mPointVector[p2], mPointVector[p3], mPointVector[p4], &crosspoint ); - double dista = sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); - double distb = sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); + double dista = std::sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); + double distb = std::sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); if ( dista <= distb ) { insertForcedSegment( p1, p3, breakline ); @@ -1381,8 +1381,8 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) p3 = mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); MathUtils::lineIntersection( mPointVector[p1], mPointVector[p2], mPointVector[p3], mPointVector[p4], &crosspoint ); - double distpart = sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); - double disttot = sqrt( ( mPointVector[p3]->x() - mPointVector[p4]->x() ) * ( mPointVector[p3]->x() - mPointVector[p4]->x() ) + ( mPointVector[p3]->y() - mPointVector[p4]->y() ) * ( mPointVector[p3]->y() - mPointVector[p4]->y() ) ); + double distpart = std::sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); + double disttot = std::sqrt( ( mPointVector[p3]->x() - mPointVector[p4]->x() ) * ( mPointVector[p3]->x() - mPointVector[p4]->x() ) + ( mPointVector[p3]->y() - mPointVector[p4]->y() ) * ( mPointVector[p3]->y() - mPointVector[p4]->y() ) ); float frac = distpart / disttot; if ( frac == 0 || frac == 1 ) { @@ -1406,8 +1406,8 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) p3 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getNext()]->getPoint(); MathUtils::lineIntersection( mPointVector[p1], mPointVector[p2], mPointVector[p3], mPointVector[p4], &crosspoint ); - double dista = sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); - double distb = sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); + double dista = std::sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); + double distb = std::sqrt( ( crosspoint.x() - mPointVector[p4]->x() ) * ( crosspoint.x() - mPointVector[p4]->x() ) + ( crosspoint.y() - mPointVector[p4]->y() ) * ( crosspoint.y() - mPointVector[p4]->y() ) ); if ( dista <= distb ) { insertForcedSegment( p1, p3, breakline ); @@ -1428,8 +1428,8 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) p3 = mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getPoint(); p4 = mHalfEdge[mHalfEdge[mHalfEdge[mHalfEdge[crossedEdges.last()]->getDual()]->getNext()]->getNext()]->getPoint(); MathUtils::lineIntersection( mPointVector[p1], mPointVector[p2], mPointVector[p3], mPointVector[p4], &crosspoint ); - double distpart = sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); - double disttot = sqrt( ( mPointVector[p3]->x() - mPointVector[p4]->x() ) * ( mPointVector[p3]->x() - mPointVector[p4]->x() ) + ( mPointVector[p3]->y() - mPointVector[p4]->y() ) * ( mPointVector[p3]->y() - mPointVector[p4]->y() ) ); + double distpart = std::sqrt( ( crosspoint.x() - mPointVector[p3]->x() ) * ( crosspoint.x() - mPointVector[p3]->x() ) + ( crosspoint.y() - mPointVector[p3]->y() ) * ( crosspoint.y() - mPointVector[p3]->y() ) ); + double disttot = std::sqrt( ( mPointVector[p3]->x() - mPointVector[p4]->x() ) * ( mPointVector[p3]->x() - mPointVector[p4]->x() ) + ( mPointVector[p3]->y() - mPointVector[p4]->y() ) * ( mPointVector[p3]->y() - mPointVector[p4]->y() ) ); float frac = distpart / disttot; if ( frac == 0 || frac == 1 ) { diff --git a/src/analysis/interpolation/LinTriangleInterpolator.cc b/src/analysis/interpolation/LinTriangleInterpolator.cc index 21c62b0ad72..a158a1bf8c0 100644 --- a/src/analysis/interpolation/LinTriangleInterpolator.cc +++ b/src/analysis/interpolation/LinTriangleInterpolator.cc @@ -82,7 +82,7 @@ bool LinTriangleInterpolator::calcNormVec( double x, double y, Vector3D *vec ) if ( !calcFirstDerY( x, y, &vec2 ) ) {return false;} Vector3D vec3( vec1.getY()*vec2.getZ() - vec1.getZ()*vec2.getY(), vec1.getZ()*vec2.getX() - vec1.getX()*vec2.getZ(), vec1.getX()*vec2.getY() - vec1.getY()*vec2.getX() );//calculate vector product - double absvec3 = sqrt( vec3.getX() * vec3.getX() + vec3.getY() * vec3.getY() + vec3.getZ() * vec3.getZ() );//length of vec3 + double absvec3 = std::sqrt( vec3.getX() * vec3.getX() + vec3.getY() * vec3.getY() + vec3.getZ() * vec3.getZ() );//length of vec3 vec->setX( vec3.getX() / absvec3 );//standardize vec3 and assign it to vec vec->setY( vec3.getY() / absvec3 ); vec->setZ( vec3.getZ() / absvec3 ); diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index 2b81fa59c61..0cecf8f068e 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -119,8 +119,8 @@ bool MathUtils::circumcenter( QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint { if ( p1 && p2 && p3 && result ) { - double distp1p2 = sqrt( ( p1->x() - p2->x() ) * ( p1->x() - p2->x() ) + ( p1->y() - p2->y() ) * ( p1->y() - p2->y() ) ); - double distp2p3 = sqrt( ( p2->x() - p3->x() ) * ( p2->x() - p3->x() ) + ( p2->y() - p3->y() ) * ( p2->y() - p3->y() ) ); + double distp1p2 = std::sqrt( ( p1->x() - p2->x() ) * ( p1->x() - p2->x() ) + ( p1->y() - p2->y() ) * ( p1->y() - p2->y() ) ); + double distp2p3 = std::sqrt( ( p2->x() - p3->x() ) * ( p2->x() - p3->x() ) + ( p2->y() - p3->y() ) * ( p2->y() - p3->y() ) ); if ( distp1p2 > distp2p3 ) { //swap p1 and p3 to avoid round-off errors @@ -142,9 +142,9 @@ bool MathUtils::circumcenter( QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint #if 0 //debugging: test, if the distances from p1, p2, p3 to result are equal - double dist1 = sqrt( ( p1->getX() - result->getX() ) * ( p1->getX() - result->getX() ) + ( p1->getY() - result->getY() ) * ( p1->getY() - result->getY() ) ); - double dist2 = sqrt( ( p2->getX() - result->getX() ) * ( p2->getX() - result->getX() ) + ( p2->getY() - result->getY() ) * ( p2->getY() - result->getY() ) ); - double dist3 = sqrt( ( p3->getX() - result->getX() ) * ( p3->getX() - result->getX() ) + ( p3->getY() - result->getY() ) * ( p3->getY() - result->getY() ) ); + double dist1 = std::sqrt( ( p1->getX() - result->getX() ) * ( p1->getX() - result->getX() ) + ( p1->getY() - result->getY() ) * ( p1->getY() - result->getY() ) ); + double dist2 = std::sqrt( ( p2->getX() - result->getX() ) * ( p2->getX() - result->getX() ) + ( p2->getY() - result->getY() ) * ( p2->getY() - result->getY() ) ); + double dist3 = std::sqrt( ( p3->getX() - result->getX() ) * ( p3->getX() - result->getX() ) + ( p3->getY() - result->getY() ) * ( p3->getY() - result->getY() ) ); if ( dist1 - dist2 > 1 || dist2 - dist1 > 1 || dist1 - dist3 > 1 || dist3 - dist1 > 1 ) { @@ -180,9 +180,9 @@ bool MathUtils::circumcenter( QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint MathUtils::lineIntersection( &midpoint12, &helppoint1, &midpoint23, &helppoint2, result ); //debugging: test, if the distances from p1, p2, p3 to result are equal - double dist1 = sqrt( ( p1->getX() - result->getX() ) * ( p1->getX() - result->getX() ) + ( p1->getY() - result->getY() ) * ( p1->getY() - result->getY() ) ); - double dist2 = sqrt( ( p2->getX() - result->getX() ) * ( p2->getX() - result->getX() ) + ( p2->getY() - result->getY() ) * ( p2->getY() - result->getY() ) ); - double dist3 = sqrt( ( p3->getX() - result->getX() ) * ( p3->getX() - result->getX() ) + ( p3->getY() - result->getY() ) * ( p3->getY() - result->getY() ) ); + double dist1 = std::sqrt( ( p1->getX() - result->getX() ) * ( p1->getX() - result->getX() ) + ( p1->getY() - result->getY() ) * ( p1->getY() - result->getY() ) ); + double dist2 = std::sqrt( ( p2->getX() - result->getX() ) * ( p2->getX() - result->getX() ) + ( p2->getY() - result->getY() ) * ( p2->getY() - result->getY() ) ); + double dist3 = std::sqrt( ( p3->getX() - result->getX() ) * ( p3->getX() - result->getX() ) + ( p3->getY() - result->getY() ) * ( p3->getY() - result->getY() ) ); if ( dist1 - dist2 > 1 || dist2 - dist1 > 1 || dist1 - dist3 > 1 || dist3 - dist1 > 1 ) { @@ -210,7 +210,7 @@ double MathUtils::distPointFromLine( QgsPoint *thepoint, QgsPoint *p1, QgsPoint double a = normal.getX(); double b = normal.getY(); double c = -( normal.getX() * p2->x() + normal.getY() * p2->y() ); - double distance = std::fabs( ( a * thepoint->x() + b * thepoint->y() + c ) / ( sqrt( a * a + b * b ) ) ); + double distance = std::fabs( ( a * thepoint->x() + b * thepoint->y() + c ) / ( std::sqrt( a * a + b * b ) ) ); return distance; } else @@ -619,7 +619,7 @@ bool MathUtils::normalLeft( Vector3D *v1, Vector3D *result, double length ) return false; } - result->setX( ( -b + sqrt( d ) ) / ( 2 * a ) ); //take one of the two solutions of the quadratic equation + result->setX( ( -b + std::sqrt( d ) ) / ( 2 * a ) ); //take one of the two solutions of the quadratic equation result->setY( ( -result->getX()*v1->getX() ) / v1->getY() ); QgsPoint point1( 0, 0, 0 ); @@ -676,7 +676,7 @@ bool MathUtils::normalRight( Vector3D *v1, Vector3D *result, double length ) return false; } - result->setX( ( -b + sqrt( d ) ) / ( 2 * a ) ); //take one of the two solutions of the quadratic equation + result->setX( ( -b + std::sqrt( d ) ) / ( 2 * a ) ); //take one of the two solutions of the quadratic equation result->setY( ( -result->getX()*v1->getX() ) / v1->getY() ); QgsPoint point1( 0, 0, 0 ); @@ -787,7 +787,7 @@ bool MathUtils::normalMinDistance( Vector3D *tangent, Vector3D *target, Vector3D QgsDebugMsg( "warning, only complex solution of xg" ); return false; } - xg1 = sqrt( xgalpha1 ) * ( -yt * yw * xt + yt * yt * xw + xw * zt * zt - zt * xt * zw ); + xg1 = std::sqrt( xgalpha1 ) * ( -yt * yw * xt + yt * yt * xw + xw * zt * zt - zt * xt * zw ); xg2 = -sqrt( xgalpha1 ) * ( -yt * yw * xt + yt * yt * xw + xw * zt * zt - zt * xt * zw ); //calculate yg @@ -798,7 +798,7 @@ bool MathUtils::normalMinDistance( Vector3D *tangent, Vector3D *target, Vector3D return false; } yg1 = -sqrt( ygalpha1 ) * ( -yw * xt * xt - zt * zt * yw + zt * yt * zw + yt * xw * xt ); - yg2 = sqrt( ygalpha1 ) * ( -yw * xt * xt - zt * zt * yw + zt * yt * zw + yt * xw * xt ); + yg2 = std::sqrt( ygalpha1 ) * ( -yw * xt * xt - zt * zt * yw + zt * yt * zw + yt * xw * xt ); //calculate zg double zgalpha1 = 1 / ( 2 * xt * xt * yw * yw * zt * zt - 2 * zt * zt * zt * xt * zw * xw + yt * yt * yt * yt * zw * zw + yt * yt * zw * zw * zt * zt + xt * xt * yt * yt * xw * xw + xt * xt * yw * yw * yt * yt - 2 * xt * xt * xt * zt * zw * xw + yt * yt * yt * yt * xw * xw + yt * yt * yw * yw * zt * zt + 2 * xt * xt * yt * yt * zw * zw - 2 * yt * yt * yt * yw * zt * zw + zt * zt * xt * xt * zw * zw + zt * zt * zt * zt * xw * xw + xt * xt * zt * zt * xw * xw + 2 * zt * zt * xw * xw * yt * yt - 2 * xt * xt * yw * zt * yt * zw - 2 * xt * yt * yt * yt * xw * yw - 2 * xt * xt * xt * yw * yt * xw - 2 * xt * zt * zt * xw * yt * yw - 2 * xt * zt * xw * yt * yt * zw - 2 * yw * zt * zt * zt * yt * zw + xt * xt * xt * xt * yw * yw + yw * yw * zt * zt * zt * zt + xt * xt * xt * xt * zw * zw ); @@ -808,10 +808,10 @@ bool MathUtils::normalMinDistance( Vector3D *tangent, Vector3D *target, Vector3D return false; } zg1 = -sqrt( zgalpha1 ) * ( yt * yw * zt - yt * yt * zw + xw * zt * xt - xt * xt * zw ); - zg2 = sqrt( zgalpha1 ) * ( yt * yw * zt - yt * yt * zw + xw * zt * xt - xt * xt * zw ); + zg2 = std::sqrt( zgalpha1 ) * ( yt * yw * zt - yt * yt * zw + xw * zt * xt - xt * xt * zw ); - double distance1 = sqrt( ( xw - xg1 ) * ( xw - xg1 ) + ( yw - yg1 ) * ( yw - yg1 ) + ( zw - zg1 ) * ( zw - zg1 ) ); - double distance2 = sqrt( ( xw - xg2 ) * ( xw - xg2 ) + ( yw - yg2 ) * ( yw - yg2 ) + ( zw - zg2 ) * ( zw - zg2 ) ); + double distance1 = std::sqrt( ( xw - xg1 ) * ( xw - xg1 ) + ( yw - yg1 ) * ( yw - yg1 ) + ( zw - zg1 ) * ( zw - zg1 ) ); + double distance2 = std::sqrt( ( xw - xg2 ) * ( xw - xg2 ) + ( yw - yg2 ) * ( yw - yg2 ) + ( zw - zg2 ) * ( zw - zg2 ) ); if ( distance1 <= distance2 )//find out, which solution is the maximum and which the minimum { diff --git a/src/analysis/interpolation/qgsidwinterpolator.cpp b/src/analysis/interpolation/qgsidwinterpolator.cpp index 2c71ad8b0b1..904a5595da4 100644 --- a/src/analysis/interpolation/qgsidwinterpolator.cpp +++ b/src/analysis/interpolation/qgsidwinterpolator.cpp @@ -44,7 +44,7 @@ int QgsIDWInterpolator::interpolatePoint( double x, double y, double &result ) Q_FOREACH ( const vertexData &vertex_it, mCachedBaseData ) { - distance = sqrt( ( vertex_it.x - x ) * ( vertex_it.x - x ) + ( vertex_it.y - y ) * ( vertex_it.y - y ) ); + distance = std::sqrt( ( vertex_it.x - x ) * ( vertex_it.x - x ) + ( vertex_it.y - y ) * ( vertex_it.y - y ) ); if ( ( distance - 0 ) < std::numeric_limits::min() ) { result = vertex_it.z; diff --git a/src/analysis/raster/qgshillshadefilter.cpp b/src/analysis/raster/qgshillshadefilter.cpp index 1168f50d289..91a63012c35 100644 --- a/src/analysis/raster/qgshillshadefilter.cpp +++ b/src/analysis/raster/qgshillshadefilter.cpp @@ -38,7 +38,7 @@ float QgsHillshadeFilter::processNineCellWindow( float *x11, float *x21, float * } float zenith_rad = mLightAngle * M_PI / 180.0; - float slope_rad = atan( sqrt( derX * derX + derY * derY ) ); + float slope_rad = atan( std::sqrt( derX * derX + derY * derY ) ); float azimuth_rad = mLightAzimuth * M_PI / 180.0; float aspect_rad = 0; if ( derX == 0 && derY == 0 ) //aspect undefined, take a neutral value. Better solutions? diff --git a/src/analysis/raster/qgskde.cpp b/src/analysis/raster/qgskde.cpp index e234006a6df..eb06adf965c 100644 --- a/src/analysis/raster/qgskde.cpp +++ b/src/analysis/raster/qgskde.cpp @@ -181,7 +181,7 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::addFeature( const double pixelCentroidX = ( xPosition + xp + 0.5 ) * mPixelSize + mBounds.xMinimum(); double pixelCentroidY = ( yPosition + yp + 0.5 ) * mPixelSize + mBounds.yMinimum(); - double distance = sqrt( std::pow( pixelCentroidX - ( *pointIt ).x(), 2.0 ) + std::pow( pixelCentroidY - ( *pointIt ).y(), 2.0 ) ); + double distance = std::sqrt( std::pow( pixelCentroidX - ( *pointIt ).x(), 2.0 ) + std::pow( pixelCentroidY - ( *pointIt ).y(), 2.0 ) ); // is pixel outside search bandwidth of feature? if ( distance > radius ) diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index 8890fef4558..c2550c66b84 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -216,7 +216,7 @@ bool QgsRasterMatrix::oneArgumentOperation( OneArgOperator op ) } else { - mData[i] = sqrt( value ); + mData[i] = std::sqrt( value ); } break; case opSIN: diff --git a/src/analysis/raster/qgsslopefilter.cpp b/src/analysis/raster/qgsslopefilter.cpp index cd176312104..17789087880 100644 --- a/src/analysis/raster/qgsslopefilter.cpp +++ b/src/analysis/raster/qgsslopefilter.cpp @@ -34,6 +34,6 @@ float QgsSlopeFilter::processNineCellWindow( float *x11, float *x21, float *x31, return mOutputNodataValue; } - return atan( sqrt( derX * derX + derY * derY ) ) * 180.0 / M_PI; + return atan( std::sqrt( derX * derX + derY * derY ) ) * 180.0 / M_PI; } diff --git a/src/analysis/vector/qgstransectsample.cpp b/src/analysis/vector/qgstransectsample.cpp index 83e544e91a8..26c26140e9e 100644 --- a/src/analysis/vector/qgstransectsample.cpp +++ b/src/analysis/vector/qgstransectsample.cpp @@ -440,28 +440,28 @@ bool QgsTransectSample::closestSegmentPoints( const QgsGeometry &g1, const QgsGe if ( d1 <= d2 && d1 <= d3 && d1 <= d4 ) { - dist = sqrt( d1 ); + dist = std::sqrt( d1 ); pt1 = p11; pt2 = minDistPoint1; return true; } else if ( d2 <= d1 && d2 <= d3 && d2 <= d4 ) { - dist = sqrt( d2 ); + dist = std::sqrt( d2 ); pt1 = p12; pt2 = minDistPoint2; return true; } else if ( d3 <= d1 && d3 <= d2 && d3 <= d4 ) { - dist = sqrt( d3 ); + dist = std::sqrt( d3 ); pt1 = p21; pt2 = minDistPoint3; return true; } else { - dist = sqrt( d4 ); + dist = std::sqrt( d4 ); pt1 = p21; pt2 = minDistPoint4; return true; @@ -512,7 +512,7 @@ bool QgsTransectSample::closestSegmentPoints( const QgsGeometry &g1, const QgsGe pt1.sqrDistToSegment( p21.x(), p21.y(), p22.x(), p22.y(), pt2 ); } - dist = sqrt( pt1.sqrDist( pt2 ) ); + dist = std::sqrt( pt1.sqrDist( pt2 ) ); return true; } diff --git a/src/app/dwg/libdxfrw/drw_base.h b/src/app/dwg/libdxfrw/drw_base.h index 20755362c4b..15691cf399e 100644 --- a/src/app/dwg/libdxfrw/drw_base.h +++ b/src/app/dwg/libdxfrw/drw_base.h @@ -175,7 +175,7 @@ class DRW_Coord void unitize() { double dist; - dist = sqrt( x * x + y * y + z * z ); + dist = std::sqrt( x * x + y * y + z * z ); if ( dist > 0.0 ) { x = x / dist; diff --git a/src/app/dwg/libdxfrw/drw_entities.cpp b/src/app/dwg/libdxfrw/drw_entities.cpp index 6f136dd50dc..501509c5d08 100644 --- a/src/app/dwg/libdxfrw/drw_entities.cpp +++ b/src/app/dwg/libdxfrw/drw_entities.cpp @@ -954,7 +954,7 @@ bool DRW_Ellipse::parseDwg( DRW::Version version, dwgBuffer *buf, duint32 bs ) //parts are the number of vertices to split the polyline, default 128 void DRW_Ellipse::toPolyline( DRW_Polyline *pol, int parts ) const { - double radMajor = sqrt( secPoint.x * secPoint.x + secPoint.y * secPoint.y ); + double radMajor = std::sqrt( secPoint.x * secPoint.x + secPoint.y * secPoint.y ); double radMinor = radMajor * ratio; //calculate sin & std::cos of included angle double incAngle = std::atan2( secPoint.y, secPoint.x ); diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index 6763275ef1b..6fbf2de2035 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -1444,7 +1444,7 @@ bool QgsDwgImporter::curveFromLWPolyline( const DRW_LWPolyline &data, QgsCompoun double a = 2.0 * atan( data.vertlist[i]->bulge ); double dx = data.vertlist[i1]->x - data.vertlist[i0]->x; double dy = data.vertlist[i1]->y - data.vertlist[i0]->y; - double c = sqrt( dx * dx + dy * dy ); + double c = std::sqrt( dx * dx + dy * dy ); double r = c / 2.0 / std::sin( a ); double h = r * ( 1 - std::cos( a ) ); @@ -1555,7 +1555,7 @@ void QgsDwgImporter::addLWPolyline( const DRW_LWPolyline &data ) double a = 2.0 * atan( data.vertlist[i]->bulge ); double dx = p1.x() - p0.x(); double dy = p1.y() - p0.y(); - double c = sqrt( dx * dx + dy * dy ); + double c = std::sqrt( dx * dx + dy * dy ); double r = c / 2.0 / std::sin( a ); double h = r * ( 1 - std::cos( a ) ); @@ -1757,7 +1757,7 @@ void QgsDwgImporter::addPolyline( const DRW_Polyline &data ) double dx = p1.x() - p0.x(); double dy = p1.y() - p0.y(); double dz = p1.z() - p0.z(); - double c = sqrt( dx * dx + dy * dy ); + double c = std::sqrt( dx * dx + dy * dy ); double r = c / 2.0 / std::sin( a ); double h = r * ( 1 - std::cos( a ) ); diff --git a/src/app/nodetool/qgsnodeeditor.cpp b/src/app/nodetool/qgsnodeeditor.cpp index b9b8b654e73..c1d023c6854 100644 --- a/src/app/nodetool/qgsnodeeditor.cpp +++ b/src/app/nodetool/qgsnodeeditor.cpp @@ -268,7 +268,7 @@ bool QgsNodeEditorModel::calcR( int row, double &r, double &minRadius ) const QgsGeometryUtils::circleCenterRadius( p1, p2, p3, r, cx, cy ); double x13 = p3.x() - p1.x(), y13 = p3.y() - p1.y(); - minRadius = 0.5 * qSqrt( x13 * x13 + y13 * y13 ); + minRadius = 0.5 * std::sqrt( x13 * x13 + y13 * y13 ); return true; } diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index f6c0280814d..329765174bd 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -647,13 +647,13 @@ bool QgsNodeTool::isNearEndpointMarker( const QgsPointXY &mapPoint ) if ( !mEndpointMarkerCenter ) return false; - double distMarker = sqrt( mEndpointMarkerCenter->sqrDist( mapPoint ) ); + double distMarker = std::sqrt( mEndpointMarkerCenter->sqrDist( mapPoint ) ); double tol = QgsTolerance::vertexSearchRadius( canvas()->mapSettings() ); QgsGeometry geom = cachedGeometryForVertex( *mMouseAtEndpoint ); QgsPointXY vertexPointV2 = geom.vertexAt( mMouseAtEndpoint->vertexId ); QgsPointXY vertexPoint = QgsPointXY( vertexPointV2.x(), vertexPointV2.y() ); - double distVertex = sqrt( vertexPoint.sqrDist( mapPoint ) ); + double distVertex = std::sqrt( vertexPoint.sqrDist( mapPoint ) ); return distMarker < tol && distMarker < distVertex; } @@ -1756,7 +1756,7 @@ bool QgsNodeTool::matchEdgeCenterTest( const QgsPointLocator::Match &m, const Qg if ( edgeCenterPtr ) *edgeCenterPtr = edgeCenter; - double distFromEdgeCenter = sqrt( mapPoint.sqrDist( edgeCenter ) ); + double distFromEdgeCenter = std::sqrt( mapPoint.sqrDist( edgeCenter ) ); double tol = QgsTolerance::vertexSearchRadius( canvas()->mapSettings() ); bool isNearCenter = distFromEdgeCenter < tol; return isNearCenter; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 08ad5c74ded..654d5b710a2 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -3763,7 +3763,7 @@ void QgisApp::saveRecentProjectPath( const QString &projectPath, bool savePrevie QDir().mkdir( previewDir ); // Render the map canvas - QSize previewSize( 250, 177 ); // h = w / sqrt(2) + QSize previewSize( 250, 177 ); // h = w / std::sqrt(2) QRect previewRect( QPoint( ( mMapCanvas->width() - previewSize.width() ) / 2 , ( mMapCanvas->height() - previewSize.height() ) / 2 ) , previewSize ); @@ -9145,8 +9145,8 @@ void QgisApp::legendLayerZoomNative() QgsCoordinateTransform ct( mMapCanvas->mapSettings().destinationCrs(), layer->crs() ); p1 = ct.transform( p1 ); p2 = ct.transform( p2 ); - double width = sqrt( p1.sqrDist( p2 ) ); // width (actually the diagonal) of reprojected pixel - mMapCanvas->zoomByFactor( sqrt( layer->rasterUnitsPerPixelX() * layer->rasterUnitsPerPixelX() + layer->rasterUnitsPerPixelY() * layer->rasterUnitsPerPixelY() ) / width ); + double width = std::sqrt( p1.sqrDist( p2 ) ); // width (actually the diagonal) of reprojected pixel + mMapCanvas->zoomByFactor( std::sqrt( layer->rasterUnitsPerPixelX() * layer->rasterUnitsPerPixelX() + layer->rasterUnitsPerPixelY() * layer->rasterUnitsPerPixelY() ) / width ); mMapCanvas->refresh(); QgsDebugMsg( "MapUnitsPerPixel after : " + QString::number( mMapCanvas->mapUnitsPerPixel() ) ); diff --git a/src/app/qgsmaptoolcircularstringradius.cpp b/src/app/qgsmaptoolcircularstringradius.cpp index 3398c755243..6b98a055d9c 100644 --- a/src/app/qgsmaptoolcircularstringradius.cpp +++ b/src/app/qgsmaptoolcircularstringradius.cpp @@ -63,7 +63,7 @@ void QgsMapToolCircularStringRadius::cadCanvasReleaseEvent( QgsMapMouseEvent *e mTemporaryEndPoint = mapPoint; //initial radius is distance( tempPoint - mPoints.last ) / 2.0 - double minRadius = sqrt( QgsGeometryUtils::sqrDistance2D( mPoints.last(), mTemporaryEndPoint ) ) / 2.0; + double minRadius = std::sqrt( QgsGeometryUtils::sqrDistance2D( mPoints.last(), mTemporaryEndPoint ) ) / 2.0; mRadius = minRadius + minRadius / 10.0; QgsPoint result; diff --git a/src/app/qgsmaptoollabel.cpp b/src/app/qgsmaptoollabel.cpp index ff7b19b8b43..84d0cea6321 100644 --- a/src/app/qgsmaptoollabel.cpp +++ b/src/app/qgsmaptoollabel.cpp @@ -343,8 +343,8 @@ bool QgsMapToolLabel::currentLabelRotationPoint( QgsPointXY &pos, bool ignoreUps // QgsDebugMsg( QString( "cp_0: x=%1, y=%2" ).arg( cp_0.x() ).arg( cp_0.y() ) ); // QgsDebugMsg( QString( "cp_1: x=%1, y=%2" ).arg( cp_1.x() ).arg( cp_1.y() ) ); // QgsDebugMsg( QString( "cp_3: x=%1, y=%2" ).arg( cp_3.x() ).arg( cp_3.y() ) ); - double labelSizeX = qSqrt( cp_0.sqrDist( cp_1 ) ); - double labelSizeY = qSqrt( cp_0.sqrDist( cp_3 ) ); + double labelSizeX = std::sqrt( cp_0.sqrDist( cp_1 ) ); + double labelSizeY = std::sqrt( cp_0.sqrDist( cp_3 ) ); double xdiff = 0; double ydiff = 0; diff --git a/src/app/qgsmaptooloffsetcurve.cpp b/src/app/qgsmaptooloffsetcurve.cpp index e8a0f071f73..390d8182d6b 100644 --- a/src/app/qgsmaptooloffsetcurve.cpp +++ b/src/app/qgsmaptooloffsetcurve.cpp @@ -239,7 +239,7 @@ void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e ) QgsPointXY minDistPoint; int beforeVertex; double leftOf; - double offset = sqrt( mOriginalGeometry.closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) ); + double offset = std::sqrt( mOriginalGeometry.closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) ); if ( offset == 0.0 ) { return; diff --git a/src/app/qgsmaptoolselectradius.cpp b/src/app/qgsmaptoolselectradius.cpp index c647cd0d326..ae57e783c15 100644 --- a/src/app/qgsmaptoolselectradius.cpp +++ b/src/app/qgsmaptoolselectradius.cpp @@ -102,7 +102,7 @@ void QgsMapToolSelectRadius::canvasReleaseEvent( QgsMapMouseEvent *e ) void QgsMapToolSelectRadius::setRadiusRubberBand( QgsPointXY &radiusEdge ) { - double r = sqrt( mRadiusCenter.sqrDist( radiusEdge ) ); + double r = std::sqrt( mRadiusCenter.sqrDist( radiusEdge ) ); mRubberBand->reset( QgsWkbTypes::PolygonGeometry ); for ( int i = 0; i <= RADIUS_SEGMENTS; ++i ) { diff --git a/src/app/qgsmeasuredialog.cpp b/src/app/qgsmeasuredialog.cpp index 8d7a7431330..d096dad8abf 100644 --- a/src/app/qgsmeasuredialog.cpp +++ b/src/app/qgsmeasuredialog.cpp @@ -490,7 +490,7 @@ void QgsMeasureDialog::updateUi() if ( forceCartesian ) { //Cartesian calculation forced - d = sqrt( p2.sqrDist( p1 ) ); + d = std::sqrt( p2.sqrDist( p1 ) ); mTotal += d; } else diff --git a/src/app/qgspointrotationitem.cpp b/src/app/qgspointrotationitem.cpp index fde389aea02..07fbbd45663 100644 --- a/src/app/qgspointrotationitem.cpp +++ b/src/app/qgspointrotationitem.cpp @@ -54,7 +54,7 @@ void QgsPointRotationItem::paint( QPainter *painter ) double h, dAngel; if ( mPixmap.width() > 0 && mPixmap.height() > 0 ) { - h = sqrt( ( double ) mPixmap.width() * mPixmap.width() + mPixmap.height() * mPixmap.height() ) / 2; //the half of the item diagonal + h = std::sqrt( ( double ) mPixmap.width() * mPixmap.width() + mPixmap.height() * mPixmap.height() ) / 2; //the half of the item diagonal dAngel = acos( mPixmap.width() / ( h * 2 ) ) * 180 / M_PI; //the diagonal angel of the original rect x = h * std::cos( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); y = h * std::sin( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); diff --git a/src/app/qgsrastercalcdialog.cpp b/src/app/qgsrastercalcdialog.cpp index 73eab96b74b..d90cea9ce8e 100644 --- a/src/app/qgsrastercalcdialog.cpp +++ b/src/app/qgsrastercalcdialog.cpp @@ -350,7 +350,7 @@ void QgsRasterCalcDialog::on_mDividePushButton_clicked() void QgsRasterCalcDialog::on_mSqrtButton_clicked() { - mExpressionTextEdit->insertPlainText( QStringLiteral( " sqrt ( " ) ); + mExpressionTextEdit->insertPlainText( QStringLiteral( " std::sqrt ( " ) ); } void QgsRasterCalcDialog::on_mCosButton_clicked() diff --git a/src/core/annotations/qgsannotation.cpp b/src/core/annotations/qgsannotation.cpp index 86cf4150965..ca7c63783ea 100644 --- a/src/core/annotations/qgsannotation.cpp +++ b/src/core/annotations/qgsannotation.cpp @@ -205,7 +205,7 @@ void QgsAnnotation::updateBalloon() mBalloonSegment = minEdgeIndex; QPointF minEdgeEnd = minEdge.p2(); mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() ); - if ( sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth ) + if ( std::sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth ) { mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth ); } @@ -261,7 +261,7 @@ QPointF QgsAnnotation::pointOnLineWithDistance( QPointF startPoint, QPointF dire { double dx = directionPoint.x() - startPoint.x(); double dy = directionPoint.y() - startPoint.y(); - double length = sqrt( dx * dx + dy * dy ); + double length = std::sqrt( dx * dx + dy * dy ); double scaleFactor = distance / length; return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor ); } diff --git a/src/core/composer/qgscomposerutils.cpp b/src/core/composer/qgscomposerutils.cpp index 8ca95102b23..0b422f14430 100644 --- a/src/core/composer/qgscomposerutils.cpp +++ b/src/core/composer/qgscomposerutils.cpp @@ -70,7 +70,7 @@ double QgsComposerUtils::angle( QPointF p1, QPointF p2 ) { double xDiff = p2.x() - p1.x(); double yDiff = p2.y() - p1.y(); - double length = sqrt( xDiff * xDiff + yDiff * yDiff ); + double length = std::sqrt( xDiff * xDiff + yDiff * yDiff ); if ( length <= 0 ) { return 0; diff --git a/src/core/diagram/qgsdiagram.cpp b/src/core/diagram/qgsdiagram.cpp index cb609e09ed8..4b0aa088dd5 100644 --- a/src/core/diagram/qgsdiagram.cpp +++ b/src/core/diagram/qgsdiagram.cpp @@ -97,9 +97,9 @@ QSizeF QgsDiagram::sizeForValue( double value, const QgsDiagramSettings &s, cons // interpolate the squared value if scale by area if ( s.scaleByArea ) { - scaledValue = sqrt( scaledValue ); - scaledLowerValue = sqrt( scaledLowerValue ); - scaledUpperValue = sqrt( scaledUpperValue ); + scaledValue = std::sqrt( scaledValue ); + scaledLowerValue = std::sqrt( scaledLowerValue ); + scaledUpperValue = std::sqrt( scaledUpperValue ); } //interpolate size diff --git a/src/core/diagram/qgstextdiagram.cpp b/src/core/diagram/qgstextdiagram.cpp index ee720da3826..25881df4fe5 100644 --- a/src/core/diagram/qgstextdiagram.cpp +++ b/src/core/diagram/qgstextdiagram.cpp @@ -243,7 +243,7 @@ void QgsTextDiagram::lineEllipseIntersection( QPointF lineStart, QPointF lineEnd double d = b * b - a * ( c - 1 ); if ( d > 0 ) { - double e = sqrt( d ); + double e = std::sqrt( d ); double u1 = ( -b - e ) / a; double u2 = ( -b + e ) / a; //work with a tolerance of 0.00001 because of limited numerical precision diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index 2e83c5b44a6..feba343168e 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -388,7 +388,7 @@ void QgsImageOperation::distanceTransform( QImage &image, const DistanceTransfor double spread; if ( properties.useMaxDistance ) { - spread = sqrt( maxValueInDistanceTransformArray( array, image.width() * image.height() ) ); + spread = std::sqrt( maxValueInDistanceTransformArray( array, image.width() * image.height() ) ); } else { @@ -541,7 +541,7 @@ void QgsImageOperation::ShadeFromArrayOperation::operator()( QRgb &rgb, const in return; } - double distance = sqrt( squaredVal ); + double distance = std::sqrt( squaredVal ); double val = distance / mSpread; QColor rampColor = mProperties.ramp->color( val ); @@ -764,7 +764,7 @@ double *QgsImageOperation::createGaussianKernel( const int radius ) double *kernel = new double[ radius * 2 + 1 ]; double sigma = radius / 3.0; double twoSigmaSquared = 2 * sigma * sigma; - double coefficient = 1.0 / sqrt( M_PI * twoSigmaSquared ); + double coefficient = 1.0 / std::sqrt( M_PI * twoSigmaSquared ); double expCoefficient = -1.0 / twoSigmaSquared; double sum = 0; diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 1c496d362a1..b596f679b1d 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -235,7 +235,7 @@ static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( sqrt( x ) ); + return QVariant( std::sqrt( x ) ); } static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) diff --git a/src/core/geometry/qgscircularstring.cpp b/src/core/geometry/qgscircularstring.cpp index d12ede393b9..5ccd632267e 100644 --- a/src/core/geometry/qgscircularstring.cpp +++ b/src/core/geometry/qgscircularstring.cpp @@ -760,7 +760,7 @@ void QgsCircularString::sumUpArea( double &sum ) const double radius, centerX, centerY; QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY ); - double d = sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) ); + double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) ); double r2 = radius * radius; if ( d > radius ) @@ -772,7 +772,7 @@ void QgsCircularString::sumUpArea( double &sum ) const bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0; bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0; - double cov = 0.5 - d * sqrt( r2 - d * d ) / ( M_PI * r2 ) - 1 / M_PI * asin( d / radius ); + double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - 1 / M_PI * asin( d / radius ); double circleChordArea = 0; if ( circlePointLeftOfLine == centerPointLeftOfLine ) { diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index 5fc00b0f91f..bfb3ddd7e5d 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -64,7 +64,7 @@ QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 ); double axis_a = dist / 2.0; - double axis_b = sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) ); + double axis_b = std::sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) ); return QgsEllipse( center, axis_a, axis_b, azimuth ); } @@ -170,7 +170,7 @@ double QgsEllipse::perimeter() const { double a = mSemiMajorAxis; double b = mSemiMinorAxis; - return M_PI * ( 3 * ( a + b ) - sqrt( 10 * a * b + 3 * ( a * a + b * b ) ) ); + return M_PI * ( 3 * ( a + b ) - std::sqrt( 10 * a * b + 3 * ( a * a + b * b ) ) ); } QVector QgsEllipse::quadrant() const @@ -262,8 +262,8 @@ QgsRectangle QgsEllipse::boundingBox() const double vx = mSemiMajorAxis * std::sin( angle ); double vy = mSemiMinorAxis * std::cos( angle ); - double halfHeight = sqrt( ux * ux + uy * uy ); - double halfWidth = sqrt( vx * vx + vy * vy ); + double halfHeight = std::sqrt( ux * ux + uy * uy ); + double halfWidth = std::sqrt( vx * vx + vy * vy ); QgsPointXY p1( mCenter.x() - halfWidth, mCenter.y() - halfHeight ); QgsPointXY p2( mCenter.x() + halfWidth, mCenter.y() + halfHeight ); diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index f1f6d351608..ba13f8fdb9d 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -139,7 +139,7 @@ double QgsGeometryUtils::distanceToVertex( const QgsAbstractGeometry &geom, QgsV { if ( !first ) { - currentDist += sqrt( QgsGeometryUtils::sqrDistance2D( previousVertex, vertex ) ); + currentDist += std::sqrt( QgsGeometryUtils::sqrDistance2D( previousVertex, vertex ) ); } previousVertex = vertex; @@ -177,7 +177,7 @@ bool QgsGeometryUtils::verticesAtDistance( const QgsAbstractGeometry &geometry, { if ( !first ) { - currentDist += sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) ); + currentDist += std::sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) ); } if ( qgsDoubleNear( currentDist, distance ) ) @@ -417,7 +417,7 @@ QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, { double dx = directionPoint.x() - startPoint.x(); double dy = directionPoint.y() - startPoint.y(); - double length = sqrt( dx * dx + dy * dy ); + double length = std::sqrt( dx * dx + dy * dy ); if ( qgsDoubleNear( length, 0.0 ) ) { @@ -451,7 +451,7 @@ void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint & { centerX = ( pt1.x() + pt2.x() ) / 2.0; centerY = ( pt1.y() + pt2.y() ) / 2.0; - radius = sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) ); + radius = std::sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) ); return; } @@ -477,7 +477,7 @@ void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint & // Calculate centroid coordinates and radius centerX = pt1.x() + ( h21 * dy31 - h31 * dy21 ) / d; centerY = pt1.y() - ( h21 * dx31 - h31 * dx21 ) / d; - radius = sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) ); + radius = std::sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) ); } bool QgsGeometryUtils::circleClockwise( double angle1, double angle2, double angle3 ) @@ -569,12 +569,12 @@ double QgsGeometryUtils::sweepAngle( double centerX, double centerY, double x1, bool QgsGeometryUtils::segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result, double radius, const QgsPoint &mousePos ) { QgsPoint midPoint( ( p1.x() + p2.x() ) / 2.0, ( p1.y() + p2.y() ) / 2.0 ); - double midDist = sqrt( sqrDistance2D( p1, midPoint ) ); + double midDist = std::sqrt( sqrDistance2D( p1, midPoint ) ); if ( radius < midDist ) { return false; } - double centerMidDist = sqrt( radius * radius - midDist * midDist ); + double centerMidDist = std::sqrt( radius * radius - midDist * midDist ); double dist = radius - centerMidDist; double midDx = midPoint.x() - p1.x(); diff --git a/src/core/geometry/qgsinternalgeometryengine.cpp b/src/core/geometry/qgsinternalgeometryengine.cpp index f388b5f2837..9af2f8cbd5b 100644 --- a/src/core/geometry/qgsinternalgeometryengine.cpp +++ b/src/core/geometry/qgsinternalgeometryengine.cpp @@ -575,7 +575,7 @@ QgsLineString *doDensify( QgsLineString *ring, int extraNodesPerSegment = -1, do if ( extraNodesPerSegment < 0 ) { // distance mode - extraNodesThisSegment = std::floor( sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ) / distance ); + extraNodesThisSegment = std::floor( std::sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ) / distance ); if ( extraNodesThisSegment >= 1 ) multiplier = 1.0 / ( extraNodesThisSegment + 1 ); } diff --git a/src/core/geometry/qgslinestring.cpp b/src/core/geometry/qgslinestring.cpp index 2019a1b5225..233fbb190b6 100644 --- a/src/core/geometry/qgslinestring.cpp +++ b/src/core/geometry/qgslinestring.cpp @@ -330,7 +330,7 @@ double QgsLineString::length() const { dx = mX.at( i ) - mX.at( i - 1 ); dy = mY.at( i ) - mY.at( i - 1 ); - length += sqrt( dx * dx + dy * dy ); + length += std::sqrt( dx * dx + dy * dy ); } return length; } @@ -679,8 +679,8 @@ void QgsLineString::extend( double startDistance, double endDistance ) // start of line if ( startDistance > 0 ) { - double currentLen = sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) + - std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) ); + double currentLen = std::sqrt( std::pow( mX.at( 0 ) - mX.at( 1 ), 2 ) + + std::pow( mY.at( 0 ) - mY.at( 1 ), 2 ) ); double newLen = currentLen + startDistance; mX[ 0 ] = mX.at( 1 ) + ( mX.at( 0 ) - mX.at( 1 ) ) / currentLen * newLen; mY[ 0 ] = mY.at( 1 ) + ( mY.at( 0 ) - mY.at( 1 ) ) / currentLen * newLen; @@ -689,8 +689,8 @@ void QgsLineString::extend( double startDistance, double endDistance ) if ( endDistance > 0 ) { int last = mX.size() - 1; - double currentLen = sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) + - std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) ); + double currentLen = std::sqrt( std::pow( mX.at( last ) - mX.at( last - 1 ), 2 ) + + std::pow( mY.at( last ) - mY.at( last - 1 ), 2 ) ); double newLen = currentLen + endDistance; mX[ last ] = mX.at( last - 1 ) + ( mX.at( last ) - mX.at( last - 1 ) ) / currentLen * newLen; mY[ last ] = mY.at( last - 1 ) + ( mY.at( last ) - mY.at( last - 1 ) ) / currentLen * newLen; @@ -916,8 +916,8 @@ QgsPoint QgsLineString::centroid() const { double currentX = mX.at( i ); double currentY = mY.at( i ); - double segmentLength = sqrt( std::pow( currentX - prevX, 2.0 ) + - std::pow( currentY - prevY, 2.0 ) ); + double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) + + std::pow( currentY - prevY, 2.0 ) ); if ( qgsDoubleNear( segmentLength, 0.0 ) ) continue; diff --git a/src/core/geometry/qgspolygon.cpp b/src/core/geometry/qgspolygon.cpp index d388d1cf253..e449a5f6c11 100644 --- a/src/core/geometry/qgspolygon.cpp +++ b/src/core/geometry/qgspolygon.cpp @@ -292,7 +292,7 @@ double QgsPolygonV2::pointDistanceToBoundary( double x, double y ) const } } - return ( inside ? 1 : -1 ) * sqrt( minimumDistance ); + return ( inside ? 1 : -1 ) * std::sqrt( minimumDistance ); } QgsPolygonV2 *QgsPolygonV2::surfaceToPolygon() const diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 93bab79357d..c0ec8a1f68e 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -728,7 +728,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QListgetPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY ); line->getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY ); - candidateLength = sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) ); + candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) ); // LOTS OF DIFFERENT COSTS TO BALANCE HERE - feel free to tweak these, but please add a unit test @@ -883,12 +883,12 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList & if ( currentDistanceAlongLine < 0 ) { // label is bigger than line, use whole available line - candidateLength = sqrt( ( x[nbPoints - 1] - x[0] ) * ( x[nbPoints - 1] - x[0] ) - + ( y[nbPoints - 1] - y[0] ) * ( y[nbPoints - 1] - y[0] ) ); + candidateLength = std::sqrt( ( x[nbPoints - 1] - x[0] ) * ( x[nbPoints - 1] - x[0] ) + + ( y[nbPoints - 1] - y[0] ) * ( y[nbPoints - 1] - y[0] ) ); } else { - candidateLength = sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) ); + candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) ); } cost = candidateLength / labelWidth; @@ -1161,7 +1161,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos if ( i == 0 ) path_distances[i] = 0; else - path_distances[i] = sqrt( std::pow( old_x - mapShape->x[i], 2 ) + std::pow( old_y - mapShape->y[i], 2 ) ); + path_distances[i] = std::sqrt( std::pow( old_x - mapShape->x[i], 2 ) + std::pow( old_y - mapShape->y[i], 2 ) ); old_x = mapShape->x[i]; old_y = mapShape->y[i]; @@ -1343,7 +1343,7 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin double dy; int bbid; double beta; - double diago = sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 ); + double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 ); double rx, ry; CHullBox **boxes = new CHullBox*[shapes_final.size()]; j = 0; @@ -1808,13 +1808,13 @@ bool FeaturePart::nextCharPosition( double charWidth, double segment_length, Poi dx = new_x - old_x; dy = new_y - old_y; } - while ( sqrt( std::pow( start_x - new_x, 2 ) + std::pow( start_y - new_y, 2 ) ) < charWidth ); // Distance from start_ to new_ + while ( std::sqrt( std::pow( start_x - new_x, 2 ) + std::pow( start_y - new_y, 2 ) ) < charWidth ); // Distance from start_ to new_ // Calculate the position to place the end of the character on GeomFunction::findLineCircleIntersection( start_x, start_y, charWidth, old_x, old_y, new_x, new_y, end_x, end_y ); // Need to calculate distance on the new segment - distance = sqrt( std::pow( old_x - end_x, 2 ) + std::pow( old_y - end_y, 2 ) ); + distance = std::sqrt( std::pow( old_x - end_x, 2 ) + std::pow( old_y - end_y, 2 ) ); } return true; } diff --git a/src/core/pal/geomfunction.cpp b/src/core/pal/geomfunction.cpp index 1fab4bb27f8..d6a24a54c4f 100644 --- a/src/core/pal/geomfunction.cpp +++ b/src/core/pal/geomfunction.cpp @@ -398,7 +398,7 @@ void GeomFunction::findLineCircleIntersection( double cx, double cy, double radi // Two solutions. // Always use the 1st one // We only really have one solution here, as we know the line segment will start in the circle and end outside - double t = ( -B + sqrt( det ) ) / ( 2 * A ); + double t = ( -B + std::sqrt( det ) ) / ( 2 * A ); xRes = x1 + t * dx; yRes = y1 + t * dy; } diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index b43384b58f1..74a2a390057 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -453,7 +453,7 @@ double LabelPosition::getDistanceToPoint( double xp, double yp ) const { //first check if inside, if so then distance is -1 double distance = ( containsPoint( xp, yp ) ? -1 - : sqrt( minDistanceToPoint( xp, yp ) ) ); + : std::sqrt( minDistanceToPoint( xp, yp ) ) ); if ( nextPart && distance > 0 ) return qMin( distance, nextPart->getDistanceToPoint( xp, yp ) ); diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index f48586fe6e5..872bcf05a23 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -416,7 +416,7 @@ void PointSet::splitPolygons( QLinkedList &shapes_toProcess, // holeE = hole ending point // retainedPt = deppest point in hole // bestArea = area of triangle HoleS->holeE->retainedPoint - bestArea = sqrt( bestArea ); + bestArea = std::sqrt( bestArea ); double cx, cy, dx, dy, ex, ey, fx, fy, seg_length, ptx = 0, pty = 0, fptx = 0, fpty = 0; int ps = -1, pe = -1, fps = -1, fpe = -1; if ( retainedPt >= 0 && bestArea > labelArea ) // there is a hole so we'll cut the shape in two new shape (only if hole area is bigger than twice labelArea) @@ -827,7 +827,7 @@ void PointSet::getPointByDistance( double *d, double *ad, double dl, double *px, { dx = x[nbPoints - 1] - x[0]; dy = y[nbPoints - 1] - y[0]; - di = sqrt( dx * dx + dy * dy ); + di = std::sqrt( dx * dx + dy * dy ); } else { diff --git a/src/core/pal/rtree.hpp b/src/core/pal/rtree.hpp index d59138804c0..b50b4a0a90e 100644 --- a/src/core/pal/rtree.hpp +++ b/src/core/pal/rtree.hpp @@ -1167,7 +1167,7 @@ namespace pal sumOfSquares += halfExtent * halfExtent; } - radius = static_cast< ELEMTYPEREAL >( sqrt( sumOfSquares ) ); + radius = static_cast< ELEMTYPEREAL >( std::sqrt( sumOfSquares ) ); // Pow maybe slow, so test for common dims like 2,3 and just use x*x, x*x*x. if ( NUMDIMS == 3 ) diff --git a/src/core/qgscoordinatetransform.cpp b/src/core/qgscoordinatetransform.cpp index dc762e97645..9b971593760 100644 --- a/src/core/qgscoordinatetransform.cpp +++ b/src/core/qgscoordinatetransform.cpp @@ -352,7 +352,7 @@ QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &r // even with 1000 points it takes < 1ms // TODO: how to effectively and precisely reproject bounding box? const int nPoints = 1000; - double d = sqrt( ( rect.width() * rect.height() ) / std::pow( sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) ); + double d = std::sqrt( ( rect.width() * rect.height() ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) ); int nXPoints = static_cast< int >( std::ceil( rect.width() / d ) ) + 1; int nYPoints = static_cast< int >( std::ceil( rect.height() / d ) ) + 1; diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 8d5720cc405..a964693aef7 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -445,7 +445,7 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( while ( i < 999 && std::fabs( ( last_sigma - sigma ) / sigma ) > 1.0e-9 ); lat2 = std::atan2( ( std::sin( u1 ) * std::cos( sigma ) + std::cos( u1 ) * std::sin( sigma ) * - std::cos( azimuth ) ), ( omf * sqrt( POW2( sin_alpha ) + + std::cos( azimuth ) ), ( omf * std::sqrt( POW2( sin_alpha ) + POW2( std::sin( u1 ) * std::sin( sigma ) - std::cos( u1 ) * std::cos( sigma ) * std::cos( azimuth ) ) ) ) ); lambda = std::atan2( ( std::sin( sigma ) * std::sin( azimuth ) ), ( std::cos( u1 ) * std::cos( sigma ) - @@ -577,7 +577,7 @@ double QgsDistanceArea::computeDistanceBearing( cosLambda = std::cos( lambda ); tu1 = ( cosU2 * sinLambda ); tu2 = ( cosU1 * sinU2 - sinU1 * cosU2 * cosLambda ); - sinSigma = sqrt( tu1 * tu1 + tu2 * tu2 ); + sinSigma = std::sqrt( tu1 * tu1 + tu2 * tu2 ); cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = std::atan2( sinSigma, cosSigma ); alpha = asin( cosU1 * cosU2 * sinLambda / sinSigma ); diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index f90a650b1d5..20b568dac99 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -2989,7 +2989,7 @@ bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext &context, const double area = geom.area(); if ( area >= 0.0 ) { - return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) ); + return ( std::sqrt( area ) >= ( minSize * mapUnitsPerMM ) ); } } return true; //should never be reached. Return true in this case to label such geometries anyway. diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index e2f2fd07975..7932a4f8b24 100644 --- a/src/core/qgspointlocator.cpp +++ b/src/core/qgspointlocator.cpp @@ -109,7 +109,7 @@ class QgsPointLocator_VisitorNearestVertex : public IVisitor if ( sqrDist < 0 ) return; // probably empty geometry - QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, sqrt( sqrDist ), pt, vertexIndex ); + QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, vertexIndex ); // in range queries the filter may reject some matches if ( mFilter && !mFilter->acceptMatch( m ) ) return; @@ -159,7 +159,7 @@ class QgsPointLocator_VisitorNearestEdge : public IVisitor QgsPointXY edgePoints[2]; edgePoints[0] = geom->vertexAt( afterVertex - 1 ); edgePoints[1] = geom->vertexAt( afterVertex ); - QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, sqrt( sqrDist ), pt, afterVertex - 1, edgePoints ); + QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, afterVertex - 1, edgePoints ); // in range queries the filter may reject some matches if ( mFilter && !mFilter->acceptMatch( m ) ) return; diff --git a/src/core/qgsscalecalculator.cpp b/src/core/qgsscalecalculator.cpp index 9ff82857b52..1ebeb5706f2 100644 --- a/src/core/qgsscalecalculator.cpp +++ b/src/core/qgsscalecalculator.cpp @@ -117,7 +117,7 @@ double QgsScaleCalculator::calculateGeographicDistance( const QgsRectangle &mapE double lat = ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5; static const double RADS = ( 4.0 * atan( 1.0 ) ) / 180.0; double a = std::pow( std::cos( lat * RADS ), 2 ); - double c = 2.0 * std::atan2( sqrt( a ), sqrt( 1.0 - a ) ); + double c = 2.0 * std::atan2( std::sqrt( a ), std::sqrt( 1.0 - a ) ); static const double RA = 6378000; // [m] // The eccentricity. This comes from sqrt(1.0 - rb*rb/(ra*ra)) with rb set // to 6357000 m. diff --git a/src/core/qgssnappingutils.cpp b/src/core/qgssnappingutils.cpp index 8cd3084d904..61d59535f44 100644 --- a/src/core/qgssnappingutils.cpp +++ b/src/core/qgssnappingutils.cpp @@ -160,7 +160,7 @@ static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY } } - return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, sqrt( minSqrDist ), minP ); + return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP ); } @@ -380,7 +380,7 @@ void QgsSnappingUtils::prepareIndex( const QList &layers { // use area as big as we think may fit into our limit QgsPointXY c = entry.second.center(); - double halfSide = sqrt( indexReasonableArea ) / 2; + double halfSide = std::sqrt( indexReasonableArea ) / 2; QgsRectangle rect( c.x() - halfSide, c.y() - halfSide, c.x() + halfSide, c.y() + halfSide ); loc->setExtent( &rect ); diff --git a/src/core/qgstextrenderer.cpp b/src/core/qgstextrenderer.cpp index c1e2bcc8a3b..10ae5252f1e 100644 --- a/src/core/qgstextrenderer.cpp +++ b/src/core/qgstextrenderer.cpp @@ -2192,14 +2192,14 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer else if ( background.type() == QgsTextBackgroundSettings::ShapeCircle ) { // start with label bound by circle - h = sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) ); + h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) ); w = h; } else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse ) { // start with label bound by ellipse - h = h / sqrt( 2.0 ) * 2; - w = w / sqrt( 2.0 ) * 2; + h = h / std::sqrt( 2.0 ) * 2; + w = w / std::sqrt( 2.0 ) * 2; } double bufferWidth = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), diff --git a/src/core/qgstolerance.cpp b/src/core/qgstolerance.cpp index 31e9f404554..2fa487be7c8 100644 --- a/src/core/qgstolerance.cpp +++ b/src/core/qgstolerance.cpp @@ -30,7 +30,7 @@ double _ratioMU2LU( const QgsMapSettings &mapSettings, QgsMapLayer *layer ) QgsPointXY ptMapCenterRightMU( ptMapCenterMU.x() + distMU, ptMapCenterMU.y() ); QgsPointXY ptMapCenterLU = mapSettings.mapToLayerCoordinates( layer, ptMapCenterMU ); QgsPointXY ptMapCenterRightLU = mapSettings.mapToLayerCoordinates( layer, ptMapCenterRightMU ); - double distLU = sqrt( ptMapCenterLU.sqrDist( ptMapCenterRightLU ) ); + double distLU = std::sqrt( ptMapCenterLU.sqrDist( ptMapCenterRightLU ) ); double ratio = distMU / distLU; return ratio; } diff --git a/src/core/qgstracer.cpp b/src/core/qgstracer.cpp index 16ce217e1af..6cbc282b88f 100644 --- a/src/core/qgstracer.cpp +++ b/src/core/qgstracer.cpp @@ -53,7 +53,7 @@ double distance2D( const QgsPolyline &coords ) { x1 = coords[i].x(); y1 = coords[i].y(); - dist += sqrt( ( x1 - x0 ) * ( x1 - x0 ) + ( y1 - y0 ) * ( y1 - y0 ) ); + dist += std::sqrt( ( x1 - x0 ) * ( x1 - x0 ) + ( y1 - y0 ) * ( y1 - y0 ) ); x0 = x1; y0 = y1; } diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp index 44b1286b035..9d16d1089e5 100644 --- a/src/core/qgsvectorlayerlabeling.cpp +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -333,7 +333,7 @@ void QgsVectorLayerSimpleLabeling::toSld( QDomNode &parent, const QgsStringMap & QgsSymbolLayerUtils::createAnchorPointElement( doc, pointPlacement, QPointF( 0, 0.5 ) ); QgsUnitTypes::RenderUnit distUnit = mSettings->distUnits; double radius = QgsSymbolLayerUtils::rescaleUom( mSettings->dist, distUnit, props ); - double offset = sqrt( radius * radius / 2 ); // make it start top/right + double offset = std::sqrt( radius * radius / 2 ); // make it start top/right maxDisplacement = radius + 1; // lock the distance QgsSymbolLayerUtils::createDisplacementElement( doc, pointPlacement, QPointF( offset, offset ) ); } diff --git a/src/core/qgsvectorlayerrenderer.cpp b/src/core/qgsvectorlayerrenderer.cpp index 517034414e8..5cc61f74dfc 100644 --- a/src/core/qgsvectorlayerrenderer.cpp +++ b/src/core/qgsvectorlayerrenderer.cpp @@ -193,8 +193,8 @@ bool QgsVectorLayerRenderer::render() QgsPointXY minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() ); QgsPointXY maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() ); - double sourceHypothenuse = sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) ); - double targetHypothenuse = sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) ); + double sourceHypothenuse = std::sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) ); + double targetHypothenuse = std::sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) ); QgsDebugMsgLevel( QString( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ), 4 ); QgsDebugMsgLevel( QString( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ), 4 ); diff --git a/src/core/raster/qgshillshaderenderer.cpp b/src/core/raster/qgshillshaderenderer.cpp index 257b6385d34..4fdf13bcd83 100644 --- a/src/core/raster/qgshillshaderenderer.cpp +++ b/src/core/raster/qgshillshaderenderer.cpp @@ -210,7 +210,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize ); double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize ); - double slopeRad = atan( mZFactor * sqrt( derX * derX + derY * derY ) ); + double slopeRad = atan( mZFactor * std::sqrt( derX * derX + derY * derY ) ); double aspectRad = std::atan2( derX, -derY ); diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index fb8427413a6..33ac2cab1d1 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -62,7 +62,7 @@ void QgsRasterInterface::initStatistics( QgsRasterBandStats &statistics, { // Calc resolution from theSampleSize double xRes, yRes; - xRes = yRes = sqrt( ( finalExtent.width() * finalExtent.height() ) / sampleSize ); + xRes = yRes = std::sqrt( ( finalExtent.width() * finalExtent.height() ) / sampleSize ); // But limit by physical resolution if ( capabilities() & Size ) @@ -230,7 +230,7 @@ QgsRasterBandStats QgsRasterInterface::bandStatistics( int bandNo, // stdDev may differ from GDAL stats, because GDAL is using naive single pass // algorithm which is more error prone (because of rounding errors) // Divide result by sample size - 1 and get square root to get stdev - myRasterBandStats.stdDev = sqrt( mySumOfSquares / ( myRasterBandStats.elementCount - 1 ) ); + myRasterBandStats.stdDev = std::sqrt( mySumOfSquares / ( myRasterBandStats.elementCount - 1 ) ); QgsDebugMsgLevel( "************ STATS **************", 4 ); QgsDebugMsgLevel( QString( "MIN %1" ).arg( myRasterBandStats.minimumValue ), 4 ); @@ -305,7 +305,7 @@ void QgsRasterInterface::initHistogram( QgsRasterHistogram &histogram, { // Calc resolution from theSampleSize double xRes, yRes; - xRes = yRes = sqrt( ( finalExtent.width() * finalExtent.height() ) / sampleSize ); + xRes = yRes = std::sqrt( ( finalExtent.width() * finalExtent.height() ) / sampleSize ); // But limit by physical resolution if ( capabilities() & Size ) diff --git a/src/core/raster/qgsrasterprojector.cpp b/src/core/raster/qgsrasterprojector.cpp index 4b1d9d07d87..8d9bdafdad8 100644 --- a/src/core/raster/qgsrasterprojector.cpp +++ b/src/core/raster/qgsrasterprojector.cpp @@ -327,11 +327,11 @@ void ProjectorData::calcSrcRowsCols() QgsPointXY myPointC = mCPMatrix[i + 1][j]; if ( mCPLegalMatrix[i][j] && mCPLegalMatrix[i][j + 1] && mCPLegalMatrix[i + 1][j] ) { - double mySize = sqrt( myPointA.sqrDist( myPointB ) ) / myDestColsPerMatrixCell; + double mySize = std::sqrt( myPointA.sqrDist( myPointB ) ) / myDestColsPerMatrixCell; if ( mySize < myMinSize ) myMinSize = mySize; - mySize = sqrt( myPointA.sqrDist( myPointC ) ) / myDestRowsPerMatrixCell; + mySize = std::sqrt( myPointA.sqrDist( myPointC ) ) / myDestRowsPerMatrixCell; if ( mySize < myMinSize ) myMinSize = mySize; } diff --git a/src/core/simplify/effectivearea.cpp b/src/core/simplify/effectivearea.cpp index 0dbc9df1d9a..d193666b759 100644 --- a/src/core/simplify/effectivearea.cpp +++ b/src/core/simplify/effectivearea.cpp @@ -65,7 +65,7 @@ static double triarea3d( const QgsPoint &P1, const QgsPoint &P2, const QgsPoint cy = az * bx - ax * bz; cz = ax * by - ay * bx; - area = std::fabs( 0.5 * ( sqrt( cx * cx + cy * cy + cz * cz ) ) ); + area = std::fabs( 0.5 * ( std::sqrt( cx * cx + cy * cy + cz * cz ) ) ); return area; } diff --git a/src/core/symbology/qgsarrowsymbollayer.cpp b/src/core/symbology/qgsarrowsymbollayer.cpp index e2702cdefad..6e33fe5e58d 100644 --- a/src/core/symbology/qgsarrowsymbollayer.cpp +++ b/src/core/symbology/qgsarrowsymbollayer.cpp @@ -377,7 +377,7 @@ bool pointsToCircle( QPointF a, QPointF b, QPointF c, QPointF ¢er, qreal &ra cy = bc2.y() - ( cx - bc2.x() ) * bc.x() / bc.y(); } // Radius - radius = sqrt( ( a.x() - cx ) * ( a.x() - cx ) + ( a.y() - cy ) * ( a.y() - cy ) ); + radius = std::sqrt( ( a.x() - cx ) * ( a.x() - cx ) + ( a.y() - cy ) * ( a.y() - cy ) ); // Center center.setX( cx ); center.setY( cy ); diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index 6bcd4122394..33692f47e08 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -1418,7 +1418,7 @@ void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, Q } //values in distance transform are squared - maxDistanceValue = sqrt( dtMaxValue ); + maxDistanceValue = std::sqrt( dtMaxValue ); } else { @@ -1444,7 +1444,7 @@ void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, Q //scale result to fit in the range [0, 1] if ( maxDistanceValue > 0 ) { - pixVal = squaredVal > 0 ? qMin( ( sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0; + pixVal = squaredVal > 0 ? qMin( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0; } else { @@ -2964,7 +2964,7 @@ QgsSymbolLayer *QgsLinePatternFillSymbolLayer::createFromSld( QDomElement &eleme QPointF vectOffset; if ( QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, vectOffset ) ) { - offset = sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) ); + offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) ); } QString uom = element.attribute( QStringLiteral( "uom" ), "" ); diff --git a/src/core/symbology/qgsgraduatedsymbolrenderer.cpp b/src/core/symbology/qgsgraduatedsymbolrenderer.cpp index f91d8217677..5a60b2deb7b 100644 --- a/src/core/symbology/qgsgraduatedsymbolrenderer.cpp +++ b/src/core/symbology/qgsgraduatedsymbolrenderer.cpp @@ -640,7 +640,7 @@ static QList _calcStdDevBreaks( QList values, int classes, QList sd = values[i] - mean; stdDev += sd * sd; } - stdDev = sqrt( stdDev / n ); + stdDev = std::sqrt( stdDev / n ); QList breaks = QgsSymbolLayerUtils::prettyBreaks( ( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes ); for ( int i = 0; i < breaks.count(); i++ ) diff --git a/src/core/symbology/qgsheatmaprenderer.cpp b/src/core/symbology/qgsheatmaprenderer.cpp index c367122a8b4..8d013248be2 100644 --- a/src/core/symbology/qgsheatmaprenderer.cpp +++ b/src/core/symbology/qgsheatmaprenderer.cpp @@ -171,7 +171,7 @@ bool QgsHeatmapRenderer::renderFeature( QgsFeature &feature, QgsRenderContext &c continue; } - double score = weight * quarticKernel( sqrt( distanceSquared ), mRadiusPixels ); + double score = weight * quarticKernel( std::sqrt( distanceSquared ), mRadiusPixels ); double value = mValues.at( index ) + score; if ( value > mCalculatedMaxValue ) { diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index b9b06c4b4a3..771e6605823 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -666,7 +666,7 @@ class MyLine // length double x = ( p2.x() - p1.x() ); double y = ( p2.y() - p1.y() ); - mLength = sqrt( x * x + y * y ); + mLength = std::sqrt( x * x + y * y ); } // return angle in radians @@ -1278,8 +1278,8 @@ void QgsMarkerLineSymbolLayer::renderPolylineCentral( const QPolygonF &points, Q QPointF last = *it; for ( ++it; it != points.constEnd(); ++it ) { - length += sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) + - ( last.y() - it->y() ) * ( last.y() - it->y() ) ); + length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) + + ( last.y() - it->y() ) * ( last.y() - it->y() ) ); last = *it; } @@ -1292,8 +1292,8 @@ void QgsMarkerLineSymbolLayer::renderPolylineCentral( const QPolygonF &points, Q for ( ++it; it != points.constEnd(); ++it ) { next = *it; - next_at += sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) + - ( last.y() - it->y() ) * ( last.y() - it->y() ) ); + next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) + + ( last.y() - it->y() ) * ( last.y() - it->y() ) ); if ( next_at >= length / 2 ) break; // we have reached the center last = *it; diff --git a/src/core/symbology/qgsmarkersymbollayer.cpp b/src/core/symbology/qgsmarkersymbollayer.cpp index 07dcdba31ae..d7b97b257fd 100644 --- a/src/core/symbology/qgsmarkersymbollayer.cpp +++ b/src/core/symbology/qgsmarkersymbollayer.cpp @@ -625,7 +625,7 @@ double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &co switch ( mScaleMethod ) { case QgsSymbol::ScaleArea: - scaledSize = sqrt( scaledSize ); + scaledSize = std::sqrt( scaledSize ); break; case QgsSymbol::ScaleDiameter: break; @@ -1242,7 +1242,7 @@ bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScal switch ( mScaleMethod ) { case QgsSymbol::ScaleArea: - size = sqrt( size ); + size = std::sqrt( size ); break; case QgsSymbol::ScaleDiameter: break; @@ -2055,7 +2055,7 @@ double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, switch ( mScaleMethod ) { case QgsSymbol::ScaleArea: - scaledSize = sqrt( scaledSize ); + scaledSize = std::sqrt( scaledSize ); break; case QgsSymbol::ScaleDiameter: break; @@ -2274,7 +2274,7 @@ bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFa switch ( mScaleMethod ) { case QgsSymbol::ScaleArea: - size = sqrt( size ); + size = std::sqrt( size ); break; case QgsSymbol::ScaleDiameter: break; @@ -2642,7 +2642,7 @@ double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context switch ( mScaleMethod ) { case QgsSymbol::ScaleArea: - scaledSize = sqrt( scaledSize ); + scaledSize = std::sqrt( scaledSize ); break; case QgsSymbol::ScaleDiameter: break; diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index a14769d6f84..96726c1210c 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3604,7 +3604,7 @@ QPointF QgsSymbolLayerUtils::pointOnLineWithDistance( QPointF startPoint, QPoint { double dx = directionPoint.x() - startPoint.x(); double dy = directionPoint.y() - startPoint.y(); - double length = sqrt( dx * dx + dy * dy ); + double length = std::sqrt( dx * dx + dy * dy ); double scaleFactor = distance / length; return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor ); } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 639c8cc18cf..885f3f55197 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -50,7 +50,7 @@ bool QgsAdvancedDigitizingDockWidget::lineCircleIntersection( const QgsPointXY & const double dx = x2 - x1; const double dy = y2 - y1; - const double dr = sqrt( std::pow( dx, 2 ) + std::pow( dy, 2 ) ); + const double dr = std::sqrt( std::pow( dx, 2 ) + std::pow( dy, 2 ) ); const double d = x1 * y2 - x2 * y1; const double disc = std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ); @@ -65,12 +65,12 @@ bool QgsAdvancedDigitizingDockWidget::lineCircleIntersection( const QgsPointXY & // two solutions const int sgnDy = dy < 0 ? -1 : 1; - const double ax = center.x() + ( d * dy + sgnDy * dx * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); - const double ay = center.y() + ( -d * dx + std::fabs( dy ) * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); + const double ax = center.x() + ( d * dy + sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); + const double ay = center.y() + ( -d * dx + std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); const QgsPointXY p1( ax, ay ); - const double bx = center.x() + ( d * dy - sgnDy * dx * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); - const double by = center.y() + ( -d * dx - std::fabs( dy ) * sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); + const double bx = center.x() + ( d * dy - sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); + const double by = center.y() + ( -d * dx - std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) ); const QgsPointXY p2( bx, by ); // snap to nearest intersection @@ -776,7 +776,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) } else { - const double dist = sqrt( point.sqrDist( previousPt ) ); + const double dist = std::sqrt( point.sqrDist( previousPt ) ); if ( dist == 0 ) { // handle case where mouse is over origin and distance constraint is enabled @@ -834,7 +834,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) // --- distance if ( !mDistanceConstraint->isLocked() && previousPointExist ) { - mDistanceConstraint->setValue( sqrt( previousPt.sqrDist( point ) ) ); + mDistanceConstraint->setValue( std::sqrt( previousPt.sqrDist( point ) ) ); } // --- X if ( !mXConstraint->isLocked() ) diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index f43ae1e651f..e44f654ab2a 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -546,8 +546,8 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) double a = 0.5 * triangleLength; double b = tan( rad1 ) * a; - double r = sqrt( x * x + y * y ); - double maxR = sqrt( a * a + b * b ); + double r = std::sqrt( x * x + y * y ); + double maxR = std::sqrt( a * a + b * b ); if ( r > maxR ) { @@ -559,10 +559,10 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) rad0 = fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI ); rad1 = fmod( rad0, ( ( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 ); b = tan( rad1 ) * a; - r = sqrt( a * a + b * b ); + r = std::sqrt( a * a + b * b ); } - double triangleSideLength = sqrt( 3.0 ) * triangleLength; + double triangleSideLength = std::sqrt( 3.0 ) * triangleLength; double newL = ( ( -sin( rad0 ) * r ) / triangleSideLength ) + 0.5; double widthShare = 1.0 - ( std::fabs( newL - 0.5 ) * 2.0 ); double newS = ( ( ( std::cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare; diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp index 0a1ac82fc1b..8b96bb9f527 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp @@ -46,7 +46,7 @@ void QgsGeometrySegmentLengthCheck::collectErrors( QListvertexAt( QgsVertexId( iPart, iRing, iVert ) ); QgsPoint pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); - double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); + double dist = std::sqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); if ( dist < mMinLength ) { errors.append( new QgsGeometryCheckError( this, featureid, QgsPoint( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist, QgsGeometryCheckError::ValueLength ) ); @@ -87,7 +87,7 @@ void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError *error, int QgsPoint pi = geom->vertexAt( error->vidx() ); QgsPoint pj = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + nVerts ) % nVerts ) ); - double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); + double dist = std::sqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); if ( dist >= mMinLength ) { error->setObsolete(); diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp index 64a7f70deb0..fd548f19a5b 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp @@ -57,7 +57,7 @@ namespace QgsGeometryCheckerUtils double nom = std::fabs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() ); double dx = p2.x() - p1.x(); double dy = p2.y() - p1.y(); - return nom / qSqrt( dx * dx + dy * dy ); + return nom / std::sqrt( dx * dx + dy * dy ); } double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ) @@ -74,7 +74,7 @@ namespace QgsGeometryCheckerUtils QgsPoint p1 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, iVert1 ) ); QgsPoint p2 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, jVert1 ) ); double lambdap1 = 0.; - double lambdap2 = qSqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) ); + double lambdap2 = std::sqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) ); QgsVector d; try { diff --git a/src/plugins/georeferencer/qgsgcplistmodel.cpp b/src/plugins/georeferencer/qgsgcplistmodel.cpp index 498588cc940..b73e44ee5a7 100644 --- a/src/plugins/georeferencer/qgsgcplistmodel.cpp +++ b/src/plugins/georeferencer/qgsgcplistmodel.cpp @@ -171,7 +171,7 @@ void QgsGCPListModel::updateModel() } } } - residual = sqrt( dX * dX + dY * dY ); + residual = std::sqrt( dX * dX + dY * dY ); p->setResidual( QPointF( dX, dY ) ); diff --git a/src/plugins/georeferencer/qgsgeorefplugingui.cpp b/src/plugins/georeferencer/qgsgeorefplugingui.cpp index 641ddd569b8..0e1c3e3af98 100644 --- a/src/plugins/georeferencer/qgsgeorefplugingui.cpp +++ b/src/plugins/georeferencer/qgsgeorefplugingui.cpp @@ -1494,7 +1494,7 @@ bool QgsGeorefPluginGui::calculateMeanError( double &error ) const // Calculate the root mean square error, adjusted for degrees of freedom of the transform // Caveat: The number of DoFs is assumed to be even (as each control point fixes two degrees of freedom). - error = sqrt( ( sumVxSquare + sumVySquare ) / ( nPointsEnabled - mGeorefTransform.getMinimumGCPCount() ) ); + error = std::sqrt( ( sumVxSquare + sumVySquare ) / ( nPointsEnabled - mGeorefTransform.getMinimumGCPCount() ) ); return true; } @@ -1745,7 +1745,7 @@ bool QgsGeorefPluginGui::writePDFReportFile( const QString &fileName, const QgsG { QStringList currentGCPStrings; QPointF residual = ( *gcpIt )->residual(); - double residualTot = sqrt( residual.x() * residual.x() + residual.y() * residual.y() ); + double residualTot = std::sqrt( residual.x() * residual.x() + residual.y() * residual.y() ); currentGCPStrings << QString::number( ( *gcpIt )->id() ); if ( ( *gcpIt )->isEnabled() ) diff --git a/src/plugins/georeferencer/qgsleastsquares.cpp b/src/plugins/georeferencer/qgsleastsquares.cpp index 6d1ee228e23..476e1d1025c 100644 --- a/src/plugins/georeferencer/qgsleastsquares.cpp +++ b/src/plugins/georeferencer/qgsleastsquares.cpp @@ -197,11 +197,11 @@ void normalizeCoordinates( const QVector &coords, QVectorpoint( 0 ); - double d = sqrt( std::pow( ( double )( p.x() - p0.x() ), 2.0 ) - + std::pow( ( double )( p.y() - p0.y() ), 2.0 ) ); + double d = std::sqrt( std::pow( ( double )( p.x() - p0.x() ), 2.0 ) + + std::pow( ( double )( p.y() - p0.y() ), 2.0 ) ); QgsDebugMsg( QString( "d = %1" ).arg( d ) ); if ( d < 5 ) // filter 'single' clicks { @@ -1697,8 +1697,8 @@ bool QgsGrassMapcalcObject::tryConnect( QgsGrassMapcalcConnector *connector, if ( mInputConnectors[i] ) continue; // used - double d = sqrt( std::pow( ( double )( mInputPoints[i].x() + pos().x() - p.x() ), 2.0 ) - + std::pow( ( double )( mInputPoints[i].y() + pos().y() - p.y() ), 2.0 ) ); + double d = std::sqrt( std::pow( ( double )( mInputPoints[i].x() + pos().x() - p.x() ), 2.0 ) + + std::pow( ( double )( mInputPoints[i].y() + pos().y() - p.y() ), 2.0 ) ); if ( d <= mSocketHalf ) { @@ -1713,8 +1713,8 @@ bool QgsGrassMapcalcObject::tryConnect( QgsGrassMapcalcConnector *connector, // Output if ( !connector->connected( Out ) && !mOutputConnector ) { - double d = sqrt( std::pow( ( double )( mOutputPoint.x() + pos().x() - p.x() ), 2.0 ) - + std::pow( ( double )( mOutputPoint.y() + pos().y() - p.y() ), 2.0 ) ); + double d = std::sqrt( std::pow( ( double )( mOutputPoint.x() + pos().x() - p.x() ), 2.0 ) + + std::pow( ( double )( mOutputPoint.y() + pos().y() - p.y() ), 2.0 ) ); if ( d <= mSocketHalf ) { @@ -1899,11 +1899,11 @@ void QgsGrassMapcalcConnector::selectEnd( QPoint point ) { mSelectedEnd = -1; - double d0 = sqrt( std::pow( ( double )( point.x() - mPoints[0].x() ), 2.0 ) - + std::pow( ( double )( point.y() - mPoints[0].y() ), 2.0 ) ); + double d0 = std::sqrt( std::pow( ( double )( point.x() - mPoints[0].x() ), 2.0 ) + + std::pow( ( double )( point.y() - mPoints[0].y() ), 2.0 ) ); - double d1 = sqrt( std::pow( ( double )( point.x() - mPoints[1].x() ), 2.0 ) - + std::pow( ( double )( point.y() - mPoints[1].y() ), 2.0 ) ); + double d1 = std::sqrt( std::pow( ( double )( point.x() - mPoints[1].x() ), 2.0 ) + + std::pow( ( double )( point.y() - mPoints[1].y() ), 2.0 ) ); if ( d0 < 15 || d1 < 15 ) diff --git a/src/plugins/topology/topolTest.cpp b/src/plugins/topology/topolTest.cpp index 50a6d4cac27..b169c620ee8 100644 --- a/src/plugins/topology/topolTest.cpp +++ b/src/plugins/topology/topolTest.cpp @@ -1015,7 +1015,7 @@ ErrorList topolTest::checkSegmentLength( double tolerance, QgsVectorLayer *layer for ( int i = 1; i < ls.size(); ++i ) { - distance = sqrt( ls[i - 1].sqrDist( ls[i] ) ); + distance = std::sqrt( ls[i - 1].sqrDist( ls[i] ) ); if ( distance < tolerance ) { fls.clear(); @@ -1040,7 +1040,7 @@ ErrorList topolTest::checkSegmentLength( double tolerance, QgsVectorLayer *layer { for ( int j = 1; j < pol[i].size(); ++j ) { - distance = sqrt( pol[i][j - 1].sqrDist( pol[i][j] ) ); + distance = std::sqrt( pol[i][j - 1].sqrDist( pol[i][j] ) ); if ( distance < tolerance ) { fls.clear(); @@ -1067,7 +1067,7 @@ ErrorList topolTest::checkSegmentLength( double tolerance, QgsVectorLayer *layer QgsPolyline &ls = mls[k]; for ( int i = 1; i < ls.size(); ++i ) { - distance = sqrt( ls[i - 1].sqrDist( ls[i] ) ); + distance = std::sqrt( ls[i - 1].sqrDist( ls[i] ) ); if ( distance < tolerance ) { fls.clear(); diff --git a/src/server/qgswmsprojectparser.cpp b/src/server/qgswmsprojectparser.cpp index 267cc9958c5..01035d0db3c 100644 --- a/src/server/qgswmsprojectparser.cpp +++ b/src/server/qgswmsprojectparser.cpp @@ -1321,7 +1321,7 @@ void QgsWmsProjectParser::addLayers( QDomDocument &doc, if ( version == QLatin1String( "1.1.1" ) ) { double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis - double SCALE_TO_SCALEHINT = OGC_PX_M * sqrt( 2.0 ); + double SCALE_TO_SCALEHINT = OGC_PX_M * std::sqrt( 2.0 ); QDomElement scaleHintElem = doc.createElement( QStringLiteral( "ScaleHint" ) ); scaleHintElem.setAttribute( QStringLiteral( "min" ), QString::number( currentLayer->maximumScale() * SCALE_TO_SCALEHINT ) ); diff --git a/src/server/services/wms/qgswmsgetcapabilities.cpp b/src/server/services/wms/qgswmsgetcapabilities.cpp index 314b2dce406..456483d9533 100644 --- a/src/server/services/wms/qgswmsgetcapabilities.cpp +++ b/src/server/services/wms/qgswmsgetcapabilities.cpp @@ -1001,7 +1001,7 @@ namespace QgsWms if ( version == QLatin1String( "1.1.1" ) ) { double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis - double SCALE_TO_SCALEHINT = OGC_PX_M * sqrt( 2.0 ); + double SCALE_TO_SCALEHINT = OGC_PX_M * std::sqrt( 2.0 ); QDomElement scaleHintElem = doc.createElement( QStringLiteral( "ScaleHint" ) ); scaleHintElem.setAttribute( QStringLiteral( "min" ), QString::number( l->maximumScale() * SCALE_TO_SCALEHINT ) ); diff --git a/tests/bench/qgsbench.cpp b/tests/bench/qgsbench.cpp index c2df882ea9a..ce146e999ee 100644 --- a/tests/bench/qgsbench.cpp +++ b/tests/bench/qgsbench.cpp @@ -248,7 +248,7 @@ void QgsBench::render() if ( i == 0 || d > maxdev[t] ) maxdev[t] = d; } - stdev[t] = sqrt( stdev[t] / mTimes.size() ); + stdev[t] = std::sqrt( stdev[t] / mTimes.size() ); } QMap map; diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index ebec8caaf75..928fe026444 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -3655,7 +3655,7 @@ void TestQgsGeometry::triangle() QVector l_tested, l_t7 = t7.lengths(); l_tested.append( 5 ); l_tested.append( 5 ); - l_tested.append( sqrt( 5 * 5 + 5 * 5 ) ); + l_tested.append( std::sqrt( 5 * 5 + 5 * 5 ) ); QGSCOMPARENEAR( l_tested.at( 0 ), l_t7.at( 0 ), 0.0001 ); QGSCOMPARENEAR( l_tested.at( 1 ), l_t7.at( 1 ), 0.0001 ); QGSCOMPARENEAR( l_tested.at( 2 ), l_t7.at( 2 ), 0.0001 ); @@ -3923,11 +3923,11 @@ void TestQgsGeometry::ellipse() // fromFoci // horizontal QgsEllipse elp_hor = QgsEllipse().fromFoci( QgsPoint( -4, 0 ), QgsPoint( 4, 0 ), QgsPoint( 0, 4 ) ); - QVERIFY( QgsEllipse( QgsPoint( 0, 0 ), sqrt( 32.0 ), sqrt( 16.0 ), 90.0 ) == elp_hor ); + QVERIFY( QgsEllipse( QgsPoint( 0, 0 ), std::sqrt( 32.0 ), std::sqrt( 16.0 ), 90.0 ) == elp_hor ); QGSCOMPARENEARPOINT( QgsPoint( 4, 0 ), elp_hor.foci().at( 0 ), 1e-8 ); QGSCOMPARENEARPOINT( QgsPoint( -4, 0 ), elp_hor.foci().at( 1 ), 1e-8 ); elp_hor = QgsEllipse().fromFoci( QgsPoint( 4, 0 ), QgsPoint( -4, 0 ), QgsPoint( 0, 4 ) ); - QVERIFY( QgsEllipse( QgsPoint( 0, 0 ), sqrt( 32.0 ), sqrt( 16.0 ), 270.0 ) == elp_hor ); + QVERIFY( QgsEllipse( QgsPoint( 0, 0 ), std::sqrt( 32.0 ), std::sqrt( 16.0 ), 270.0 ) == elp_hor ); QGSCOMPARENEARPOINT( QgsPoint( -4, 0 ), elp_hor.foci().at( 0 ), 1e-8 ); QGSCOMPARENEARPOINT( QgsPoint( 4, 0 ), elp_hor.foci().at( 1 ), 1e-8 ); diff --git a/tests/src/core/testqgsgeometryutils.cpp b/tests/src/core/testqgsgeometryutils.cpp index 1188741b7d6..98bc1688a30 100644 --- a/tests/src/core/testqgsgeometryutils.cpp +++ b/tests/src/core/testqgsgeometryutils.cpp @@ -485,7 +485,7 @@ void TestQgsGeometryUtils::testCircleCenterRadius_data() QTest::addColumn( "expectedCenterX" ); QTest::addColumn( "expectedCenterY" ); - QTest::newRow( "circleCenterRadius1" ) << 1.0 << 1.0 << 5.0 << 7.0 << 1.0 << 1.0 << sqrt( 13.0 ) << 3.0 << 4.0; + QTest::newRow( "circleCenterRadius1" ) << 1.0 << 1.0 << 5.0 << 7.0 << 1.0 << 1.0 << std::sqrt( 13.0 ) << 3.0 << 4.0; QTest::newRow( "circleCenterRadius1" ) << 0.0 << 2.0 << 2.0 << 2.0 << 0.0 << 2.0 << 1.0 << 1.0 << 2.0; } diff --git a/tests/src/core/testqgspoint.cpp b/tests/src/core/testqgspoint.cpp index f6383e6fe97..ecd4df82957 100644 --- a/tests/src/core/testqgspoint.cpp +++ b/tests/src/core/testqgspoint.cpp @@ -711,7 +711,7 @@ void TestQgsPointXY::vector() // length QCOMPARE( v1.length(), 0.0 ); - QVERIFY( qgsDoubleNear( v2.length(), sqrt( 5.0 ), 0.000000001 ) ); + QVERIFY( qgsDoubleNear( v2.length(), std::sqrt( 5.0 ), 0.000000001 ) ); // perpVector QCOMPARE( QgsVector( 2, 3 ).perpVector().x(), -3.0 ); diff --git a/tests/src/core/testqgspointlocator.cpp b/tests/src/core/testqgspointlocator.cpp index 9adf597c701..0fb8b25de43 100644 --- a/tests/src/core/testqgspointlocator.cpp +++ b/tests/src/core/testqgspointlocator.cpp @@ -110,7 +110,7 @@ class TestQgsPointLocator : public QObject QCOMPARE( m.layer(), mVL ); QCOMPARE( m.featureId(), ( QgsFeatureId )1 ); QCOMPARE( m.point(), QgsPointXY( 1, 1 ) ); - QCOMPARE( m.distance(), sqrt( 2.0 ) ); + QCOMPARE( m.distance(), std::sqrt( 2.0 ) ); QCOMPARE( m.vertexIndex(), 2 ); } @@ -158,7 +158,7 @@ class TestQgsPointLocator : public QObject QCOMPARE( lst[1].point(), QgsPointXY( 1, 1 ) ); QCOMPARE( lst[1].distance(), 1. ); QCOMPARE( lst[2].point(), QgsPointXY( 0, 1 ) ); - QCOMPARE( lst[2].distance(), sqrt( 2 ) ); + QCOMPARE( lst[2].distance(), std::sqrt( 2 ) ); QgsPointLocator::MatchList lst2 = loc.verticesInRect( QgsPointXY( 1, 0 ), 1 ); QCOMPARE( lst2.count(), 2 ); From ad89193b053600b2f2b1f8b6869481b2635dce80 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:24:11 +1000 Subject: [PATCH 124/364] qFuzzyIsNull -> qgsDoubleNear --- src/analysis/vector/qgsgeometrysnapper.cpp | 2 +- src/core/geometry/qgsgeometryutils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analysis/vector/qgsgeometrysnapper.cpp b/src/analysis/vector/qgsgeometrysnapper.cpp index 11fde6e8fb8..d0fe08f3df0 100644 --- a/src/analysis/vector/qgsgeometrysnapper.cpp +++ b/src/analysis/vector/qgsgeometrysnapper.cpp @@ -58,7 +58,7 @@ bool QgsSnapIndex::SegmentSnapItem::getIntersection( const QgsPoint &p1, const Q double vl = v.length(); double wl = w.length(); - if ( qFuzzyIsNull( vl ) || qFuzzyIsNull( wl ) ) + if ( qgsDoubleNear( vl, 0, 0.000000000001 ) || qgsDoubleNear( wl, 0, 0.000000000001 ) ) { return false; } diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index ba13f8fdb9d..b1670fb2cf4 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -344,7 +344,7 @@ bool QgsGeometryUtils::segmentIntersection( const QgsPoint &p1, const QgsPoint & double vl = v.length(); double wl = w.length(); - if ( qFuzzyIsNull( vl ) || qFuzzyIsNull( wl ) ) + if ( qgsDoubleNear( vl, 0, 0.000000000001 ) || qgsDoubleNear( wl, 0, 0.000000000001 ) ) { return false; } From d3854e9e5aacf849b7f21f2f472262e6a2b798e8 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:33:50 +1000 Subject: [PATCH 125/364] Remove use of some other q* functions which are implemented in std --- .../interpolation/qgsinterpolator.cpp | 2 +- .../interpolation/qgstininterpolator.cpp | 2 +- src/analysis/raster/qgsalignraster.cpp | 2 +- src/analysis/vector/qgszonalstatistics.cpp | 2 +- src/app/qgsmergeattributesdialog.cpp | 2 +- src/app/qgsrasterlayerproperties.cpp | 6 +-- src/app/qgsstatisticalsummarydockwidget.cpp | 2 +- src/core/composer/qgscomposernodesitem.cpp | 4 +- src/core/dxf/qgsdxfexport.cpp | 2 +- src/core/expression/qgsexpressionutils.h | 4 +- src/core/geometry/qgscurve.cpp | 2 +- src/core/geometry/qgspoint.cpp | 14 +++--- src/core/geometry/qgsrectangle.cpp | 4 +- src/core/qgsaggregatecalculator.cpp | 2 +- src/core/qgsclipper.h | 4 +- src/core/qgscolorramp.cpp | 4 +- src/core/qgscoordinatetransform.cpp | 2 +- src/core/qgspointxy.cpp | 4 +- src/core/qgsstatisticalsummary.h | 6 +-- src/core/qgsvectorlayerdiagramprovider.cpp | 2 +- src/core/raster/qgscolorrampshader.cpp | 2 +- src/core/raster/qgsraster.cpp | 2 +- src/core/raster/qgsrasterblock.cpp | 4 +- src/core/raster/qgsrasterblock.h | 2 +- src/core/raster/qgsrasterchecker.cpp | 2 +- src/core/raster/qgsrasterinterface.cpp | 4 +- src/core/raster/qgsrastertransparency.cpp | 4 +- src/gui/qgsrasterlayersaveasdialog.cpp | 4 +- .../qgsmultibandcolorrendererwidget.cpp | 4 +- .../raster/qgsrastertransparencywidget.cpp | 6 +-- .../qgssinglebandgrayrendererwidget.cpp | 4 +- ...qgssinglebandpseudocolorrendererwidget.cpp | 10 ++-- .../qgsdelimitedtextprovider.cpp | 2 +- src/providers/gdal/qgsgdalprovider.cpp | 2 +- .../grass/qgsgrassrasterprovider.cpp | 2 +- src/providers/wcs/qgswcsprovider.cpp | 2 +- tests/src/core/testqgsgeometry.cpp | 32 ++++++------- tests/src/core/testqgsgeometryutils.cpp | 4 +- tests/src/core/testqgsstatisticalsummary.cpp | 48 +++++++++---------- 39 files changed, 106 insertions(+), 106 deletions(-) diff --git a/src/analysis/interpolation/qgsinterpolator.cpp b/src/analysis/interpolation/qgsinterpolator.cpp index a1d6450da04..ae70f1b526a 100644 --- a/src/analysis/interpolation/qgsinterpolator.cpp +++ b/src/analysis/interpolation/qgsinterpolator.cpp @@ -82,7 +82,7 @@ int QgsInterpolator::cacheBaseData() continue; } attributeValue = attributeVariant.toDouble( &attributeConversionOk ); - if ( !attributeConversionOk || qIsNaN( attributeValue ) ) //don't consider vertices with attributes like 'nan' for the interpolation + if ( !attributeConversionOk || std::isnan( attributeValue ) ) //don't consider vertices with attributes like 'nan' for the interpolation { continue; } diff --git a/src/analysis/interpolation/qgstininterpolator.cpp b/src/analysis/interpolation/qgstininterpolator.cpp index 332fb08f82e..716c58605b0 100644 --- a/src/analysis/interpolation/qgstininterpolator.cpp +++ b/src/analysis/interpolation/qgstininterpolator.cpp @@ -176,7 +176,7 @@ int QgsTINInterpolator::insertData( QgsFeature *f, bool zCoord, int attr, InputT return 3; } attributeValue = attributeVariant.toDouble( &attributeConversionOk ); - if ( !attributeConversionOk || qIsNaN( attributeValue ) ) //don't consider vertices with attributes like 'nan' for the interpolation + if ( !attributeConversionOk || std::isnan( attributeValue ) ) //don't consider vertices with attributes like 'nan' for the interpolation { return 4; } diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 97a6eec3457..45b890582ab 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -390,7 +390,7 @@ void QgsAlignRaster::dump() const int QgsAlignRaster::suggestedReferenceLayer() const { int bestIndex = -1; - double bestCellArea = qInf(); + double bestCellArea = INFINITY; QSizeF cs; int i = 0; diff --git a/src/analysis/vector/qgszonalstatistics.cpp b/src/analysis/vector/qgszonalstatistics.cpp index 7513c925b63..4676f89daf4 100644 --- a/src/analysis/vector/qgszonalstatistics.cpp +++ b/src/analysis/vector/qgszonalstatistics.cpp @@ -497,7 +497,7 @@ void QgsZonalStatistics::statisticsFromPreciseIntersection( const QgsGeometry &p bool QgsZonalStatistics::validPixel( float value ) const { - if ( value == mInputNodataValue || qIsNaN( value ) ) + if ( value == mInputNodataValue || std::isnan( value ) ) { return false; } diff --git a/src/app/qgsmergeattributesdialog.cpp b/src/app/qgsmergeattributesdialog.cpp index 04def05925c..904678f64a8 100644 --- a/src/app/qgsmergeattributesdialog.cpp +++ b/src/app/qgsmergeattributesdialog.cpp @@ -369,7 +369,7 @@ QVariant QgsMergeAttributesDialog::calcStatistic( int col, QgsStatisticalSummary summary.calculate( values ); double val = summary.statistic( stat ); - return qIsNaN( val ) ? QVariant( QVariant::Double ) : val; + return std::isnan( val ) ? QVariant( QVariant::Double ) : val; } QVariant QgsMergeAttributesDialog::concatenationAttribute( int col ) diff --git a/src/app/qgsrasterlayerproperties.cpp b/src/app/qgsrasterlayerproperties.cpp index 95a540b0d60..729ceda1d2f 100644 --- a/src/app/qgsrasterlayerproperties.cpp +++ b/src/app/qgsrasterlayerproperties.cpp @@ -669,7 +669,7 @@ void QgsRasterLayerProperties::sync() mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSourceNoDataValue( 1 ) ); - bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !qIsNaN( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ); + bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !std::isnan( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ); mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData ); lblSrcNoDataValue->setEnabled( enableSrcNoData ); @@ -1231,14 +1231,14 @@ void QgsRasterLayerProperties::setTransparencyCell( int row, int column, double case Qgis::Float32: case Qgis::Float64: lineEdit->setValidator( new QDoubleValidator( nullptr ) ); - if ( !qIsNaN( value ) ) + if ( !std::isnan( value ) ) { valueString = QgsRasterBlock::printValue( value ); } break; default: lineEdit->setValidator( new QIntValidator( nullptr ) ); - if ( !qIsNaN( value ) ) + if ( !std::isnan( value ) ) { valueString = QString::number( static_cast( value ) ); } diff --git a/src/app/qgsstatisticalsummarydockwidget.cpp b/src/app/qgsstatisticalsummarydockwidget.cpp index 17499de0489..f74c31ab45c 100644 --- a/src/app/qgsstatisticalsummarydockwidget.cpp +++ b/src/app/qgsstatisticalsummarydockwidget.cpp @@ -185,7 +185,7 @@ void QgsStatisticalSummaryDockWidget::updateNumericStatistics( bool selectedOnly { double val = stats.statistic( stat ); addRow( row, QgsStatisticalSummary::displayName( stat ), - qIsNaN( val ) ? QString() : QString::number( val ), + std::isnan( val ) ? QString() : QString::number( val ), stats.count() != 0 ); row++; } diff --git a/src/core/composer/qgscomposernodesitem.cpp b/src/core/composer/qgscomposernodesitem.cpp index c6548002ea1..1075e2bdc33 100644 --- a/src/core/composer/qgscomposernodesitem.cpp +++ b/src/core/composer/qgscomposernodesitem.cpp @@ -76,7 +76,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, const double b = pt1.y() - coef * pt1.x(); double distance = std::numeric_limits::max(); - if ( qIsInf( coef ) ) + if ( std::isinf( coef ) ) distance = std::fabs( pt1.x() - start.x() ); else { @@ -84,7 +84,7 @@ bool QgsComposerNodesItem::addNode( QPointF pt, const double b2 = start.y() - coef2 * start.x(); QPointF inter; - if ( qIsInf( coef2 ) ) + if ( std::isinf( coef2 ) ) { distance = std::fabs( pt1.y() - start.y() ); inter.setX( start.x() ); diff --git a/src/core/dxf/qgsdxfexport.cpp b/src/core/dxf/qgsdxfexport.cpp index 45b1d34926f..9e4b4069070 100644 --- a/src/core/dxf/qgsdxfexport.cpp +++ b/src/core/dxf/qgsdxfexport.cpp @@ -443,7 +443,7 @@ void QgsDxfExport::writeGroup( int code, const QgsPoint &p ) { writeGroup( code + 10, p.x() ); writeGroup( code + 20, p.y() ); - if ( p.is3D() && qIsFinite( p.z() ) ) + if ( p.is3D() && std::isfinite( p.z() ) ) writeGroup( code + 30, p.z() ); } diff --git a/src/core/expression/qgsexpressionutils.h b/src/core/expression/qgsexpressionutils.h index 8d88a6067bd..a19acf887e3 100644 --- a/src/core/expression/qgsexpressionutils.h +++ b/src/core/expression/qgsexpressionutils.h @@ -144,7 +144,7 @@ class QgsExpressionUtils { bool ok; double val = v.toString().toDouble( &ok ); - ok = ok && qIsFinite( val ) && !qIsNaN( val ); + ok = ok && std::isfinite( val ) && !std::isnan( val ); return ok; } return false; @@ -191,7 +191,7 @@ class QgsExpressionUtils { bool ok; double x = value.toDouble( &ok ); - if ( !ok || qIsNaN( x ) || !qIsFinite( x ) ) + if ( !ok || std::isnan( x ) || !std::isfinite( x ) ) { parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) ); return 0; diff --git a/src/core/geometry/qgscurve.cpp b/src/core/geometry/qgscurve.cpp index 08fa2861c76..12aaa33da97 100644 --- a/src/core/geometry/qgscurve.cpp +++ b/src/core/geometry/qgscurve.cpp @@ -35,7 +35,7 @@ bool QgsCurve::isClosed() const bool closed = qgsDoubleNear( start.x(), end.x(), 1E-8 ) && qgsDoubleNear( start.y(), end.y(), 1E-8 ); if ( is3D() && closed ) - closed &= qgsDoubleNear( start.z(), end.z(), 1E-8 ) || ( qIsNaN( start.z() ) && qIsNaN( end.z() ) ); + closed &= qgsDoubleNear( start.z(), end.z(), 1E-8 ) || ( std::isnan( start.z() ) && std::isnan( end.z() ) ); return closed; } diff --git a/src/core/geometry/qgspoint.cpp b/src/core/geometry/qgspoint.cpp index e0102083c7f..4038b1ee132 100644 --- a/src/core/geometry/qgspoint.cpp +++ b/src/core/geometry/qgspoint.cpp @@ -43,14 +43,14 @@ QgsPoint::QgsPoint( double x, double y, double z, double m, QgsWkbTypes::Type wk Q_ASSERT( QgsWkbTypes::flatType( wkbType ) == QgsWkbTypes::Point ); mWkbType = wkbType; } - else if ( qIsNaN( z ) ) + else if ( std::isnan( z ) ) { - if ( qIsNaN( m ) ) + if ( std::isnan( m ) ) mWkbType = QgsWkbTypes::Point; else mWkbType = QgsWkbTypes::PointM; } - else if ( qIsNaN( m ) ) + else if ( std::isnan( m ) ) mWkbType = QgsWkbTypes::PointZ; else mWkbType = QgsWkbTypes::PointZM; @@ -101,9 +101,9 @@ bool QgsPoint::operator==( const QgsPoint &pt ) const equal &= qgsDoubleNear( pt.x(), mX, 1E-8 ); equal &= qgsDoubleNear( pt.y(), mY, 1E-8 ); if ( QgsWkbTypes::hasZ( type ) ) - equal &= qgsDoubleNear( pt.z(), mZ, 1E-8 ) || ( qIsNaN( pt.z() ) && qIsNaN( mZ ) ); + equal &= qgsDoubleNear( pt.z(), mZ, 1E-8 ) || ( std::isnan( pt.z() ) && std::isnan( mZ ) ); if ( QgsWkbTypes::hasM( type ) ) - equal &= qgsDoubleNear( pt.m(), mM, 1E-8 ) || ( qIsNaN( pt.m() ) && qIsNaN( mM ) ); + equal &= qgsDoubleNear( pt.m(), mM, 1E-8 ) || ( std::isnan( pt.m() ) && std::isnan( mM ) ); return equal; } @@ -501,7 +501,7 @@ double QgsPoint::distanceSquared( const QgsPoint &other ) const double QgsPoint::distance3D( double x, double y, double z ) const { double zDistSquared = 0.0; - if ( is3D() || !qIsNaN( z ) ) + if ( is3D() || !std::isnan( z ) ) zDistSquared = ( mZ - z ) * ( mZ - z ); return sqrt( ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared ); @@ -519,7 +519,7 @@ double QgsPoint::distance3D( const QgsPoint &other ) const double QgsPoint::distanceSquared3D( double x, double y, double z ) const { double zDistSquared = 0.0; - if ( is3D() || !qIsNaN( z ) ) + if ( is3D() || !std::isnan( z ) ) zDistSquared = ( mZ - z ) * ( mZ - z ); return ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared; diff --git a/src/core/geometry/qgsrectangle.cpp b/src/core/geometry/qgsrectangle.cpp index 4ad6b3ac3e7..b9a5e903b55 100644 --- a/src/core/geometry/qgsrectangle.cpp +++ b/src/core/geometry/qgsrectangle.cpp @@ -377,11 +377,11 @@ QgsRectangle &QgsRectangle::operator=( const QgsRectangle &r ) bool QgsRectangle::isFinite() const { - if ( qIsInf( mXmin ) || qIsInf( mYmin ) || qIsInf( mXmax ) || qIsInf( mYmax ) ) + if ( std::isinf( mXmin ) || std::isinf( mYmin ) || std::isinf( mXmax ) || std::isinf( mYmax ) ) { return false; } - if ( qIsNaN( mXmin ) || qIsNaN( mYmin ) || qIsNaN( mXmax ) || qIsNaN( mYmax ) ) + if ( std::isnan( mXmin ) || std::isnan( mYmin ) || std::isnan( mXmax ) || std::isnan( mYmax ) ) { return false; } diff --git a/src/core/qgsaggregatecalculator.cpp b/src/core/qgsaggregatecalculator.cpp index 3aab5a37346..00ad14f8de8 100644 --- a/src/core/qgsaggregatecalculator.cpp +++ b/src/core/qgsaggregatecalculator.cpp @@ -434,7 +434,7 @@ QVariant QgsAggregateCalculator::calculateNumericAggregate( QgsFeatureIterator & } s.finalize(); double val = s.statistic( stat ); - return qIsNaN( val ) ? QVariant() : val; + return std::isnan( val ) ? QVariant() : val; } QVariant QgsAggregateCalculator::calculateStringAggregate( QgsFeatureIterator &fit, int attr, QgsExpression *expression, diff --git a/src/core/qgsclipper.h b/src/core/qgsclipper.h index 4f7e906293a..4750fa6e744 100644 --- a/src/core/qgsclipper.h +++ b/src/core/qgsclipper.h @@ -234,8 +234,8 @@ inline void QgsClipper::trimFeatureToBoundary( // look at each edge of the polygon in turn //ignore segments with nan or inf coordinates - if ( qIsNaN( inX[i2] ) || qIsNaN( inY[i2] ) || qIsInf( inX[i2] ) || qIsInf( inY[i2] ) - || qIsNaN( inX[i1] ) || qIsNaN( inY[i1] ) || qIsInf( inX[i1] ) || qIsInf( inY[i1] ) ) + if ( std::isnan( inX[i2] ) || std::isnan( inY[i2] ) || std::isinf( inX[i2] ) || std::isinf( inY[i2] ) + || std::isnan( inX[i1] ) || std::isnan( inY[i1] ) || std::isinf( inX[i1] ) || std::isinf( inY[i1] ) ) { i1 = i2; continue; diff --git a/src/core/qgscolorramp.cpp b/src/core/qgscolorramp.cpp index 715333a827a..8182256b14b 100644 --- a/src/core/qgscolorramp.cpp +++ b/src/core/qgscolorramp.cpp @@ -29,7 +29,7 @@ static QColor _interpolate( const QColor &c1, const QColor &c2, const double value ) { - if ( qIsNaN( value ) ) return c2; + if ( std::isnan( value ) ) return c2; qreal r = ( c1.redF() + value * ( c2.redF() - c1.redF() ) ); qreal g = ( c1.greenF() + value * ( c2.greenF() - c1.greenF() ) ); @@ -433,7 +433,7 @@ QColor QgsRandomColorRamp::color( double value ) const int maxVal = 255; //if value is nan, then use last precalculated color - int colorIndex = ( !qIsNaN( value ) ? value : 1 ) * ( mTotalColorCount - 1 ); + int colorIndex = ( !std::isnan( value ) ? value : 1 ) * ( mTotalColorCount - 1 ); if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex ) { //use precalculated hue diff --git a/src/core/qgscoordinatetransform.cpp b/src/core/qgscoordinatetransform.cpp index 9b971593760..08730091e58 100644 --- a/src/core/qgscoordinatetransform.cpp +++ b/src/core/qgscoordinatetransform.cpp @@ -410,7 +410,7 @@ QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &r for ( int i = 0; i < nXPoints * nYPoints; i++ ) { - if ( !qIsFinite( x[i] ) || !qIsFinite( y[i] ) ) + if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) ) { continue; } diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index a7ffde758cb..73c1b57b4d5 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -53,8 +53,8 @@ QString QgsPointXY::toString() const QString QgsPointXY::toString( int precision ) const { - QString x = qIsFinite( mX ) ? QString::number( mX, 'f', precision ) : QObject::tr( "infinite" ); - QString y = qIsFinite( mY ) ? QString::number( mY, 'f', precision ) : QObject::tr( "infinite" ); + QString x = std::isfinite( mX ) ? QString::number( mX, 'f', precision ) : QObject::tr( "infinite" ); + QString y = std::isfinite( mY ) ? QString::number( mY, 'f', precision ) : QObject::tr( "infinite" ); return QStringLiteral( "%1,%2" ).arg( x, y ); } diff --git a/src/core/qgsstatisticalsummary.h b/src/core/qgsstatisticalsummary.h index 00cb9af8a07..1412c023dc4 100644 --- a/src/core/qgsstatisticalsummary.h +++ b/src/core/qgsstatisticalsummary.h @@ -18,7 +18,7 @@ #include #include - +#include #include "qgis_core.h" /*************************************************************************** @@ -179,7 +179,7 @@ class CORE_EXPORT QgsStatisticalSummary /** Returns calculated range (difference between maximum and minimum values). A NaN value may be returned if the range cannot * be calculated. */ - double range() const { return qIsNaN( mMax ) || qIsNaN( mMin ) ? std::numeric_limits::quiet_NaN() : mMax - mMin; } + double range() const { return std::isnan( mMax ) || std::isnan( mMin ) ? std::numeric_limits::quiet_NaN() : mMax - mMin; } /** Returns population standard deviation. This is only calculated if Statistic::StDev has * been specified in the constructor or via setStatistics. A NaN value may be returned if the standard deviation cannot @@ -239,7 +239,7 @@ class CORE_EXPORT QgsStatisticalSummary * \see firstQuartile * \see thirdQuartile */ - double interQuartileRange() const { return qIsNaN( mThirdQuartile ) || qIsNaN( mFirstQuartile ) ? std::numeric_limits::quiet_NaN() : mThirdQuartile - mFirstQuartile; } + double interQuartileRange() const { return std::isnan( mThirdQuartile ) || std::isnan( mFirstQuartile ) ? std::numeric_limits::quiet_NaN() : mThirdQuartile - mFirstQuartile; } /** Returns the friendly display name for a statistic * \param statistic statistic to return name for diff --git a/src/core/qgsvectorlayerdiagramprovider.cpp b/src/core/qgsvectorlayerdiagramprovider.cpp index cdfe2680005..a47f042e5e9 100644 --- a/src/core/qgsvectorlayerdiagramprovider.cpp +++ b/src/core/qgsvectorlayerdiagramprovider.cpp @@ -266,7 +266,7 @@ QgsLabelFeature *QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature &fea ddPosX = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionX, context.expressionContext(), std::numeric_limits::quiet_NaN() ); ddPosY = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionY, context.expressionContext(), std::numeric_limits::quiet_NaN() ); - ddPos = !qIsNaN( ddPosX ) && !qIsNaN( ddPosY ); + ddPos = !std::isnan( ddPosX ) && !std::isnan( ddPosY ); if ( ddPos ) { diff --git a/src/core/raster/qgscolorrampshader.cpp b/src/core/raster/qgscolorrampshader.cpp index 485728bd816..740f02d3047 100644 --- a/src/core/raster/qgscolorrampshader.cpp +++ b/src/core/raster/qgscolorrampshader.cpp @@ -313,7 +313,7 @@ bool QgsColorRampShader::shade( double value, int *returnRedValue, int *returnGr { return false; } - if ( qIsNaN( value ) || qIsInf( value ) ) + if ( std::isnan( value ) || std::isinf( value ) ) return false; int colorRampItemListCount = mColorRampItemList.count(); diff --git a/src/core/raster/qgsraster.cpp b/src/core/raster/qgsraster.cpp index 3b4cd392f77..16e98f686e7 100644 --- a/src/core/raster/qgsraster.cpp +++ b/src/core/raster/qgsraster.cpp @@ -34,7 +34,7 @@ bool QgsRaster::isRepresentableValue( double value, Qgis::DataType dataType ) case Qgis::Int32: return value >= std::numeric_limits::min() && value <= std::numeric_limits::max(); case Qgis::Float32: - return qIsNaN( value ) || qIsInf( value ) || + return std::isnan( value ) || std::isinf( value ) || ( value >= -std::numeric_limits::max() && value <= std::numeric_limits::max() ); default: return true; diff --git a/src/core/raster/qgsrasterblock.cpp b/src/core/raster/qgsrasterblock.cpp index 39944842fe9..c12ae7d918b 100644 --- a/src/core/raster/qgsrasterblock.cpp +++ b/src/core/raster/qgsrasterblock.cpp @@ -262,9 +262,9 @@ void QgsRasterBlock::resetNoDataValue() bool QgsRasterBlock::isNoDataValue( double value, double noDataValue ) { // TODO: optimize no data value test by memcmp() - // More precise would be qIsNaN(value) && qIsNaN(noDataValue(bandNo)), but probably + // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably // not important and slower - if ( qIsNaN( value ) || + if ( std::isnan( value ) || qgsDoubleNear( value, noDataValue ) ) { return true; diff --git a/src/core/raster/qgsrasterblock.h b/src/core/raster/qgsrasterblock.h index b91979d6d41..5e6dc866f6e 100644 --- a/src/core/raster/qgsrasterblock.h +++ b/src/core/raster/qgsrasterblock.h @@ -532,7 +532,7 @@ inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP { - return qIsNaN( value ) || qgsDoubleNear( value, mNoDataValue ); + return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue ); } #endif diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index f59739ab09d..d60968d6809 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -224,7 +224,7 @@ void QgsRasterChecker::compare( const QString ¶mName, int verifiedVal, int e bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double tolerance ) { // values may be nan - return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( std::fabs( verifiedVal - expectedVal ) <= tolerance ); + return ( std::isnan( verifiedVal ) && std::isnan( expectedVal ) ) || ( std::fabs( verifiedVal - expectedVal ) <= tolerance ); } void QgsRasterChecker::compare( const QString ¶mName, double verifiedVal, double expectedVal, QString &report, bool &ok, double tolerance ) diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index 33ac2cab1d1..985996931e0 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -260,7 +260,7 @@ void QgsRasterInterface::initHistogram( QgsRasterHistogram &histogram, int mySrcDataType = sourceDataType( bandNo ); - if ( qIsNaN( histogram.minimum ) ) + if ( std::isnan( histogram.minimum ) ) { // TODO: this was OK when stats/histogram were calced in provider, // but what TODO in other interfaces? Check for mInput for now. @@ -277,7 +277,7 @@ void QgsRasterInterface::initHistogram( QgsRasterHistogram &histogram, histogram.minimum = stats.minimumValue; } } - if ( qIsNaN( histogram.maximum ) ) + if ( std::isnan( histogram.maximum ) ) { if ( !mInput && mySrcDataType == Qgis::Byte ) { diff --git a/src/core/raster/qgsrastertransparency.cpp b/src/core/raster/qgsrastertransparency.cpp index a3524fb813e..c7be553cb56 100644 --- a/src/core/raster/qgsrastertransparency.cpp +++ b/src/core/raster/qgsrastertransparency.cpp @@ -104,7 +104,7 @@ void QgsRasterTransparency::setTransparentThreeValuePixelList( const QListsetValidator( new QDoubleValidator( nullptr ) ); - if ( !qIsNaN( value ) ) + if ( !std::isnan( value ) ) { valueString = QgsRasterBlock::printValue( value ); } break; default: lineEdit->setValidator( new QIntValidator( nullptr ) ); - if ( !qIsNaN( value ) ) + if ( !std::isnan( value ) ) { valueString = QString::number( static_cast( value ) ); } diff --git a/src/gui/raster/qgsmultibandcolorrendererwidget.cpp b/src/gui/raster/qgsmultibandcolorrendererwidget.cpp index a1f29b1a7c6..088d9e4859e 100644 --- a/src/gui/raster/qgsmultibandcolorrendererwidget.cpp +++ b/src/gui/raster/qgsmultibandcolorrendererwidget.cpp @@ -281,7 +281,7 @@ void QgsMultiBandColorRendererWidget::loadMinMax( int bandNo, double min, double } mDisableMinMaxWidgetRefresh = true; - if ( qIsNaN( min ) ) + if ( std::isnan( min ) ) { myMinLineEdit->clear(); } @@ -290,7 +290,7 @@ void QgsMultiBandColorRendererWidget::loadMinMax( int bandNo, double min, double myMinLineEdit->setText( QString::number( min ) ); } - if ( qIsNaN( max ) ) + if ( std::isnan( max ) ) { myMaxLineEdit->clear(); } diff --git a/src/gui/raster/qgsrastertransparencywidget.cpp b/src/gui/raster/qgsrastertransparencywidget.cpp index 7762e4f7542..1916821c952 100644 --- a/src/gui/raster/qgsrastertransparencywidget.cpp +++ b/src/gui/raster/qgsrastertransparencywidget.cpp @@ -92,7 +92,7 @@ void QgsRasterTransparencyWidget::syncToLayer() mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSourceNoDataValue( 1 ) ); - bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !qIsNaN( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ); + bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !std::isnan( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ); mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData ); lblSrcNoDataValue->setEnabled( enableSrcNoData ); @@ -586,14 +586,14 @@ void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, doub case Qgis::Float32: case Qgis::Float64: lineEdit->setValidator( new QDoubleValidator( nullptr ) ); - if ( !qIsNaN( value ) ) + if ( !std::isnan( value ) ) { valueString = QgsRasterBlock::printValue( value ); } break; default: lineEdit->setValidator( new QIntValidator( nullptr ) ); - if ( !qIsNaN( value ) ) + if ( !std::isnan( value ) ) { valueString = QString::number( static_cast( value ) ); } diff --git a/src/gui/raster/qgssinglebandgrayrendererwidget.cpp b/src/gui/raster/qgssinglebandgrayrendererwidget.cpp index 2b7ef25dd2a..6e185afc790 100644 --- a/src/gui/raster/qgssinglebandgrayrendererwidget.cpp +++ b/src/gui/raster/qgssinglebandgrayrendererwidget.cpp @@ -144,7 +144,7 @@ void QgsSingleBandGrayRendererWidget::loadMinMax( int bandNo, double min, double QgsDebugMsg( QString( "theBandNo = %1 min = %2 max = %3" ).arg( bandNo ).arg( min ).arg( max ) ); mDisableMinMaxWidgetRefresh = true; - if ( qIsNaN( min ) ) + if ( std::isnan( min ) ) { mMinLineEdit->clear(); } @@ -153,7 +153,7 @@ void QgsSingleBandGrayRendererWidget::loadMinMax( int bandNo, double min, double mMinLineEdit->setText( QString::number( min ) ); } - if ( qIsNaN( max ) ) + if ( std::isnan( max ) ) { mMaxLineEdit->clear(); } diff --git a/src/gui/raster/qgssinglebandpseudocolorrendererwidget.cpp b/src/gui/raster/qgssinglebandpseudocolorrendererwidget.cpp index 99274d6d5ae..450075354b0 100644 --- a/src/gui/raster/qgssinglebandpseudocolorrendererwidget.cpp +++ b/src/gui/raster/qgssinglebandpseudocolorrendererwidget.cpp @@ -329,7 +329,7 @@ void QgsSingleBandPseudoColorRendererWidget::on_mDeleteEntryButton_clicked() void QgsSingleBandPseudoColorRendererWidget::classify() { std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() ); - if ( !ramp || qIsNaN( lineEditValue( mMinLineEdit ) ) || qIsNaN( lineEditValue( mMaxLineEdit ) ) ) + if ( !ramp || std::isnan( lineEditValue( mMinLineEdit ) ) || std::isnan( lineEditValue( mMaxLineEdit ) ) ) { return; } @@ -745,7 +745,7 @@ void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int bandNo, double min, QgsDebugMsg( QString( "theBandNo = %1 min = %2 max = %3" ).arg( bandNo ).arg( min ).arg( max ) ); mDisableMinMaxWidgetRefresh = true; - if ( qIsNaN( min ) ) + if ( std::isnan( min ) ) { mMinLineEdit->clear(); } @@ -754,7 +754,7 @@ void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int bandNo, double min, mMinLineEdit->setText( QString::number( min ) ); } - if ( qIsNaN( max ) ) + if ( std::isnan( max ) ) { mMaxLineEdit->clear(); } @@ -769,7 +769,7 @@ void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int bandNo, double min, void QgsSingleBandPseudoColorRendererWidget::setLineEditValue( QLineEdit *lineEdit, double value ) { QString s; - if ( !qIsNaN( value ) ) + if ( !std::isnan( value ) ) { s = QString::number( value ); } @@ -791,7 +791,7 @@ void QgsSingleBandPseudoColorRendererWidget::resetClassifyButton() mClassifyButton->setEnabled( true ); double min = lineEditValue( mMinLineEdit ); double max = lineEditValue( mMaxLineEdit ); - if ( qIsNaN( min ) || qIsNaN( max ) || min >= max ) + if ( std::isnan( min ) || std::isnan( max ) || min >= max ) { mClassifyButton->setEnabled( false ); } diff --git a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp index 5af721ad71a..35a4285bda2 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp @@ -529,7 +529,7 @@ void QgsDelimitedTextProvider::scanFile( bool buildIndexes ) foundFirstGeometry = true; } mNumberFeatures++; - if ( buildSpatialIndex && qIsFinite( pt.x() ) && qIsFinite( pt.y() ) ) + if ( buildSpatialIndex && std::isfinite( pt.x() ) && std::isfinite( pt.y() ) ) { QgsFeature f; f.setId( mFile->recordId() ); diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index b24cff7e2c0..236777ecb84 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -1033,7 +1033,7 @@ QgsRasterIdentifyResult QgsGdalProvider::identify( const QgsPointXY &point, QgsR double value = myBlock->value( r, c ); if ( ( sourceHasNoDataValue( i ) && useSourceNoDataValue( i ) && - ( qIsNaN( value ) || qgsDoubleNear( value, sourceNoDataValue( i ) ) ) ) || + ( std::isnan( value ) || qgsDoubleNear( value, sourceNoDataValue( i ) ) ) ) || ( QgsRasterRange::contains( value, userNoDataValues( i ) ) ) ) { results.insert( i, QVariant() ); // null QVariant represents no data diff --git a/src/providers/grass/qgsgrassrasterprovider.cpp b/src/providers/grass/qgsgrassrasterprovider.cpp index 3e95e76530d..0c42f70b6fe 100644 --- a/src/providers/grass/qgsgrassrasterprovider.cpp +++ b/src/providers/grass/qgsgrassrasterprovider.cpp @@ -445,7 +445,7 @@ QgsRasterIdentifyResult QgsGrassRasterProvider::identify( const QgsPointXY &poin } // no data? - if ( qIsNaN( value ) || qgsDoubleNear( value, mNoDataValue ) ) + if ( std::isnan( value ) || qgsDoubleNear( value, mNoDataValue ) ) { return noDataResult; } diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp index b2e0606fc3c..03f55d75a1f 100644 --- a/src/providers/wcs/qgswcsprovider.cpp +++ b/src/providers/wcs/qgswcsprovider.cpp @@ -1500,7 +1500,7 @@ QgsRasterIdentifyResult QgsWcsProvider::identify( const QgsPointXY &point, QgsRa // Apply no data and user no data if ( ( sourceHasNoDataValue( i ) && useSourceNoDataValue( i ) && - ( qIsNaN( value ) || qgsDoubleNear( value, sourceNoDataValue( i ) ) ) ) || + ( std::isnan( value ) || qgsDoubleNear( value, sourceNoDataValue( i ) ) ) ) || ( QgsRasterRange::contains( value, userNoDataValues( i ) ) ) ) { results.insert( i, QVariant() ); diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 928fe026444..c4a738d31a1 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -814,12 +814,12 @@ void TestQgsGeometry::point() p29.setM( 9.0 ); QVERIFY( p29.convertTo( QgsWkbTypes::PointM ) ); QCOMPARE( p29.wkbType(), QgsWkbTypes::PointM ); - QVERIFY( qIsNaN( p29.z() ) ); + QVERIFY( std::isnan( p29.z() ) ); QCOMPARE( p29.m(), 9.0 ); QVERIFY( p29.convertTo( QgsWkbTypes::Point ) ); QCOMPARE( p29.wkbType(), QgsWkbTypes::Point ); - QVERIFY( qIsNaN( p29.z() ) ); - QVERIFY( qIsNaN( p29.m() ) ); + QVERIFY( std::isnan( p29.z() ) ); + QVERIFY( std::isnan( p29.m() ) ); QVERIFY( !p29.convertTo( QgsWkbTypes::Polygon ) ); //boundary @@ -851,12 +851,12 @@ void TestQgsGeometry::point() // distance 3D QCOMPARE( QgsPoint( 0, 0 ).distanceSquared3D( QgsPoint( 1, 1 ) ), 2.0 ); - QVERIFY( qIsNaN( QgsPoint( 0, 0 ).distanceSquared3D( 1, 1, 0 ) ) ); - QVERIFY( qIsNaN( QgsPoint( 0, 0 ).distanceSquared3D( QgsPoint( QgsWkbTypes::PointZ, 2, 2, 2, 0 ) ) ) ); - QVERIFY( qIsNaN( QgsPoint( 0, 0 ).distanceSquared3D( 2, 2, 2 ) ) ); - QVERIFY( qIsNaN( QgsPoint( QgsWkbTypes::PointZ, 2, 2, 2, 0 ).distanceSquared3D( QgsPoint( 1, 1 ) ) ) ); + QVERIFY( std::isnan( QgsPoint( 0, 0 ).distanceSquared3D( 1, 1, 0 ) ) ); + QVERIFY( std::isnan( QgsPoint( 0, 0 ).distanceSquared3D( QgsPoint( QgsWkbTypes::PointZ, 2, 2, 2, 0 ) ) ) ); + QVERIFY( std::isnan( QgsPoint( 0, 0 ).distanceSquared3D( 2, 2, 2 ) ) ); + QVERIFY( std::isnan( QgsPoint( QgsWkbTypes::PointZ, 2, 2, 2, 0 ).distanceSquared3D( QgsPoint( 1, 1 ) ) ) ); QCOMPARE( QgsPoint( QgsWkbTypes::PointZ, 2, 2, 2, 0 ).distanceSquared3D( 1, 1, 0 ), 6.0 ); - QVERIFY( qIsNaN( QgsPoint( QgsWkbTypes::PointZ, -2, -2, -2, 0 ).distanceSquared3D( QgsPoint( 0, 0 ) ) ) ); + QVERIFY( std::isnan( QgsPoint( QgsWkbTypes::PointZ, -2, -2, -2, 0 ).distanceSquared3D( QgsPoint( 0, 0 ) ) ) ); QCOMPARE( QgsPoint( QgsWkbTypes::PointZ, -2, -2, -2, 0 ).distanceSquared3D( 0, 0, 0 ), 12.0 ); QCOMPARE( QgsPoint( QgsWkbTypes::PointZ, -2, -2, -2, 0 ).distanceSquared3D( QgsPoint( QgsWkbTypes::PointZ, 2, 2, 2, 0 ) ), 48.0 ); QCOMPARE( QgsPoint( QgsWkbTypes::PointZ, -2, -2, -2, 0 ).distanceSquared3D( 2, 2, 2 ), 48.0 ); @@ -1317,8 +1317,8 @@ void TestQgsGeometry::lineString() QCOMPARE( l9.zAt( 0 ), 3.0 ); QCOMPARE( l9.zAt( 1 ), 13.0 ); QCOMPARE( l9.zAt( 2 ), 23.0 ); - QVERIFY( qIsNaN( l9.zAt( -1 ) ) ); //out of range - QVERIFY( qIsNaN( l9.zAt( 11 ) ) ); //out of range + QVERIFY( std::isnan( l9.zAt( -1 ) ) ); //out of range + QVERIFY( std::isnan( l9.zAt( 11 ) ) ); //out of range l9.setZAt( 0, 53.0 ); QCOMPARE( l9.zAt( 0 ), 53.0 ); @@ -1330,8 +1330,8 @@ void TestQgsGeometry::lineString() QCOMPARE( l9.mAt( 0 ), 4.0 ); QCOMPARE( l9.mAt( 1 ), 14.0 ); QCOMPARE( l9.mAt( 2 ), 24.0 ); - QVERIFY( qIsNaN( l9.mAt( -1 ) ) ); //out of range - QVERIFY( qIsNaN( l9.mAt( 11 ) ) ); //out of range + QVERIFY( std::isnan( l9.mAt( -1 ) ) ); //out of range + QVERIFY( std::isnan( l9.mAt( 11 ) ) ); //out of range l9.setMAt( 0, 54.0 ); QCOMPARE( l9.mAt( 0 ), 54.0 ); @@ -1346,8 +1346,8 @@ void TestQgsGeometry::lineString() << QgsPoint( QgsWkbTypes::PointM, 21, 22, 0, 24 ) ); //basically we just don't want these to crash - QVERIFY( qIsNaN( l9.zAt( 0 ) ) ); - QVERIFY( qIsNaN( l9.zAt( 1 ) ) ); + QVERIFY( std::isnan( l9.zAt( 0 ) ) ); + QVERIFY( std::isnan( l9.zAt( 1 ) ) ); l9.setZAt( 0, 53.0 ); l9.setZAt( 1, 63.0 ); @@ -1357,8 +1357,8 @@ void TestQgsGeometry::lineString() << QgsPoint( 21, 22 ) ); //basically we just don't want these to crash - QVERIFY( qIsNaN( l9.mAt( 0 ) ) ); - QVERIFY( qIsNaN( l9.mAt( 1 ) ) ); + QVERIFY( std::isnan( l9.mAt( 0 ) ) ); + QVERIFY( std::isnan( l9.mAt( 1 ) ) ); l9.setMAt( 0, 53.0 ); l9.setMAt( 1, 63.0 ); diff --git a/tests/src/core/testqgsgeometryutils.cpp b/tests/src/core/testqgsgeometryutils.cpp index 98bc1688a30..e7c14e46dff 100644 --- a/tests/src/core/testqgsgeometryutils.cpp +++ b/tests/src/core/testqgsgeometryutils.cpp @@ -642,14 +642,14 @@ void TestQgsGeometryUtils::testClosestPoint() QgsPoint pt1 = QgsGeometryUtils::closestPoint( linestringZ, QgsPoint( 1, 0 ) ); QGSCOMPARENEAR( pt1.z(), 1, 0.0001 ); - QVERIFY( qIsNaN( pt1.m() ) ); + QVERIFY( std::isnan( pt1.m() ) ); QgsLineString linestringM( QVector() << QgsPoint( 1, 1, std::numeric_limits::quiet_NaN(), 1 ) << QgsPoint( 1, 3, std::numeric_limits::quiet_NaN(), 2 ) ); QgsPoint pt2 = QgsGeometryUtils::closestPoint( linestringM, QgsPoint( 1, 4 ) ); - QVERIFY( qIsNaN( pt2.z() ) ); + QVERIFY( std::isnan( pt2.z() ) ); QGSCOMPARENEAR( pt2.m(), 2, 0.0001 ); QgsLineString linestringZM( QVector() diff --git a/tests/src/core/testqgsstatisticalsummary.cpp b/tests/src/core/testqgsstatisticalsummary.cpp index 49343ff4dbc..b7db0080ebe 100644 --- a/tests/src/core/testqgsstatisticalsummary.cpp +++ b/tests/src/core/testqgsstatisticalsummary.cpp @@ -311,32 +311,32 @@ void TestQgsStatisticSummary::noValues() QCOMPARE( s.statistic( QgsStatisticalSummary::CountMissing ), 0.0 ); QCOMPARE( s.sum(), 0.0 ); QCOMPARE( s.statistic( QgsStatisticalSummary::Sum ), 0.0 ); - QVERIFY( qIsNaN( s.mean() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::Mean ) ) ); - QVERIFY( qIsNaN( s.median() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::Median ) ) ); - QVERIFY( qIsNaN( s.stDev() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::StDev ) ) ); - QVERIFY( qIsNaN( s.sampleStDev() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::StDevSample ) ) ); - QVERIFY( qIsNaN( s.min() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::Min ) ) ); - QVERIFY( qIsNaN( s.max() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::Max ) ) ); - QVERIFY( qIsNaN( s.range() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::Range ) ) ); - QVERIFY( qIsNaN( s.minority() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::Minority ) ) ); - QVERIFY( qIsNaN( s.majority() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::Majority ) ) ); + QVERIFY( std::isnan( s.mean() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Mean ) ) ); + QVERIFY( std::isnan( s.median() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Median ) ) ); + QVERIFY( std::isnan( s.stDev() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::StDev ) ) ); + QVERIFY( std::isnan( s.sampleStDev() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::StDevSample ) ) ); + QVERIFY( std::isnan( s.min() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Min ) ) ); + QVERIFY( std::isnan( s.max() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Max ) ) ); + QVERIFY( std::isnan( s.range() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Range ) ) ); + QVERIFY( std::isnan( s.minority() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Minority ) ) ); + QVERIFY( std::isnan( s.majority() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Majority ) ) ); QCOMPARE( s.variety(), 0 ); QCOMPARE( s.statistic( QgsStatisticalSummary::Variety ), 0.0 ); - QVERIFY( qIsNaN( s.firstQuartile() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::FirstQuartile ) ) ); - QVERIFY( qIsNaN( s.thirdQuartile() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::ThirdQuartile ) ) ); - QVERIFY( qIsNaN( s.interQuartileRange() ) ); - QVERIFY( qIsNaN( s.statistic( QgsStatisticalSummary::InterQuartileRange ) ) ); + QVERIFY( std::isnan( s.firstQuartile() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::FirstQuartile ) ) ); + QVERIFY( std::isnan( s.thirdQuartile() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::ThirdQuartile ) ) ); + QVERIFY( std::isnan( s.interQuartileRange() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::InterQuartileRange ) ) ); } QGSTEST_MAIN( TestQgsStatisticSummary ) From 891d612e95a4d4288cd2d34452590212ce9b695a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 03:42:03 +1000 Subject: [PATCH 126/364] Remove unused qmath.h includes --- src/analysis/interpolation/CloughTocherInterpolator.cc | 1 - src/analysis/interpolation/MathUtils.cc | 2 -- src/analysis/raster/qgsalignraster.cpp | 1 - src/analysis/raster/qgsrastermatrix.cpp | 2 +- src/analysis/vector/qgsgeometrysnapper.cpp | 2 -- src/analysis/vector/qgszonalstatistics.cpp | 2 -- src/core/effects/qgsimageoperation.cpp | 1 - src/core/effects/qgsimageoperation.h | 1 - src/core/geometry/qgsgeos.cpp | 1 - src/core/geometry/qgslinestring.cpp | 1 - src/core/qgsfield.cpp | 1 - src/core/qgshistogram.cpp | 1 - src/core/qgsproperty.cpp | 1 - src/core/qgspropertytransformer.cpp | 1 - src/core/qgsstatisticalsummary.cpp | 1 - src/core/raster/qgsbrightnesscontrastfilter.cpp | 1 - src/core/raster/qgscubicrasterresampler.cpp | 1 - src/core/raster/qgsrasterchecker.cpp | 1 - src/core/raster/qgsrasterdataprovider.cpp | 2 -- src/core/raster/qgsrasterinterface.cpp | 2 -- src/gui/qgscurveeditorwidget.cpp | 1 - src/gui/qgsdial.cpp | 1 - src/gui/qgsgradientcolorrampdialog.cpp | 1 - src/gui/qgsslider.cpp | 1 - .../geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp | 2 -- .../geometry_checker/checks/qgsgeometrysliverpolygoncheck.h | 1 - src/plugins/geometry_checker/utils/qgsfeaturepool.cpp | 1 - src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp | 1 - src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h | 1 - src/providers/arcgisrest/qgsamsprovider.cpp | 1 - 30 files changed, 1 insertion(+), 36 deletions(-) diff --git a/src/analysis/interpolation/CloughTocherInterpolator.cc b/src/analysis/interpolation/CloughTocherInterpolator.cc index d66c9421f95..45286acdfb0 100644 --- a/src/analysis/interpolation/CloughTocherInterpolator.cc +++ b/src/analysis/interpolation/CloughTocherInterpolator.cc @@ -18,7 +18,6 @@ #include "qgslogger.h" #include "MathUtils.h" #include "NormVecDecorator.h" -#include CloughTocherInterpolator::CloughTocherInterpolator() : mTIN( nullptr ) diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index 0cecf8f068e..a83fe7e2e0e 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -19,8 +19,6 @@ #include "qgspoint.h" #include "Vector3D.h" -#include - bool MathUtils::calcBarycentricCoordinates( double x, double y, QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *result ) { if ( p1 && p2 && p3 && result ) diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 45b890582ab..6ff588939c4 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index c2550c66b84..1bb15ca95d7 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -17,7 +17,7 @@ #include "qgsrastermatrix.h" #include -#include +#include QgsRasterMatrix::QgsRasterMatrix() : mColumns( 0 ) diff --git a/src/analysis/vector/qgsgeometrysnapper.cpp b/src/analysis/vector/qgsgeometrysnapper.cpp index d0fe08f3df0..9e8e3d478f9 100644 --- a/src/analysis/vector/qgsgeometrysnapper.cpp +++ b/src/analysis/vector/qgsgeometrysnapper.cpp @@ -15,8 +15,6 @@ ***************************************************************************/ #include -#include - #include "qgsfeatureiterator.h" #include "qgsgeometry.h" #include "qgsvectorlayer.h" diff --git a/src/analysis/vector/qgszonalstatistics.cpp b/src/analysis/vector/qgszonalstatistics.cpp index 4676f89daf4..bf60724c684 100644 --- a/src/analysis/vector/qgszonalstatistics.cpp +++ b/src/analysis/vector/qgszonalstatistics.cpp @@ -26,8 +26,6 @@ #include "qgsrasterblock.h" #include "qgslogger.h" -#include "qmath.h" - #include QgsZonalStatistics::QgsZonalStatistics( QgsVectorLayer *polygonLayer, QgsRasterLayer *rasterLayer, const QString &attributePrefix, int rasterBand, QgsZonalStatistics::Statistics stats ) diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index feba343168e..e3bcafe9798 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -22,7 +22,6 @@ #include #include #include -#include //determined via trial-and-error. Could possibly be optimised, or varied //depending on the image size. diff --git a/src/core/effects/qgsimageoperation.h b/src/core/effects/qgsimageoperation.h index 6272b2467e1..0ebc40da0b4 100644 --- a/src/core/effects/qgsimageoperation.h +++ b/src/core/effects/qgsimageoperation.h @@ -21,7 +21,6 @@ #include #include "qgis.h" #include -#include #include "qgis_core.h" diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index cb3858eb0a5..769e2d50787 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -27,7 +27,6 @@ email : marco.hugentobler at sourcepole dot com #include "qgspolygon.h" #include #include -#include #define DEFAULT_QUADRANT_SEGMENTS 8 diff --git a/src/core/geometry/qgslinestring.cpp b/src/core/geometry/qgslinestring.cpp index 233fbb190b6..7e8f6cb7226 100644 --- a/src/core/geometry/qgslinestring.cpp +++ b/src/core/geometry/qgslinestring.cpp @@ -26,7 +26,6 @@ #include #include #include -#include /*************************************************************************** diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 432c0c9c314..e088c0bd1ce 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -21,7 +21,6 @@ #include "qgssettings.h" #include -#include #include /*************************************************************************** diff --git a/src/core/qgshistogram.cpp b/src/core/qgshistogram.cpp index 42d5589a0b4..1500594230d 100644 --- a/src/core/qgshistogram.cpp +++ b/src/core/qgshistogram.cpp @@ -19,7 +19,6 @@ #include "qgsstatisticalsummary.h" #include "qgsvectorlayer.h" -#include QgsHistogram::QgsHistogram() : mMax( 0 ) diff --git a/src/core/qgsproperty.cpp b/src/core/qgsproperty.cpp index f058bbe89ec..92d5d2528e8 100644 --- a/src/core/qgsproperty.cpp +++ b/src/core/qgsproperty.cpp @@ -20,7 +20,6 @@ #include "qgsfeature.h" #include "qgssymbollayerutils.h" #include "qgscolorramp.h" -#include QgsPropertyDefinition::QgsPropertyDefinition() diff --git a/src/core/qgspropertytransformer.cpp b/src/core/qgspropertytransformer.cpp index 2aee158e353..0f62f7bc289 100644 --- a/src/core/qgspropertytransformer.cpp +++ b/src/core/qgspropertytransformer.cpp @@ -21,7 +21,6 @@ #include "qgsfeature.h" #include "qgssymbollayerutils.h" #include "qgscolorramp.h" -#include // diff --git a/src/core/qgsstatisticalsummary.cpp b/src/core/qgsstatisticalsummary.cpp index ce19cdf324a..c0b19ddcaa4 100644 --- a/src/core/qgsstatisticalsummary.cpp +++ b/src/core/qgsstatisticalsummary.cpp @@ -15,7 +15,6 @@ #include "qgsstatisticalsummary.h" #include -#include #include #include diff --git a/src/core/raster/qgsbrightnesscontrastfilter.cpp b/src/core/raster/qgsbrightnesscontrastfilter.cpp index 173aafdd0b5..2d7a6107830 100644 --- a/src/core/raster/qgsbrightnesscontrastfilter.cpp +++ b/src/core/raster/qgsbrightnesscontrastfilter.cpp @@ -18,7 +18,6 @@ #include "qgsrasterdataprovider.h" #include "qgsbrightnesscontrastfilter.h" -#include #include #include diff --git a/src/core/raster/qgscubicrasterresampler.cpp b/src/core/raster/qgscubicrasterresampler.cpp index a1dda4546c0..7718fd6cb09 100644 --- a/src/core/raster/qgscubicrasterresampler.cpp +++ b/src/core/raster/qgscubicrasterresampler.cpp @@ -17,7 +17,6 @@ #include "qgscubicrasterresampler.h" #include -#include QgsCubicRasterResampler::QgsCubicRasterResampler() // red diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index d60968d6809..42cf37806ee 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -18,7 +18,6 @@ #include "qgsrasterdataprovider.h" #include "qgsrasterlayer.h" -#include #include #include #include diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index 0e1ecf23627..a402b8781ec 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -27,8 +27,6 @@ #include #include -#include - #define ERR(message) QgsError(message, "Raster provider") void QgsRasterDataProvider::setUseSourceNoDataValue( int bandNo, bool use ) diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index 985996931e0..15f9b3abcb1 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -22,8 +22,6 @@ #include #include -#include - #include "qgslogger.h" #include "qgsrasterbandstats.h" #include "qgsrasterhistogram.h" diff --git a/src/gui/qgscurveeditorwidget.cpp b/src/gui/qgscurveeditorwidget.cpp index 2bf0ac2420d..136feeb6870 100644 --- a/src/gui/qgscurveeditorwidget.cpp +++ b/src/gui/qgscurveeditorwidget.cpp @@ -16,7 +16,6 @@ #include "qgscurveeditorwidget.h" -#include #include #include #include diff --git a/src/gui/qgsdial.cpp b/src/gui/qgsdial.cpp index f4c62dce346..39da2e89a73 100644 --- a/src/gui/qgsdial.cpp +++ b/src/gui/qgsdial.cpp @@ -21,7 +21,6 @@ #include #include #include -#include QgsDial::QgsDial( QWidget *parent ) : QDial( parent ) { diff --git a/src/gui/qgsgradientcolorrampdialog.cpp b/src/gui/qgsgradientcolorrampdialog.cpp index 01cca0d5ed5..3e892fe37f8 100644 --- a/src/gui/qgsgradientcolorrampdialog.cpp +++ b/src/gui/qgsgradientcolorrampdialog.cpp @@ -21,7 +21,6 @@ #include "qgscptcityarchive.h" #include "qgssettings.h" -#include #include #include #include diff --git a/src/gui/qgsslider.cpp b/src/gui/qgsslider.cpp index 770bbbaaf11..83e92b278f0 100644 --- a/src/gui/qgsslider.cpp +++ b/src/gui/qgsslider.cpp @@ -21,7 +21,6 @@ #include #include #include -#include QgsSlider::QgsSlider( QWidget *parent ) : QSlider( parent ) { diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp index 8b96bb9f527..1f86f0e7199 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp @@ -13,8 +13,6 @@ * * ***************************************************************************/ -#include - #include "qgsgeometrysegmentlengthcheck.h" #include "qgsgeometryutils.h" #include "../utils/qgsfeaturepool.h" diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h index 703ba831f6b..bdad2a27971 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h @@ -17,7 +17,6 @@ #define QGS_GEOMETRY_SLIVERPOLYGON_CHECK_H #include "qgsgeometryareacheck.h" -#include class QgsGeometrySliverPolygonCheck : public QgsGeometryAreaCheck { diff --git a/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp b/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp index 999d170b62d..a37ef0d956b 100644 --- a/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp +++ b/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp @@ -23,7 +23,6 @@ #include "qgsgeometryutils.h" #include -#include #include QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, bool selectedOnly ) diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp index fd548f19a5b..481697e4a69 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp @@ -15,7 +15,6 @@ ***************************************************************************/ #include "qgsgeometryutils.h" -#include #include "qgsgeos.h" #include "qgsgeometrycollection.h" #include "qgssurface.h" diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h index 80249450ff0..c703765cdd5 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.h @@ -19,7 +19,6 @@ #include "geometry/qgsabstractgeometry.h" #include "geometry/qgspoint.h" -#include class QgsGeometryEngine; diff --git a/src/providers/arcgisrest/qgsamsprovider.cpp b/src/providers/arcgisrest/qgsamsprovider.cpp index f8e5a5711dc..ecae64daab4 100644 --- a/src/providers/arcgisrest/qgsamsprovider.cpp +++ b/src/providers/arcgisrest/qgsamsprovider.cpp @@ -30,7 +30,6 @@ #include #include #include -#include QgsAmsLegendFetcher::QgsAmsLegendFetcher( QgsAmsProvider *provider ) : QgsImageFetcher( provider ), mProvider( provider ) From c19418c4479b3eb0a637035f6fd79644f7c1fb34 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 04:13:38 +1000 Subject: [PATCH 127/364] Add more missing std:: prefixes --- .../network/qgsvectorlayerdirector.cpp | 2 +- src/analysis/raster/qgsalignraster.cpp | 2 +- src/analysis/raster/qgshillshadefilter.cpp | 2 +- src/analysis/raster/qgskde.cpp | 4 +-- src/analysis/raster/qgsrastermatrix.cpp | 10 +++---- src/analysis/raster/qgsrelief.cpp | 4 +-- src/analysis/raster/qgsruggednessfilter.cpp | 2 +- src/analysis/raster/qgsslopefilter.cpp | 2 +- src/app/dwg/qgsdwgimporter.cpp | 6 ++--- src/app/qgsdecorationgrid.cpp | 2 +- src/app/qgsdecorationscalebar.cpp | 2 +- src/app/qgsmeasuredialog.cpp | 2 +- src/app/qgspointrotationitem.cpp | 2 +- src/core/composer/qgscomposeritem.cpp | 2 +- src/core/composer/qgscomposermapgrid.cpp | 2 +- src/core/composer/qgscomposernodesitem.cpp | 2 +- src/core/composer/qgscomposerscalebar.cpp | 10 +++---- src/core/composer/qgscomposerutils.cpp | 8 +++--- src/core/composer/qgscomposition.cpp | 22 ++++++++-------- src/core/effects/qgsimageoperation.cpp | 2 +- src/core/expression/qgsexpressionfunction.cpp | 22 ++++++++-------- src/core/expression/qgsexpressionnodeimpl.cpp | 2 +- src/core/geometry/qgscircularstring.cpp | 2 +- src/core/geometry/qgsellipse.cpp | 2 +- src/core/geometry/qgsgeometry.cpp | 2 +- src/core/geometry/qgsgeometryutils.cpp | 4 +-- src/core/geometry/qgsgeos.cpp | 4 +-- src/core/geometry/qgspoint.cpp | 12 ++++----- src/core/geometry/qgsrectangle.cpp | 2 +- src/core/geometry/qgstriangle.cpp | 6 ++--- src/core/layout/qgslayoututils.cpp | 2 +- src/core/pal/feature.cpp | 2 +- src/core/pal/geomfunction.h | 2 +- src/core/pal/labelposition.cpp | 2 +- src/core/qgis.h | 4 +-- src/core/qgscolorramp.cpp | 2 +- src/core/qgscoordinateutils.cpp | 2 +- src/core/qgsdiagramrenderer.cpp | 2 +- src/core/qgsdistancearea.cpp | 14 +++++----- src/core/qgshistogram.cpp | 2 +- src/core/qgsmapsettingsutils.cpp | 2 +- src/core/qgspallabeling.cpp | 4 +-- src/core/qgspointxy.cpp | 10 +++---- src/core/qgsscalecalculator.cpp | 2 +- src/core/qgstolerance.cpp | 4 +-- src/core/qgsvector.cpp | 2 +- src/core/raster/qgscolorrampshader.cpp | 4 +-- src/core/raster/qgshillshaderenderer.cpp | 2 +- src/core/raster/qgsrasterchecker.cpp | 2 +- src/core/raster/qgsrasterinterface.h | 2 +- src/core/simplify/effectivearea.cpp | 2 +- src/core/symbology/qgsarrowsymbollayer.cpp | 4 +-- src/core/symbology/qgsheatmaprenderer.cpp | 4 +-- src/core/symbology/qgslinesymbollayer.cpp | 4 +-- src/core/symbology/qgssymbollayerutils.cpp | 2 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 2 +- src/gui/qgscolorwidgets.cpp | 26 +++++++++---------- .../checks/qgsgeometrycheck.cpp | 4 +-- .../georeferencer/qgsgeorefplugingui.cpp | 2 +- .../georeferencer/qgsresidualplotitem.cpp | 6 ++--- src/providers/gdal/qgsgdalprovider.cpp | 2 +- src/providers/wms/qgswmsprovider.cpp | 8 +++--- 62 files changed, 141 insertions(+), 141 deletions(-) diff --git a/src/analysis/network/qgsvectorlayerdirector.cpp b/src/analysis/network/qgsvectorlayerdirector.cpp index a990a27c84d..3a5d57c1813 100644 --- a/src/analysis/network/qgsvectorlayerdirector.cpp +++ b/src/analysis/network/qgsvectorlayerdirector.cpp @@ -50,7 +50,7 @@ class QgsPointCompare double tx1 = std::ceil( p1.x() / mTolerance ); double tx2 = std::ceil( p2.x() / mTolerance ); if ( tx1 == tx2 ) - return ceil( p1.y() / mTolerance ) < std::ceil( p2.y() / mTolerance ); + return std::ceil( p1.y() / mTolerance ) < std::ceil( p2.y() / mTolerance ); return tx1 < tx2; } diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 6ff588939c4..3b96d8e375e 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -40,7 +40,7 @@ static double floor_with_tolerance( double value ) if ( std::fabs( value - std::round( value ) ) < 1e-6 ) return std::round( value ); else - return floor( value ); + return std::floor( value ); } static double fmod_with_tolerance( double num, double denom ) diff --git a/src/analysis/raster/qgshillshadefilter.cpp b/src/analysis/raster/qgshillshadefilter.cpp index 91a63012c35..3da206fbc24 100644 --- a/src/analysis/raster/qgshillshadefilter.cpp +++ b/src/analysis/raster/qgshillshadefilter.cpp @@ -38,7 +38,7 @@ float QgsHillshadeFilter::processNineCellWindow( float *x11, float *x21, float * } float zenith_rad = mLightAngle * M_PI / 180.0; - float slope_rad = atan( std::sqrt( derX * derX + derY * derY ) ); + float slope_rad = std::atan( std::sqrt( derX * derX + derY * derY ) ); float azimuth_rad = mLightAzimuth * M_PI / 180.0; float aspect_rad = 0; if ( derX == 0 && derY == 0 ) //aspect undefined, take a neutral value. Better solutions? diff --git a/src/analysis/raster/qgskde.cpp b/src/analysis/raster/qgskde.cpp index eb06adf965c..69df88cc001 100644 --- a/src/analysis/raster/qgskde.cpp +++ b/src/analysis/raster/qgskde.cpp @@ -330,7 +330,7 @@ double QgsKernelDensityEstimation::quarticKernel( const double distance, const d return k * ( 15. / 16. ) * std::pow( 1. - std::pow( distance / bandwidth, 2 ), 2 ); } case OutputRaw: - return pow( 1. - std::pow( distance / bandwidth, 2 ), 2 ); + return std::pow( 1. - std::pow( distance / bandwidth, 2 ), 2 ); } return 0.0; //no, seriously, I told you NO WARNINGS! } @@ -348,7 +348,7 @@ double QgsKernelDensityEstimation::triweightKernel( const double distance, const return k * ( 35. / 32. ) * std::pow( 1. - std::pow( distance / bandwidth, 2 ), 3 ); } case OutputRaw: - return pow( 1. - std::pow( distance / bandwidth, 2 ), 3 ); + return std::pow( 1. - std::pow( distance / bandwidth, 2 ), 3 ); } return 0.0; // this is getting ridiculous... don't you ever listen to a word I say? } diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index 1bb15ca95d7..fa5e4a6ca3d 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -226,16 +226,16 @@ bool QgsRasterMatrix::oneArgumentOperation( OneArgOperator op ) mData[i] = std::cos( value ); break; case opTAN: - mData[i] = tan( value ); + mData[i] = std::tan( value ); break; case opASIN: - mData[i] = asin( value ); + mData[i] = std::asin( value ); break; case opACOS: - mData[i] = acos( value ); + mData[i] = std::acos( value ); break; case opATAN: - mData[i] = atan( value ); + mData[i] = std::atan( value ); break; case opSIGN: mData[i] = -value; @@ -292,7 +292,7 @@ double QgsRasterMatrix::calculateTwoArgumentOp( TwoArgOperator op, double arg1, } else { - return pow( arg1, arg2 ); + return std::pow( arg1, arg2 ); } case opEQ: return ( arg1 == arg2 ? 1.0 : 0.0 ); diff --git a/src/analysis/raster/qgsrelief.cpp b/src/analysis/raster/qgsrelief.cpp index 29a5ff89343..e1fee183771 100644 --- a/src/analysis/raster/qgsrelief.cpp +++ b/src/analysis/raster/qgsrelief.cpp @@ -560,7 +560,7 @@ bool QgsRelief::exportFrequencyDistributionToCsv( const QString &file ) //log10 transformation for all frequency values for ( int i = 0; i < 252; ++i ) { - frequency[i] = log10( frequency[i] ); + frequency[i] = std::log10( frequency[i] ); } //write out frequency values to csv file for debugging @@ -651,7 +651,7 @@ QList< QgsRelief::ReliefColor > QgsRelief::calculateOptimizedReliefClasses() //log10 transformation for all frequency values for ( int i = 0; i < 252; ++i ) { - frequency[i] = log10( frequency[i] ); + frequency[i] = std::log10( frequency[i] ); } //start with 9 uniformly distributed classes diff --git a/src/analysis/raster/qgsruggednessfilter.cpp b/src/analysis/raster/qgsruggednessfilter.cpp index c1ff676def2..dfc4808e178 100644 --- a/src/analysis/raster/qgsruggednessfilter.cpp +++ b/src/analysis/raster/qgsruggednessfilter.cpp @@ -83,6 +83,6 @@ float QgsRuggednessFilter::processNineCellWindow( float *x11, float *x21, float sum += ( *x33 - *x22 ) * ( *x33 - *x22 ); } - return sqrt( sum ); + return std::sqrt( sum ); } diff --git a/src/analysis/raster/qgsslopefilter.cpp b/src/analysis/raster/qgsslopefilter.cpp index 17789087880..db263daddf0 100644 --- a/src/analysis/raster/qgsslopefilter.cpp +++ b/src/analysis/raster/qgsslopefilter.cpp @@ -34,6 +34,6 @@ float QgsSlopeFilter::processNineCellWindow( float *x11, float *x21, float *x31, return mOutputNodataValue; } - return atan( std::sqrt( derX * derX + derY * derY ) ) * 180.0 / M_PI; + return std::atan( std::sqrt( derX * derX + derY * derY ) ) * 180.0 / M_PI; } diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index 6fbf2de2035..0c0fcc59fc3 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -1441,7 +1441,7 @@ bool QgsDwgImporter::curveFromLWPolyline( const DRW_LWPolyline &data, QgsCompoun { size_t i1 = ( i + 1 ) % vertexnum; - double a = 2.0 * atan( data.vertlist[i]->bulge ); + double a = 2.0 * std::atan( data.vertlist[i]->bulge ); double dx = data.vertlist[i1]->x - data.vertlist[i0]->x; double dy = data.vertlist[i1]->y - data.vertlist[i0]->y; double c = std::sqrt( dx * dx + dy * dy ); @@ -1552,7 +1552,7 @@ void QgsDwgImporter::addLWPolyline( const DRW_LWPolyline &data ) if ( hasBulge ) { - double a = 2.0 * atan( data.vertlist[i]->bulge ); + double a = 2.0 * std::atan( data.vertlist[i]->bulge ); double dx = p1.x() - p0.x(); double dy = p1.y() - p0.y(); double c = std::sqrt( dx * dx + dy * dy ); @@ -1753,7 +1753,7 @@ void QgsDwgImporter::addPolyline( const DRW_Polyline &data ) if ( hasBulge ) { - double a = 2.0 * atan( data.vertlist[i]->bulge ); + double a = 2.0 * std::atan( data.vertlist[i]->bulge ); double dx = p1.x() - p0.x(); double dy = p1.y() - p0.y(); double dz = p1.z() - p0.z(); diff --git a/src/app/qgsdecorationgrid.cpp b/src/app/qgsdecorationgrid.cpp index 868cbbd5164..e2c393f1e39 100644 --- a/src/app/qgsdecorationgrid.cpp +++ b/src/app/qgsdecorationgrid.cpp @@ -787,7 +787,7 @@ bool QgsDecorationGrid::getIntervalFromExtent( double *values, bool useXAxis ) if ( !qgsDoubleNear( interval, 0.0 ) ) { double interval2 = 0; - int factor = std::pow( 10, std::floor( log10( interval ) ) ); + int factor = std::pow( 10, std::floor( std::log10( interval ) ) ); if ( factor != 0 ) { interval2 = std::round( interval / factor ) * factor; diff --git a/src/app/qgsdecorationscalebar.cpp b/src/app/qgsdecorationscalebar.cpp index a98773265b9..a0b65f5d546 100644 --- a/src/app/qgsdecorationscalebar.cpp +++ b/src/app/qgsdecorationscalebar.cpp @@ -158,7 +158,7 @@ void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRender // Work out the exponent for the number - e.g, 1234 will give 3, // and .001234 will give -3 - double myPowerOf10 = std::floor( log10( myActualSize ) ); + double myPowerOf10 = std::floor( std::log10( myActualSize ) ); // snap to integer < 10 times power of 10 if ( mSnapping ) diff --git a/src/app/qgsmeasuredialog.cpp b/src/app/qgsmeasuredialog.cpp index d096dad8abf..fba4334d918 100644 --- a/src/app/qgsmeasuredialog.cpp +++ b/src/app/qgsmeasuredialog.cpp @@ -280,7 +280,7 @@ QString QgsMeasureDialog::formatDistance( double distance, bool convertUnits ) c { // special handling for degrees - because we can't use smaller units (eg m->mm), we need to make sure there's // enough decimal places to show a usable measurement value - int minPlaces = std::round( log10( 1.0 / distance ) ) + 1; + int minPlaces = std::round( std::log10( 1.0 / distance ) ) + 1; decimals = qMax( decimals, minPlaces ); } return QgsDistanceArea::formatDistance( distance, decimals, mDistanceUnits, baseUnit ); diff --git a/src/app/qgspointrotationitem.cpp b/src/app/qgspointrotationitem.cpp index 07fbbd45663..cb7405d1110 100644 --- a/src/app/qgspointrotationitem.cpp +++ b/src/app/qgspointrotationitem.cpp @@ -55,7 +55,7 @@ void QgsPointRotationItem::paint( QPainter *painter ) if ( mPixmap.width() > 0 && mPixmap.height() > 0 ) { h = std::sqrt( ( double ) mPixmap.width() * mPixmap.width() + mPixmap.height() * mPixmap.height() ) / 2; //the half of the item diagonal - dAngel = acos( mPixmap.width() / ( h * 2 ) ) * 180 / M_PI; //the diagonal angel of the original rect + dAngel = std::acos( mPixmap.width() / ( h * 2 ) ) * 180 / M_PI; //the diagonal angel of the original rect x = h * std::cos( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); y = h * std::sin( ( painterRotation( mRotation ) - dAngel ) * M_PI / 180 ); } diff --git a/src/core/composer/qgscomposeritem.cpp b/src/core/composer/qgscomposeritem.cpp index b8632ad532b..fb7e343ef83 100644 --- a/src/core/composer/qgscomposeritem.cpp +++ b/src/core/composer/qgscomposeritem.cpp @@ -1012,7 +1012,7 @@ void QgsComposerItem::setItemRotation( const double r, const bool adjustPosition mItemRotation = r; if ( mItemRotation >= 360.0 || mItemRotation <= -360.0 ) { - mItemRotation = fmod( mItemRotation, 360.0 ); + mItemRotation = std::fmod( mItemRotation, 360.0 ); } QgsExpressionContext context = createExpressionContext(); diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index a7a39180cde..dad47f9dd1b 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -1426,7 +1426,7 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMapGr ( mGridAnnotationFormat == QgsComposerMapGrid::Decimal || mGridAnnotationFormat == QgsComposerMapGrid::DecimalWithSuffix ) ) { // wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W" - double wrappedX = fmod( value, 360.0 ); + double wrappedX = std::fmod( value, 360.0 ); if ( wrappedX > 180.0 ) { value = wrappedX - 360.0; diff --git a/src/core/composer/qgscomposernodesitem.cpp b/src/core/composer/qgscomposernodesitem.cpp index 1075e2bdc33..722f4b15848 100644 --- a/src/core/composer/qgscomposernodesitem.cpp +++ b/src/core/composer/qgscomposernodesitem.cpp @@ -50,7 +50,7 @@ QgsComposerNodesItem::QgsComposerNodesItem( const QString &tagName, double QgsComposerNodesItem::computeDistance( QPointF pt1, QPointF pt2 ) const { - return sqrt( std::pow( pt1.x() - pt2.x(), 2 ) + std::pow( pt1.y() - pt2.y(), 2 ) ); + return std::sqrt( std::pow( pt1.x() - pt2.x(), 2 ) + std::pow( pt1.y() - pt2.y(), 2 ) ); } bool QgsComposerNodesItem::addNode( QPointF pt, diff --git a/src/core/composer/qgscomposerscalebar.cpp b/src/core/composer/qgscomposerscalebar.cpp index 91c0699cb07..32e4498c810 100644 --- a/src/core/composer/qgscomposerscalebar.cpp +++ b/src/core/composer/qgscomposerscalebar.cpp @@ -262,15 +262,15 @@ void QgsComposerScaleBar::refreshDataDefinedProperty( const QgsComposerObject::D // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc inline double nextNiceNumber( double a, double d = 1 ) { - double s = std::pow( 10.0, std::floor( log10( a ) ) ) / d; - return ceil( a / s ) * s; + double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d; + return std::ceil( a / s ) * s; } // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc inline double prevNiceNumber( double a, double d = 1 ) { - double s = std::pow( 10.0, std::floor( log10( a ) ) ) / d; - return floor( a / s ) * s; + double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d; + return std::floor( a / s ) * s; } void QgsComposerScaleBar::refreshSegmentMillimeters() @@ -473,7 +473,7 @@ void QgsComposerScaleBar::applyDefaultSize( QgsUnitTypes::DistanceUnit u ) } double segmentWidth = initialUnitsPerSegment / upperMagnitudeMultiplier; - int segmentMagnitude = std::floor( log10( segmentWidth ) ); + int segmentMagnitude = std::floor( std::log10( segmentWidth ) ); double unitsPerSegment = upperMagnitudeMultiplier * ( std::pow( 10.0, segmentMagnitude ) ); double multiplier = std::floor( ( widthInSelectedUnits / ( unitsPerSegment * 10.0 ) ) / 2.5 ) * 2.5; diff --git a/src/core/composer/qgscomposerutils.cpp b/src/core/composer/qgscomposerutils.cpp index 0b422f14430..c9fdfab9a40 100644 --- a/src/core/composer/qgscomposerutils.cpp +++ b/src/core/composer/qgscomposerutils.cpp @@ -42,9 +42,9 @@ void QgsComposerUtils::drawArrowHead( QPainter *p, const double x, const double QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth ); QPointF p1Rotated, p2Rotated; - p1Rotated.setX( p1.x() * std::cos( angleRad ) + p1.y() * -sin( angleRad ) ); + p1Rotated.setX( p1.x() * std::cos( angleRad ) + p1.y() * -std::sin( angleRad ) ); p1Rotated.setY( p1.x() * std::sin( angleRad ) + p1.y() * std::cos( angleRad ) ); - p2Rotated.setX( p2.x() * std::cos( angleRad ) + p2.y() * -sin( angleRad ) ); + p2Rotated.setX( p2.x() * std::cos( angleRad ) + p2.y() * -std::sin( angleRad ) ); p2Rotated.setY( p2.x() * std::sin( angleRad ) + p2.y() * std::cos( angleRad ) ); QPolygonF arrowHeadPoly; @@ -76,7 +76,7 @@ double QgsComposerUtils::angle( QPointF p1, QPointF p2 ) return 0; } - double angle = acos( ( -yDiff * length ) / ( length * length ) ) * 180 / M_PI; + double angle = std::acos( ( -yDiff * length ) / ( length * length ) ) * 180 / M_PI; if ( xDiff < 0 ) { return ( 360 - angle ); @@ -99,7 +99,7 @@ double QgsComposerUtils::normalizedAngle( const double angle ) double clippedAngle = angle; if ( clippedAngle >= 360.0 || clippedAngle <= -360.0 ) { - clippedAngle = fmod( clippedAngle, 360.0 ); + clippedAngle = std::fmod( clippedAngle, 360.0 ); } if ( clippedAngle < 0.0 ) { diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index 778d36c03ce..92c0c8c9b8c 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -565,7 +565,7 @@ QPointF QgsComposition::positionOnPage( QPointF position ) const else { //y coordinate is less then the end of the last page - y = fmod( position.y(), ( paperHeight() + spaceBetweenPages() ) ); + y = std::fmod( position.y(), ( paperHeight() + spaceBetweenPages() ) ); } return QPointF( position.x(), y ); } @@ -1170,7 +1170,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlacePt ) { - newLabel->setItemPosition( newLabel->pos().x(), fmod( newLabel->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newLabel->setItemPosition( newLabel->pos().x(), std::fmod( newLabel->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newLabel->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1203,7 +1203,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newMap->setItemPosition( newMap->pos().x(), fmod( newMap->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newMap->setItemPosition( newMap->pos().x(), std::fmod( newMap->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newMap->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1247,7 +1247,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newArrow->setItemPosition( newArrow->pos().x(), fmod( newArrow->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newArrow->setItemPosition( newArrow->pos().x(), std::fmod( newArrow->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newArrow->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1275,7 +1275,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newScaleBar->setItemPosition( newScaleBar->pos().x(), fmod( newScaleBar->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newScaleBar->setItemPosition( newScaleBar->pos().x(), std::fmod( newScaleBar->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newScaleBar->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1305,7 +1305,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newShape->setItemPosition( newShape->pos().x(), fmod( newShape->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newShape->setItemPosition( newShape->pos().x(), std::fmod( newShape->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newShape->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1335,7 +1335,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newPolygon->setItemPosition( newPolygon->pos().x(), fmod( newPolygon->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newPolygon->setItemPosition( newPolygon->pos().x(), std::fmod( newPolygon->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newPolygon->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1366,7 +1366,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newPolyline->setItemPosition( newPolyline->pos().x(), fmod( newPolyline->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newPolyline->setItemPosition( newPolyline->pos().x(), std::fmod( newPolyline->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newPolyline->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1396,7 +1396,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newPicture->setItemPosition( newPicture->pos().x(), fmod( newPicture->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newPicture->setItemPosition( newPicture->pos().x(), std::fmod( newPicture->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newPicture->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -1424,7 +1424,7 @@ void QgsComposition::addItemsFromXml( const QDomElement &elem, const QDomDocumen { if ( pasteInPlace ) { - newLegend->setItemPosition( newLegend->pos().x(), fmod( newLegend->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); + newLegend->setItemPosition( newLegend->pos().x(), std::fmod( newLegend->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) ); newLegend->move( pasteInPlacePt->x(), pasteInPlacePt->y() ); } else @@ -3152,7 +3152,7 @@ void QgsComposition::computeWorldFileParameters( const QRectF &exportRegion, dou // rotation matrix double r[6]; r[0] = std::cos( alpha ); - r[1] = -sin( alpha ); + r[1] = -std::sin( alpha ); r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha ); r[3] = std::sin( alpha ); r[4] = std::cos( alpha ); diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index e3bcafe9798..fe7d98f7be0 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -770,7 +770,7 @@ double *QgsImageOperation::createGaussianKernel( const int radius ) double result; for ( int i = 0; i <= radius; ++i ) { - result = coefficient * exp( i * i * expCoefficient ); + result = coefficient * std::exp( i * i * expCoefficient ); kernel[ radius - i ] = result; sum += result; if ( i > 0 ) diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index b596f679b1d..3175353e361 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -267,22 +267,22 @@ static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext * static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( tan( x ) ); + return QVariant( std::tan( x ) ); } static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( asin( x ) ); + return QVariant( std::asin( x ) ); } static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( acos( x ) ); + return QVariant( std::acos( x ) ); } static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( atan( x ) ); + return QVariant( std::atan( x ) ); } static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { @@ -293,14 +293,14 @@ static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); - return QVariant( exp( x ) ); + return QVariant( std::exp( x ) ); } static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ); if ( x <= 0 ) return QVariant(); - return QVariant( log( x ) ); + return QVariant( std::log( x ) ); } static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { @@ -315,7 +315,7 @@ static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext * double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ); if ( x <= 0 || b <= 0 ) return QVariant(); - return QVariant( log( x ) / log( b ) ); + return QVariant( std::log( x ) / std::log( b ) ); } static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { @@ -2656,11 +2656,11 @@ static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionConte { if ( pt1->y() < pt2->y() ) { - return atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ); + return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ); } else /* ( pt1->y() > pt2->y() ) - equality case handled above */ { - return atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI / 2 ); } } @@ -2669,12 +2669,12 @@ static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionConte { if ( pt1->y() > pt2->y() ) { - return atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI; } else /* ( pt1->y() < pt2->y() ) - equality case handled above */ { - return atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI / 2 ) ); } } diff --git a/src/core/expression/qgsexpressionnodeimpl.cpp b/src/core/expression/qgsexpressionnodeimpl.cpp index 8e33114cb78..d7528d875ef 100644 --- a/src/core/expression/qgsexpressionnodeimpl.cpp +++ b/src/core/expression/qgsexpressionnodeimpl.cpp @@ -577,7 +577,7 @@ double QgsExpressionNodeBinaryOperator::computeDouble( double x, double y ) case boDiv: return x / y; case boMod: - return fmod( x, y ); + return std::fmod( x, y ); default: Q_ASSERT( false ); return 0; diff --git a/src/core/geometry/qgscircularstring.cpp b/src/core/geometry/qgscircularstring.cpp index 5ccd632267e..07ceada9178 100644 --- a/src/core/geometry/qgscircularstring.cpp +++ b/src/core/geometry/qgscircularstring.cpp @@ -772,7 +772,7 @@ void QgsCircularString::sumUpArea( double &sum ) const bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0; bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0; - double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - 1 / M_PI * asin( d / radius ); + double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - 1 / M_PI * std::asin( d / radius ); double circleChordArea = 0; if ( circlePointLeftOfLine == centerPointLeftOfLine ) { diff --git a/src/core/geometry/qgsellipse.cpp b/src/core/geometry/qgsellipse.cpp index bfb3ddd7e5d..4b653d38201 100644 --- a/src/core/geometry/qgsellipse.cpp +++ b/src/core/geometry/qgsellipse.cpp @@ -139,7 +139,7 @@ void QgsEllipse::setAzimuth( const double azimuth ) double QgsEllipse::focusDistance() const { - return sqrt( mSemiMajorAxis * mSemiMajorAxis - mSemiMinorAxis * mSemiMinorAxis ); + return std::sqrt( mSemiMajorAxis * mSemiMajorAxis - mSemiMinorAxis * mSemiMinorAxis ); } QVector QgsEllipse::foci() const diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 95ed2a5e49e..203d1ad68ed 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -989,7 +989,7 @@ QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle // constrain angle to 0 - 180 if ( angle > 180.0 ) - angle = fmod( angle, 180.0 ); + angle = std::fmod( angle, 180.0 ); return minBounds; } diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index b1670fb2cf4..8724af6500e 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -659,7 +659,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co double increment = tolerance; //one segment per degree if ( toleranceType == QgsAbstractGeometry::MaximumDifference ) { - double halfAngle = acos( -tolerance / radius + 1 ); + double halfAngle = std::acos( -tolerance / radius + 1 ); increment = 2 * halfAngle; } @@ -936,7 +936,7 @@ double QgsGeometryUtils::normalizedAngle( double angle ) double clippedAngle = angle; if ( clippedAngle >= M_PI * 2 || clippedAngle <= -2 * M_PI ) { - clippedAngle = fmod( clippedAngle, 2 * M_PI ); + clippedAngle = std::fmod( clippedAngle, 2 * M_PI ); } if ( clippedAngle < 0.0 ) { diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index 769e2d50787..14ca2d20cf2 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -2704,12 +2704,12 @@ int QgsGeos::geomDigits( const GEOSGeometry *geom ) GEOSCoordSeq_getX_r( geosinit.ctxt, bBoxCoordSeq, i, &t ); int digits; - digits = std::ceil( log10( std::fabs( t ) ) ); + digits = std::ceil( std::log10( std::fabs( t ) ) ); if ( digits > maxDigits ) maxDigits = digits; GEOSCoordSeq_getY_r( geosinit.ctxt, bBoxCoordSeq, i, &t ); - digits = std::ceil( log10( std::fabs( t ) ) ); + digits = std::ceil( std::log10( std::fabs( t ) ) ); if ( digits > maxDigits ) maxDigits = digits; } diff --git a/src/core/geometry/qgspoint.cpp b/src/core/geometry/qgspoint.cpp index 4038b1ee132..f7fad7bc56c 100644 --- a/src/core/geometry/qgspoint.cpp +++ b/src/core/geometry/qgspoint.cpp @@ -480,12 +480,12 @@ QPointF QgsPoint::toQPointF() const double QgsPoint::distance( double x, double y ) const { - return sqrt( ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) ); + return std::sqrt( ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) ); } double QgsPoint::distance( const QgsPoint &other ) const { - return sqrt( ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) ); + return std::sqrt( ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) ); } double QgsPoint::distanceSquared( double x, double y ) const @@ -504,7 +504,7 @@ double QgsPoint::distance3D( double x, double y, double z ) const if ( is3D() || !std::isnan( z ) ) zDistSquared = ( mZ - z ) * ( mZ - z ); - return sqrt( ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared ); + return std::sqrt( ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared ); } double QgsPoint::distance3D( const QgsPoint &other ) const @@ -513,7 +513,7 @@ double QgsPoint::distance3D( const QgsPoint &other ) const if ( is3D() || other.is3D() ) zDistSquared = ( mZ - other.z() ) * ( mZ - other.z() ); - return sqrt( ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) + zDistSquared ); + return std::sqrt( ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) + zDistSquared ); } double QgsPoint::distanceSquared3D( double x, double y, double z ) const @@ -550,7 +550,7 @@ double QgsPoint::inclination( const QgsPoint &other ) const } double dz = other.z() - mZ; - return ( acos( dz / distance ) * 180.0 / M_PI ); + return ( std::acos( dz / distance ) * 180.0 / M_PI ); } QgsPoint QgsPoint::project( double distance, double azimuth, double inclination ) const @@ -559,7 +559,7 @@ QgsPoint QgsPoint::project( double distance, double azimuth, double inclination double radsXy = azimuth * M_PI / 180.0; double dx = 0.0, dy = 0.0, dz = 0.0; - inclination = fmod( inclination, 360.0 ); + inclination = std::fmod( inclination, 360.0 ); if ( !qgsDoubleNear( inclination, 90.0 ) ) pType = QgsWkbTypes::addZ( pType ); diff --git a/src/core/geometry/qgsrectangle.cpp b/src/core/geometry/qgsrectangle.cpp index b9a5e903b55..75c08eec6a7 100644 --- a/src/core/geometry/qgsrectangle.cpp +++ b/src/core/geometry/qgsrectangle.cpp @@ -305,7 +305,7 @@ QString QgsRectangle::toString( int precision ) const precision = 0; if ( ( width() < 10 || height() < 10 ) && ( width() > 0 && height() > 0 ) ) { - precision = static_cast( std::ceil( -1.0 * log10( qMin( width(), height() ) ) ) ) + 1; + precision = static_cast( std::ceil( -1.0 * std::log10( qMin( width(), height() ) ) ) ) + 1; // sanity check if ( precision > 20 ) precision = 20; diff --git a/src/core/geometry/qgstriangle.cpp b/src/core/geometry/qgstriangle.cpp index bb390874106..e9bff06c876 100644 --- a/src/core/geometry/qgstriangle.cpp +++ b/src/core/geometry/qgstriangle.cpp @@ -382,9 +382,9 @@ QVector QgsTriangle::angles() const cx = vertexAt( 2 ).x(); cy = vertexAt( 2 ).y(); - double a1 = fmod( QgsGeometryUtils::angleBetweenThreePoints( cx, cy, ax, ay, bx, by ), M_PI ); - double a2 = fmod( QgsGeometryUtils::angleBetweenThreePoints( ax, ay, bx, by, cx, cy ), M_PI ); - double a3 = fmod( QgsGeometryUtils::angleBetweenThreePoints( bx, by, cx, cy, ax, ay ), M_PI ); + double a1 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( cx, cy, ax, ay, bx, by ), M_PI ); + double a2 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( ax, ay, bx, by, cx, cy ), M_PI ); + double a3 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( bx, by, cx, cy, ax, ay ), M_PI ); angles.append( ( a1 > M_PI / 2 ? a1 - M_PI / 2 : a1 ) ); angles.append( ( a2 > M_PI / 2 ? a2 - M_PI / 2 : a2 ) ); diff --git a/src/core/layout/qgslayoututils.cpp b/src/core/layout/qgslayoututils.cpp index 387ba16cd9a..1422deee6fa 100644 --- a/src/core/layout/qgslayoututils.cpp +++ b/src/core/layout/qgslayoututils.cpp @@ -27,7 +27,7 @@ double QgsLayoutUtils::normalizedAngle( const double angle, const bool allowNega double clippedAngle = angle; if ( clippedAngle >= 360.0 || clippedAngle <= -360.0 ) { - clippedAngle = fmod( clippedAngle, 360.0 ); + clippedAngle = std::fmod( clippedAngle, 360.0 ); } if ( !allowNegative && clippedAngle < 0.0 ) { diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index c0ec8a1f68e..1c2a5889c34 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -720,7 +720,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QList 1 (lower for longer segments) - double segmentAngleCost = 1 - std::fabs( fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2; // 0 -> 1, lower for more horizontal segments + double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2; // 0 -> 1, lower for more horizontal segments while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment ) { diff --git a/src/core/pal/geomfunction.h b/src/core/pal/geomfunction.h index a8e0d762d70..7980a3a6e1c 100644 --- a/src/core/pal/geomfunction.h +++ b/src/core/pal/geomfunction.h @@ -65,7 +65,7 @@ namespace pal static inline double dist_euc2d( double x1, double y1, double x2, double y2 ) { - return sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ); + return std::sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ); } static inline double dist_euc2d_sq( double x1, double y1, double x2, double y2 ) diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index 74a2a390057..270f3026bdb 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -525,7 +525,7 @@ int LabelPosition::polygonIntersectionCost( PointSet *polygon ) const //effectively take the average polygon intersection cost for all label parts double totalCost = polygonIntersectionCostForParts( polygon ); int n = partCount(); - return ceil( totalCost / n ); + return std::ceil( totalCost / n ); } bool LabelPosition::intersectsWithPolygon( PointSet *polygon ) const diff --git a/src/core/qgis.h b/src/core/qgis.h index 4a1e853fd2c..4d8a9d7ea38 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -232,8 +232,8 @@ inline bool qgsDoubleNearSig( double a, double b, int significantDigits = 10 ) // has to be considered (maybe TODO) // Is there a better way? int aexp, bexp; - double ar = frexp( a, &aexp ); - double br = frexp( b, &bexp ); + double ar = std::frexp( a, &aexp ); + double br = std::frexp( b, &bexp ); return aexp == bexp && std::round( ar * std::pow( 10.0, significantDigits ) ) == std::round( br * std::pow( 10.0, significantDigits ) ); diff --git a/src/core/qgscolorramp.cpp b/src/core/qgscolorramp.cpp index 8182256b14b..b0920dac184 100644 --- a/src/core/qgscolorramp.cpp +++ b/src/core/qgscolorramp.cpp @@ -396,7 +396,7 @@ QList QgsLimitedRandomColorRamp::randomColors( int count, //see http://basecase.org/env/on-rainbows for more details currentHueAngle += 137.50776; //scale hue to between hueMax and hueMin - h = qBound( 0.0, std::round( ( fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 359.0 ); + h = qBound( 0.0, std::round( ( std::fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 359.0 ); s = qBound( 0, ( qrand() % ( safeSatMax - safeSatMin + 1 ) ) + safeSatMin, 255 ); v = qBound( 0, ( qrand() % ( safeValMax - safeValMin + 1 ) ) + safeValMin, 255 ); colors.append( QColor::fromHsv( h, s, v ) ); diff --git a/src/core/qgscoordinateutils.cpp b/src/core/qgscoordinateutils.cpp index 561c47837a6..319666518ae 100644 --- a/src/core/qgscoordinateutils.cpp +++ b/src/core/qgscoordinateutils.cpp @@ -45,7 +45,7 @@ int QgsCoordinateUtils::calculateCoordinatePrecision( double mapUnitsPerPixel, c // having enough decimal places to show the difference in position between adjacent pixels. // Also avoid taking the log of 0. if ( !qgsDoubleNear( mapUnitsPerPixel, 0.0 ) ) - dp = static_cast( std::ceil( -1.0 * log10( mapUnitsPerPixel ) ) ); + dp = static_cast( std::ceil( -1.0 * std::log10( mapUnitsPerPixel ) ) ); } else { diff --git a/src/core/qgsdiagramrenderer.cpp b/src/core/qgsdiagramrenderer.cpp index c65bea80449..9ca2279ec94 100644 --- a/src/core/qgsdiagramrenderer.cpp +++ b/src/core/qgsdiagramrenderer.cpp @@ -258,7 +258,7 @@ void QgsDiagramSettings::readXml( const QDomElement &elem ) barWidth = elem.attribute( QStringLiteral( "barWidth" ) ).toDouble(); if ( elem.hasAttribute( QStringLiteral( "angleOffset" ) ) ) - rotationOffset = fmod( 360.0 - elem.attribute( QStringLiteral( "angleOffset" ) ).toInt() / 16.0, 360.0 ); + rotationOffset = std::fmod( 360.0 - elem.attribute( QStringLiteral( "angleOffset" ) ).toInt() / 16.0, 360.0 ); else rotationOffset = elem.attribute( QStringLiteral( "rotationOffset" ) ).toDouble(); diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index a964693aef7..85a966f3a49 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -411,8 +411,8 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( double radians_long = DEG2RAD( p1.x() ); double b2 = POW2( b ); // spheroid_mu2 double omf = 1 - f; - double tan_u1 = omf * tan( radians_lat ); - double u1 = atan( tan_u1 ); + double tan_u1 = omf * std::tan( radians_lat ); + double u1 = std::atan( tan_u1 ); double sigma, last_sigma, delta_sigma, two_sigma_m; double sigma1, sin_alpha, alpha, cos_alphasq; double u2, A, B; @@ -428,7 +428,7 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject( } sigma1 = std::atan2( tan_u1, std::cos( azimuth ) ); sin_alpha = std::cos( u1 ) * std::sin( azimuth ); - alpha = asin( sin_alpha ); + alpha = std::asin( sin_alpha ); cos_alphasq = 1.0 - POW2( sin_alpha ); u2 = POW2( std::cos( alpha ) ) * ( POW2( a ) - b2 ) / b2; // spheroid_mu2 A = 1.0 + ( u2 / 16384.0 ) * ( 4096.0 + u2 * ( -768.0 + u2 * ( 320.0 - 175.0 * u2 ) ) ); @@ -551,8 +551,8 @@ double QgsDistanceArea::computeDistanceBearing( double p2_lat = DEG2RAD( p2.y() ), p2_lon = DEG2RAD( p2.x() ); double L = p2_lon - p1_lon; - double U1 = atan( ( 1 - f ) * tan( p1_lat ) ); - double U2 = atan( ( 1 - f ) * tan( p2_lat ) ); + double U1 = std::atan( ( 1 - f ) * std::tan( p1_lat ) ); + double U2 = std::atan( ( 1 - f ) * std::tan( p2_lat ) ); double sinU1 = std::sin( U1 ), cosU1 = std::cos( U1 ); double sinU2 = std::sin( U2 ), cosU2 = std::cos( U2 ); double lambda = L; @@ -580,7 +580,7 @@ double QgsDistanceArea::computeDistanceBearing( sinSigma = std::sqrt( tu1 * tu1 + tu2 * tu2 ); cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = std::atan2( sinSigma, cosSigma ); - alpha = asin( cosU1 * cosU2 * sinLambda / sinSigma ); + alpha = std::asin( cosU1 * cosU2 * sinLambda / sinSigma ); cosSqAlpha = std::cos( alpha ) * std::cos( alpha ); cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; C = f / 16 * cosSqAlpha * ( 4 + f * ( 4 - 3 * cosSqAlpha ) ); @@ -797,7 +797,7 @@ double QgsDistanceArea::computePolygonFlatArea( const QList &points } // QgsDebugMsg("Area from point: " + (points[i % size]).toString(2)); area = area / 2.0; - return fabs( area ); // All areas are positive! + return std::fabs( area ); // All areas are positive! } QString QgsDistanceArea::formatDistance( double distance, int decimals, QgsUnitTypes::DistanceUnit unit, bool keepBaseUnit ) diff --git a/src/core/qgshistogram.cpp b/src/core/qgshistogram.cpp index 1500594230d..d54a561a9a4 100644 --- a/src/core/qgshistogram.cpp +++ b/src/core/qgshistogram.cpp @@ -69,7 +69,7 @@ double QgsHistogram::optimalBinWidth() const int QgsHistogram::optimalNumberBins() const { - return ceil( ( mMax - mMin ) / optimalBinWidth() ); + return std::ceil( ( mMax - mMin ) / optimalBinWidth() ); } QList QgsHistogram::binEdges( int bins ) const diff --git a/src/core/qgsmapsettingsutils.cpp b/src/core/qgsmapsettingsutils.cpp index 6bccd819a1f..d54c7772152 100644 --- a/src/core/qgsmapsettingsutils.cpp +++ b/src/core/qgsmapsettingsutils.cpp @@ -93,7 +93,7 @@ QString QgsMapSettingsUtils::worldFileContent( const QgsMapSettings &mapSettings // rotation matrix double r[6]; r[0] = std::cos( alpha ); - r[1] = -sin( alpha ); + r[1] = -std::sin( alpha ); r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha ); r[3] = std::sin( alpha ); r[4] = std::cos( alpha ); diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 20b568dac99..ac7aef85b5f 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -604,7 +604,7 @@ void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer ) if ( tempAngle.isValid() ) { double oldAngle = layer->customProperty( QStringLiteral( "labeling/angleOffset" ), QVariant( 0.0 ) ).toDouble(); - angleOffset = fmod( 360 - oldAngle, 360.0 ); + angleOffset = std::fmod( 360 - oldAngle, 360.0 ); } else { @@ -824,7 +824,7 @@ void QgsPalLayerSettings::readXml( QDomElement &elem, const QgsReadWriteContext if ( placementElem.hasAttribute( QStringLiteral( "angleOffset" ) ) ) { double oldAngle = placementElem.attribute( QStringLiteral( "angleOffset" ), QStringLiteral( "0" ) ).toDouble(); - angleOffset = fmod( 360 - oldAngle, 360.0 ); + angleOffset = std::fmod( 360 - oldAngle, 360.0 ); } else { diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index 73c1b57b4d5..f98798337c3 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -61,7 +61,7 @@ QString QgsPointXY::toString( int precision ) const QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix, const bool padded ) const { //first, limit longitude to -360 to 360 degree range - double myWrappedX = fmod( mX, 360.0 ); + double myWrappedX = std::fmod( mX, 360.0 ); //next, wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W" if ( myWrappedX > 180.0 ) { @@ -73,7 +73,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix } //first, limit latitude to -180 to 180 degree range - double myWrappedY = fmod( mY, 180.0 ); + double myWrappedY = std::fmod( mY, 180.0 ); //next, wrap around latitudes > 90 or < -90 degrees, so that, e.g., "110S" -> "70N" if ( myWrappedY > 90.0 ) { @@ -175,7 +175,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const bool padded ) const { //first, limit longitude to -360 to 360 degree range - double myWrappedX = fmod( mX, 360.0 ); + double myWrappedX = std::fmod( mX, 360.0 ); //next, wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W" if ( myWrappedX > 180.0 ) { @@ -273,12 +273,12 @@ double QgsPointXY::sqrDist( const QgsPointXY &other ) const double QgsPointXY::distance( double x, double y ) const { - return sqrt( sqrDist( x, y ) ); + return std::sqrt( sqrDist( x, y ) ); } double QgsPointXY::distance( const QgsPointXY &other ) const { - return sqrt( sqrDist( other ) ); + return std::sqrt( sqrDist( other ) ); } double QgsPointXY::azimuth( const QgsPointXY &other ) const diff --git a/src/core/qgsscalecalculator.cpp b/src/core/qgsscalecalculator.cpp index 1ebeb5706f2..4973ab400df 100644 --- a/src/core/qgsscalecalculator.cpp +++ b/src/core/qgsscalecalculator.cpp @@ -115,7 +115,7 @@ double QgsScaleCalculator::calculateGeographicDistance( const QgsRectangle &mapE // For a longitude change of 180 degrees double lat = ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5; - static const double RADS = ( 4.0 * atan( 1.0 ) ) / 180.0; + static const double RADS = ( 4.0 * std::atan( 1.0 ) ) / 180.0; double a = std::pow( std::cos( lat * RADS ), 2 ); double c = 2.0 * std::atan2( std::sqrt( a ), std::sqrt( 1.0 - a ) ); static const double RA = 6378000; // [m] diff --git a/src/core/qgstolerance.cpp b/src/core/qgstolerance.cpp index 2fa487be7c8..85c46ca9bf8 100644 --- a/src/core/qgstolerance.cpp +++ b/src/core/qgstolerance.cpp @@ -109,11 +109,11 @@ double QgsTolerance::computeMapUnitPerPixel( QgsMapLayer *layer, const QgsMapSet double y = p3.sqrDist( p4 ); if ( x > y ) { - return sqrt( x ); + return std::sqrt( x ); } else { - return sqrt( y ); + return std::sqrt( y ); } } diff --git a/src/core/qgsvector.cpp b/src/core/qgsvector.cpp index ac2c48fddd8..974a0722672 100644 --- a/src/core/qgsvector.cpp +++ b/src/core/qgsvector.cpp @@ -76,7 +76,7 @@ QgsVector &QgsVector::operator-=( QgsVector other ) double QgsVector::length() const { - return sqrt( mX * mX + mY * mY ); + return std::sqrt( mX * mX + mY * mY ); } double QgsVector::x() const diff --git a/src/core/raster/qgscolorrampshader.cpp b/src/core/raster/qgscolorrampshader.cpp index 740f02d3047..aaebc1f2be3 100644 --- a/src/core/raster/qgscolorrampshader.cpp +++ b/src/core/raster/qgscolorrampshader.cpp @@ -285,8 +285,8 @@ void QgsColorRampShader::classifyColorRamp( const int classes, const int band, c QVector::const_iterator color_it = entryColors.begin(); // calculate a reasonable number of decimals to display - double maxabs = log10( qMax( std::fabs( max ), std::fabs( min ) ) ); - int nDecimals = std::round( qMax( 3.0 + maxabs - log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); + double maxabs = std::log10( qMax( std::fabs( max ), std::fabs( min ) ) ); + int nDecimals = std::round( qMax( 3.0 + maxabs - std::log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); QList colorRampItems; for ( ; value_it != entryValues.end(); ++value_it, ++color_it ) diff --git a/src/core/raster/qgshillshaderenderer.cpp b/src/core/raster/qgshillshaderenderer.cpp index 4fdf13bcd83..4fd0cfa0adb 100644 --- a/src/core/raster/qgshillshaderenderer.cpp +++ b/src/core/raster/qgshillshaderenderer.cpp @@ -210,7 +210,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize ); double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize ); - double slopeRad = atan( mZFactor * std::sqrt( derX * derX + derY * derY ) ); + double slopeRad = std::atan( mZFactor * std::sqrt( derX * derX + derY * derY ) ); double aspectRad = std::atan2( derX, -derY ); diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index 42cf37806ee..6b58fd39b5a 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -202,7 +202,7 @@ double QgsRasterChecker::tolerance( double val, int places ) { // float precision is about 7 decimal digits, double about 16 // default places = 6 - return 1. * std::pow( 10, std::round( log10( std::fabs( val ) ) - places ) ); + return 1. * std::pow( 10, std::round( std::log10( std::fabs( val ) ) - places ) ); } QString QgsRasterChecker::compareHead() diff --git a/src/core/raster/qgsrasterinterface.h b/src/core/raster/qgsrasterinterface.h index 437c03779b1..99842f27eaf 100644 --- a/src/core/raster/qgsrasterinterface.h +++ b/src/core/raster/qgsrasterinterface.h @@ -203,7 +203,7 @@ class CORE_EXPORT QgsRasterInterface //! \brief helper function to create zero padded band names virtual QString generateBandName( int bandNumber ) const { - return tr( "Band" ) + QStringLiteral( " %1" ) .arg( bandNumber, 1 + static_cast< int >( log10( static_cast< double >( bandCount() ) ) ), 10, QChar( '0' ) ); + return tr( "Band" ) + QStringLiteral( " %1" ) .arg( bandNumber, 1 + static_cast< int >( std::log10( static_cast< double >( bandCount() ) ) ), 10, QChar( '0' ) ); } /** Read block of data using given extent and size. diff --git a/src/core/simplify/effectivearea.cpp b/src/core/simplify/effectivearea.cpp index d193666b759..126064431c0 100644 --- a/src/core/simplify/effectivearea.cpp +++ b/src/core/simplify/effectivearea.cpp @@ -43,7 +43,7 @@ static void destroy_minheap( MINHEAP tree ) */ static double triarea2d( const QgsPoint &P1, const QgsPoint &P2, const QgsPoint &P3 ) { - return fabs( 0.5 * ( ( P1.x() - P2.x() ) * ( P3.y() - P2.y() ) - ( P1.y() - P2.y() ) * ( P3.x() - P2.x() ) ) ); + return std::fabs( 0.5 * ( ( P1.x() - P2.x() ) * ( P3.y() - P2.y() ) - ( P1.y() - P2.y() ) * ( P3.x() - P2.x() ) ) ); } /** diff --git a/src/core/symbology/qgsarrowsymbollayer.cpp b/src/core/symbology/qgsarrowsymbollayer.cpp index 6e33fe5e58d..f2564251beb 100644 --- a/src/core/symbology/qgsarrowsymbollayer.cpp +++ b/src/core/symbology/qgsarrowsymbollayer.cpp @@ -201,7 +201,7 @@ void QgsArrowSymbolLayer::stopRender( QgsSymbolRenderContext &context ) inline qreal euclidian_distance( QPointF po, QPointF pd ) { - return sqrt( ( po.x() - pd.x() ) * ( po.x() - pd.x() ) + ( po.y() - pd.y() ) * ( po.y() - pd.y() ) ); + return std::sqrt( ( po.x() - pd.x() ) * ( po.x() - pd.x() ) + ( po.y() - pd.y() ) * ( po.y() - pd.y() ) ); } QPolygonF straightArrow( QPointF po, QPointF pd, @@ -506,7 +506,7 @@ QPolygonF curvedArrow( QPointF po, QPointF pm, QPointF pd, pm = circlePoint( circleCenter, circleRadius, angle_m ); pd = circlePoint( circleCenter, circleRadius, angle_d ); - qreal headAngle = direction * atan( headWidth / circleRadius ); + qreal headAngle = direction * std::atan( headWidth / circleRadius ); QPainterPath path; diff --git a/src/core/symbology/qgsheatmaprenderer.cpp b/src/core/symbology/qgsheatmaprenderer.cpp index 8d013248be2..0ced32a6f34 100644 --- a/src/core/symbology/qgsheatmaprenderer.cpp +++ b/src/core/symbology/qgsheatmaprenderer.cpp @@ -203,12 +203,12 @@ double QgsHeatmapRenderer::uniformKernel( const double distance, const int bandw double QgsHeatmapRenderer::quarticKernel( const double distance, const int bandwidth ) const { - return pow( 1. - std::pow( distance / static_cast< double >( bandwidth ), 2 ), 2 ); + return std::pow( 1. - std::pow( distance / static_cast< double >( bandwidth ), 2 ), 2 ); } double QgsHeatmapRenderer::triweightKernel( const double distance, const int bandwidth ) const { - return pow( 1. - std::pow( distance / static_cast< double >( bandwidth ), 2 ), 3 ); + return std::pow( 1. - std::pow( distance / static_cast< double >( bandwidth ), 2 ), 3 ); } double QgsHeatmapRenderer::epanechnikovKernel( const double distance, const int bandwidth ) const diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 771e6605823..ee7db666715 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -672,7 +672,7 @@ class MyLine // return angle in radians double angle() { - double a = ( mVertical ? M_PI / 2 : atan( mT ) ); + double a = ( mVertical ? M_PI / 2 : std::atan( mT ) ); if ( !mIncreasing ) a += M_PI; @@ -685,7 +685,7 @@ class MyLine if ( mVertical ) return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) ); - double alpha = atan( mT ); + double alpha = std::atan( mT ); double dx = std::cos( alpha ) * interval; double dy = std::sin( alpha ) * interval; return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) ); diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 96726c1210c..32c13eb1c29 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3932,7 +3932,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, cell = 20 * 1e-07; } - double base = std::pow( 10.0, std::floor( log10( cell ) ) ); + double base = std::pow( 10.0, std::floor( std::log10( cell ) ) ); double unit = base; if ( ( 2 * base ) - cell < h * ( cell - unit ) ) { diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 885f3f55197..25c432f1b84 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -828,7 +828,7 @@ bool QgsAdvancedDigitizingDockWidget::applyConstraints( QgsMapMouseEvent *e ) point.x() - previousPt.x() ) - angle ) * 180 / M_PI; // modulus - angle = fmod( angle, 360.0 ); + angle = std::fmod( angle, 360.0 ); mAngleConstraint->setValue( angle ); } // --- distance diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index e44f654ab2a..ffa518e0194 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -456,10 +456,10 @@ void QgsColorWheel::paintEvent( QPaintEvent *event ) double lightness = mCurrentColor.lightnessF(); double hueRadians = ( h * M_PI / 180.0 ); double hx = std::cos( hueRadians ) * triangleRadius; - double hy = -sin( hueRadians ) * triangleRadius; - double sx = -cos( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; - double sy = -sin( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; - double vx = -cos( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; + double hy = -std::sin( hueRadians ) * triangleRadius; + double sx = -std::cos( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; + double sy = -std::sin( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; + double vx = -std::cos( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; double vy = std::sin( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius; double mx = ( sx + vx ) / 2.0; double my = ( sy + vy ) / 2.0; @@ -539,31 +539,31 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) double eventAngleRadians = line.angle() * M_PI / 180.0; double hueRadians = h * M_PI / 180.0; - double rad0 = fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI ); - double rad1 = fmod( rad0, ( ( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 ); + double rad0 = std::fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI ); + double rad1 = std::fmod( rad0, ( ( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 ); double length = mWheelImage->width() / 2.0; double triangleLength = length - mWheelThickness - 1; double a = 0.5 * triangleLength; - double b = tan( rad1 ) * a; + double b = std::tan( rad1 ) * a; double r = std::sqrt( x * x + y * y ); double maxR = std::sqrt( a * a + b * b ); if ( r > maxR ) { - double dx = tan( rad1 ) * r; - double rad2 = atan( dx / maxR ); + double dx = std::tan( rad1 ) * r; + double rad2 = std::atan( dx / maxR ); rad2 = qMin( rad2, M_PI / 3.0 ); rad2 = qMax( rad2, -M_PI / 3.0 ); eventAngleRadians += rad2 - rad1; - rad0 = fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI ); - rad1 = fmod( rad0, ( ( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 ); - b = tan( rad1 ) * a; + rad0 = std::fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI ); + rad1 = std::fmod( rad0, ( ( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 ); + b = std::tan( rad1 ) * a; r = std::sqrt( a * a + b * b ); } double triangleSideLength = std::sqrt( 3.0 ) * triangleLength; - double newL = ( ( -sin( rad0 ) * r ) / triangleSideLength ) + 0.5; + double newL = ( ( -std::sin( rad0 ) * r ) / triangleSideLength ) + 0.5; double widthShare = 1.0 - ( std::fabs( newL - 0.5 ) * 2.0 ); double newS = ( ( ( std::cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare; s = qMin( static_cast< int >( std::round( qMax( 0.0, newS ) * 255.0 ) ), 255 ); diff --git a/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp index d3fbd2e93ff..dbc503e6190 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp @@ -48,12 +48,12 @@ int QgsGeometryCheckPrecision::reducedPrecision() double QgsGeometryCheckPrecision::tolerance() { - return pow( 10, -get()->mPrecision ); + return std::pow( 10, -get()->mPrecision ); } double QgsGeometryCheckPrecision::reducedTolerance() { - return pow( 10, -get()->mReducedPrecision ); + return std::pow( 10, -get()->mReducedPrecision ); } QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, diff --git a/src/plugins/georeferencer/qgsgeorefplugingui.cpp b/src/plugins/georeferencer/qgsgeorefplugingui.cpp index 0e1c3e3af98..17d6e371132 100644 --- a/src/plugins/georeferencer/qgsgeorefplugingui.cpp +++ b/src/plugins/georeferencer/qgsgeorefplugingui.cpp @@ -805,7 +805,7 @@ void QgsGeorefPluginGui::updateMouseCoordinatePrecision() // to show the difference in position between adjacent pixels. // Also avoid taking the log of 0. if ( mCanvas->mapUnitsPerPixel() != 0.0 ) - dp = static_cast( std::ceil( -1.0 * log10( mCanvas->mapUnitsPerPixel() ) ) ); + dp = static_cast( std::ceil( -1.0 * std::log10( mCanvas->mapUnitsPerPixel() ) ) ); } else dp = QgsProject::instance()->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) ); diff --git a/src/plugins/georeferencer/qgsresidualplotitem.cpp b/src/plugins/georeferencer/qgsresidualplotitem.cpp index df94fef0e3e..72ce3907b6a 100644 --- a/src/plugins/georeferencer/qgsresidualplotitem.cpp +++ b/src/plugins/georeferencer/qgsresidualplotitem.cpp @@ -111,14 +111,14 @@ void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsIt int nDecPlaces; if ( scaleBarWidthUnits < 1 ) { - nDecPlaces = -floor( log10( scaleBarWidthUnits ) ); + nDecPlaces = -std::floor( std::log10( scaleBarWidthUnits ) ); scaleBarWidthUnits *= std::pow( 10.0, nDecPlaces ); scaleBarWidthUnits = ( int )( scaleBarWidthUnits + 0.5 ); scaleBarWidthUnits /= std::pow( 10.0, nDecPlaces ); } else { - nDecPlaces = ( int )log10( scaleBarWidthUnits ); + nDecPlaces = ( int )std::log10( scaleBarWidthUnits ); scaleBarWidthUnits /= std::pow( 10.0, nDecPlaces ); scaleBarWidthUnits = ( int )( scaleBarWidthUnits + 0.5 ); scaleBarWidthUnits *= std::pow( 10.0, nDecPlaces ); @@ -228,5 +228,5 @@ double QgsResidualPlotItem::dist( QPointF p1, QPointF p2 ) const { double dx = p2.x() - p1.x(); double dy = p2.y() - p1.y(); - return sqrt( dx * dx + dy * dy ); + return std::sqrt( dx * dx + dy * dy ); } diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 236777ecb84..5c906c35818 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -959,7 +959,7 @@ QString QgsGdalProvider::generateBandName( int bandNumber ) const } if ( !bandNameValues.isEmpty() ) - return tr( "Band" ) + QStringLiteral( " %1 / %2" ) .arg( bandNumber, 1 + ( int ) log10( ( float ) bandCount() ), 10, QChar( '0' ) ).arg( bandNameValues.join( QStringLiteral( " / " ) ) ); + return tr( "Band" ) + QStringLiteral( " %1 / %2" ) .arg( bandNumber, 1 + ( int ) std::log10( ( float ) bandCount() ), 10, QChar( '0' ) ).arg( bandNameValues.join( QStringLiteral( " / " ) ) ); } } } diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index fac436e8316..094c9ac9190 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -500,7 +500,7 @@ void QgsWmsProvider::setFormatQueryItem( QUrl &url ) static bool _fuzzyContainsRect( const QRectF &r1, const QRectF &r2 ) { - double significantDigits = log10( qMax( r1.width(), r1.height() ) ); + double significantDigits = std::log10( qMax( r1.width(), r1.height() ) ); double epsilon = std::pow( 10.0, significantDigits - 5 ); // floats have 6-9 significant digits return r1.contains( r2.adjusted( epsilon, epsilon, -epsilon, -epsilon ) ); } @@ -1189,8 +1189,8 @@ void QgsWmsProvider::setupXyzCapabilities( const QString &uri ) // the whole world is projected to a square: // X going from 180 W to 180 E // Y going from ~85 N to ~85 S (=atan(sinh(pi)) ... to get a square) - QgsPointXY topLeftLonLat( -180, 180.0 / M_PI * atan( sinh( M_PI ) ) ); - QgsPointXY bottomRightLonLat( 180, 180.0 / M_PI * atan( sinh( -M_PI ) ) ); + QgsPointXY topLeftLonLat( -180, 180.0 / M_PI * std::atan( std::sinh( M_PI ) ) ); + QgsPointXY bottomRightLonLat( 180, 180.0 / M_PI * std::atan( std::sinh( -M_PI ) ) ); QgsPointXY topLeft = ct.transform( topLeftLonLat ); QgsPointXY bottomRight = ct.transform( bottomRightLonLat ); double xspan = ( bottomRight.x() - topLeft.x() ); @@ -4044,7 +4044,7 @@ static QString formatDouble( double x ) { if ( x == 0.0 ) return QStringLiteral( "0" ); - const int numberOfDecimals = qMax( 0, 19 - static_cast( std::ceil( log10( std::fabs( x ) ) ) ) ); + const int numberOfDecimals = qMax( 0, 19 - static_cast( std::ceil( std::log10( std::fabs( x ) ) ) ) ); return qgsDoubleToString( x, numberOfDecimals ); } From 34003259b79286fedf304586f12acf1b4d0af1e2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 04:15:36 +1000 Subject: [PATCH 128/364] Replace use of math.h with cmath --- src/app/pluginmanager/qgspluginmanager.cpp | 4 ++-- src/app/qgsmaptoolrotatefeature.cpp | 2 +- src/core/layout/qgslayoututils.cpp | 2 +- src/core/pal/geomfunction.h | 2 +- src/plugins/georeferencer/qgsgeorefdelegates.cpp | 2 +- src/providers/grass/qgis.r.in.cpp | 2 +- src/providers/grass/qgis.v.in.cpp | 2 +- src/providers/wfs/qgswfsshareddata.cpp | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/pluginmanager/qgspluginmanager.cpp b/src/app/pluginmanager/qgspluginmanager.cpp index 843805137b6..41608a777f4 100644 --- a/src/app/pluginmanager/qgspluginmanager.cpp +++ b/src/app/pluginmanager/qgspluginmanager.cpp @@ -15,7 +15,7 @@ * * ***************************************************************************/ -#include +#include #include #include @@ -1580,4 +1580,4 @@ void QgsPluginManager::pushMessage( const QString &text, QgsMessageBar::MessageL void QgsPluginManager::showHelp() { QgsHelp::openHelp( QStringLiteral( "plugins/plugins.html" ) ); -} \ No newline at end of file +} diff --git a/src/app/qgsmaptoolrotatefeature.cpp b/src/app/qgsmaptoolrotatefeature.cpp index fbd05b01a79..6c38c3ec4dd 100644 --- a/src/app/qgsmaptoolrotatefeature.cpp +++ b/src/app/qgsmaptoolrotatefeature.cpp @@ -34,7 +34,7 @@ #include #include -#include +#include #define PI 3.14159265 diff --git a/src/core/layout/qgslayoututils.cpp b/src/core/layout/qgslayoututils.cpp index 1422deee6fa..3429259c205 100644 --- a/src/core/layout/qgslayoututils.cpp +++ b/src/core/layout/qgslayoututils.cpp @@ -20,7 +20,7 @@ #include "qgsrendercontext.h" #include "qgslayoutitemmap.h" #include -#include +#include double QgsLayoutUtils::normalizedAngle( const double angle, const bool allowNegative ) { diff --git a/src/core/pal/geomfunction.h b/src/core/pal/geomfunction.h index 7980a3a6e1c..a24d016bc68 100644 --- a/src/core/pal/geomfunction.h +++ b/src/core/pal/geomfunction.h @@ -34,7 +34,7 @@ #include "qgis_core.h" -#include "math.h" +#include #include "qgsgeos.h" namespace pal diff --git a/src/plugins/georeferencer/qgsgeorefdelegates.cpp b/src/plugins/georeferencer/qgsgeorefdelegates.cpp index fa3f36113d0..f5fd24b7378 100644 --- a/src/plugins/georeferencer/qgsgeorefdelegates.cpp +++ b/src/plugins/georeferencer/qgsgeorefdelegates.cpp @@ -19,7 +19,7 @@ #include "qgsgeorefvalidators.h" #include "qgsgeorefdelegates.h" -#include +#include // ------------------------ QgsNonEditableDelegate ------------------------- // QgsNonEditableDelegate::QgsNonEditableDelegate( QWidget *parent ) diff --git a/src/providers/grass/qgis.r.in.cpp b/src/providers/grass/qgis.r.in.cpp index 188a2f9e03a..c741232369b 100644 --- a/src/providers/grass/qgis.r.in.cpp +++ b/src/providers/grass/qgis.r.in.cpp @@ -18,7 +18,7 @@ extern "C" #include #include #include -#include +#include #ifdef WIN32 #include #include diff --git a/src/providers/grass/qgis.v.in.cpp b/src/providers/grass/qgis.v.in.cpp index dc663749f91..f8595f8ebf1 100644 --- a/src/providers/grass/qgis.v.in.cpp +++ b/src/providers/grass/qgis.v.in.cpp @@ -18,7 +18,7 @@ extern "C" #include #include #include -#include +#include #include #ifdef WIN32 #include diff --git a/src/providers/wfs/qgswfsshareddata.cpp b/src/providers/wfs/qgswfsshareddata.cpp index 1e5d508d475..641237c7924 100644 --- a/src/providers/wfs/qgswfsshareddata.cpp +++ b/src/providers/wfs/qgswfsshareddata.cpp @@ -13,7 +13,7 @@ * * ***************************************************************************/ -#include // M_PI +#include // M_PI #include "qgswfsconstants.h" #include "qgswfsshareddata.h" From 76a22726fd72ec32b1601a1c93d0211b36d537ce Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 04:46:16 +1000 Subject: [PATCH 129/364] Always use " for qgs header imports --- src/analysis/interpolation/NormVecDecorator.h | 4 +-- src/analysis/interpolation/ParametricLine.cc | 2 +- .../interpolation/TriangleInterpolator.h | 4 +-- src/analysis/interpolation/Triangulation.h | 4 +-- src/analysis/network/qgsgraphanalyzer.h | 2 +- src/analysis/network/qgsgraphbuilder.cpp | 4 +-- src/analysis/network/qgsgraphbuilder.h | 3 +- .../network/qgsgraphbuilderinterface.h | 6 ++-- .../network/qgsnetworkdistancestrategy.h | 2 +- .../network/qgsnetworkspeedstrategy.h | 2 +- src/analysis/network/qgsnetworkstrategy.h | 4 +-- src/app/composer/qgscompositionwidget.cpp | 2 +- src/app/dwg/qgsdwgimporter.h | 2 +- src/app/gps/qgsgpsinformationwidget.h | 6 ++-- .../qgsapppluginmanagerinterface.cpp | 2 +- src/app/qgisapp.cpp | 10 +++---- src/app/qgsdecorationgrid.h | 2 +- src/app/qgsdiagramproperties.h | 2 +- src/app/qgslabelengineconfigdialog.cpp | 2 +- src/app/qgslabelingwidget.h | 4 +-- src/app/qgsmapcanvasdockwidget.h | 2 +- src/app/qgsmaptoolpinlabels.cpp | 2 +- src/app/qgsmaptoolshowhidelabels.cpp | 2 +- src/app/qgsoptions.h | 2 +- src/app/qgsrulebasedlabelingwidget.h | 2 +- src/core/geometry/qgsrectangle.cpp | 1 - src/core/pal/util.cpp | 2 +- src/core/qgsactionmanager.cpp | 2 +- src/core/qgsapplication.h | 4 +-- src/core/qgslabelsearchtree.h | 4 +-- src/core/qgslocalec.cpp | 2 +- src/core/qgsmessagelog.cpp | 2 +- src/core/qgsnetworkaccessmanager.cpp | 10 +++---- src/core/qgsofflineediting.h | 4 +-- src/core/qgspalgeometry.h | 2 +- src/core/qgspallabeling.cpp | 30 +++++++++---------- src/core/qgsrenderchecker.h | 8 ++--- src/core/qgsscalecalculator.h | 2 +- src/core/qgstextlabelfeature.cpp | 2 +- src/gui/attributetable/qgsfeaturelistmodel.h | 2 +- .../qgsorganizetablecolumnsdialog.cpp | 8 ++--- .../effects/qgseffectstackpropertieswidget.h | 2 +- src/gui/ogr/qgsvectorlayersaveasdialog.h | 2 +- src/gui/qgsadvanceddigitizingdockwidget.h | 2 +- src/gui/qgsattributeeditorcontext.h | 6 ++-- src/gui/qgsbrowserdockwidget.h | 8 ++--- src/gui/qgsbrowserdockwidget_p.h | 8 ++--- src/gui/qgscredentialdialog.h | 2 +- src/gui/qgsdetaileditemwidget.h | 4 +-- src/gui/qgslegendfilterbutton.cpp | 4 +-- src/gui/qgsmapcanvasmap.h | 2 +- src/gui/qgsmessagebaritem.h | 4 +-- src/gui/qgsmessagelogviewer.h | 2 +- src/gui/qgsnewhttpconnection.h | 2 +- src/gui/qgsprojectionselectiondialog.h | 2 +- src/gui/qgsprojectionselectiontreewidget.h | 2 +- src/gui/qgssqlcomposerdialog.h | 2 +- src/gui/qgssublayersdialog.h | 4 +-- src/gui/qgstextformatwidget.h | 2 +- .../qgsdatadefinedsizelegendwidget.h | 2 +- .../coordinate_capture/coordinatecapture.cpp | 14 ++++----- .../coordinate_capture/coordinatecapture.h | 4 +-- .../evisdatabaseconnectiongui.h | 2 +- .../evisdatabaselayerfieldselectiongui.h | 2 +- src/plugins/evis/evis.cpp | 8 ++--- src/plugins/evis/evis.h | 4 +-- src/plugins/georeferencer/qgsgeorefplugin.cpp | 8 ++--- src/plugins/georeferencer/qgsgeorefplugin.h | 2 +- .../georeferencer/qgsgeoreftransform.h | 2 +- .../georeferencer/qgsmapcoordsdialog.h | 2 +- .../georeferencer/qgsrasterchangecoords.cpp | 2 +- .../featuresource/qgsglobefeaturecursor.h | 6 ++-- .../featuresource/qgsglobefeatureutils.h | 16 +++++----- src/plugins/globe/globe_plugin.h | 2 +- src/plugins/globe/qgsglobeplugindialog.h | 2 +- .../globe/qgsglobevectorlayerproperties.h | 4 +-- .../offline_editing_plugin.cpp | 8 ++--- .../offline_editing/offline_editing_plugin.h | 2 +- .../spatialquery/qgsreaderfeatures.cpp | 2 +- src/plugins/spatialquery/qgsreaderfeatures.h | 4 +-- src/plugins/spatialquery/qgsspatialquery.h | 4 +-- src/plugins/topology/checkDock.h | 6 ++-- src/plugins/topology/rulesDialog.cpp | 14 ++++----- src/plugins/topology/rulesDialog.h | 2 +- src/plugins/topology/topol.cpp | 6 ++-- src/plugins/topology/topolError.cpp | 2 +- src/plugins/topology/topolError.h | 6 ++-- src/plugins/topology/topolTest.cpp | 18 +++++------ src/plugins/topology/topolTest.h | 4 +-- src/providers/db2/qgsdb2dataitems.h | 2 +- src/providers/db2/qgsdb2geometrycolumns.cpp | 2 +- src/providers/db2/qgsdb2provider.cpp | 6 ++-- src/providers/db2/qgsdb2provider.h | 2 +- src/providers/db2/qgsdb2tablemodel.h | 2 +- src/providers/gpx/gpsdata.cpp | 2 +- src/providers/grass/qgsgrass.h | 2 +- src/providers/grass/qgsgrassgislib.h | 14 ++++----- src/providers/ogr/qgsogrsourceselect.h | 2 +- .../postgres/qgspostgresprovider.cpp | 20 ++++++------- .../spatialite/qgsspatialiteprovider.cpp | 4 +-- .../virtual/qgsembeddedlayerselectdialog.cpp | 16 +++++----- src/providers/virtual/qgsvirtuallayerblob.h | 2 +- .../virtual/qgsvirtuallayerprovider.h | 2 +- .../virtual/qgsvirtuallayerqueryparser.h | 6 ++-- .../virtual/qgsvirtuallayersourceselect.cpp | 20 ++++++------- .../virtual/qgsvirtuallayersourceselect.h | 4 +-- .../virtual/qgsvirtuallayersqlitemodule.cpp | 12 ++++---- .../virtual/qgsvirtuallayersqlitemodule.h | 2 +- 108 files changed, 252 insertions(+), 254 deletions(-) diff --git a/src/analysis/interpolation/NormVecDecorator.h b/src/analysis/interpolation/NormVecDecorator.h index 5d0e9ec85e9..ee592217c0f 100644 --- a/src/analysis/interpolation/NormVecDecorator.h +++ b/src/analysis/interpolation/NormVecDecorator.h @@ -20,8 +20,8 @@ #include "TriDecorator.h" #include "qgis_sip.h" #include "qgis.h" -#include -#include +#include "TriangleInterpolator.h" +#include "MathUtils.h" #include "qgslogger.h" #include "qgis_analysis.h" diff --git a/src/analysis/interpolation/ParametricLine.cc b/src/analysis/interpolation/ParametricLine.cc index ee4bbea6aee..8c8c5929ac2 100644 --- a/src/analysis/interpolation/ParametricLine.cc +++ b/src/analysis/interpolation/ParametricLine.cc @@ -15,7 +15,7 @@ ***************************************************************************/ #include "ParametricLine.h" -#include +#include "qgslogger.h" void ParametricLine::add( ParametricLine *pl ) { diff --git a/src/analysis/interpolation/TriangleInterpolator.h b/src/analysis/interpolation/TriangleInterpolator.h index 25dd93ce6d9..5c811e94f94 100644 --- a/src/analysis/interpolation/TriangleInterpolator.h +++ b/src/analysis/interpolation/TriangleInterpolator.h @@ -17,8 +17,8 @@ #ifndef TINTERPOLATOR_H #define TINTERPOLATOR_H -#include -#include +#include "qgspoint.h" +#include "Vector3D.h" #include "qgis_analysis.h" /** \ingroup analysis diff --git a/src/analysis/interpolation/Triangulation.h b/src/analysis/interpolation/Triangulation.h index 3f9d28d4bf0..39604df98bb 100644 --- a/src/analysis/interpolation/Triangulation.h +++ b/src/analysis/interpolation/Triangulation.h @@ -19,8 +19,8 @@ #include #include "qgis.h" -#include -#include +#include +#include "TriangleInterpolator.h" #include "qgis_analysis.h" class Line3D; diff --git a/src/analysis/network/qgsgraphanalyzer.h b/src/analysis/network/qgsgraphanalyzer.h index 919b008a39d..f6fc773fcaa 100644 --- a/src/analysis/network/qgsgraphanalyzer.h +++ b/src/analysis/network/qgsgraphanalyzer.h @@ -18,7 +18,7 @@ #include -#include +#include "qgis.h" #include "qgis_analysis.h" class QgsGraph; diff --git a/src/analysis/network/qgsgraphbuilder.cpp b/src/analysis/network/qgsgraphbuilder.cpp index 250388fe715..6d3e161e01f 100644 --- a/src/analysis/network/qgsgraphbuilder.cpp +++ b/src/analysis/network/qgsgraphbuilder.cpp @@ -21,8 +21,8 @@ #include "qgsgraphbuilder.h" #include "qgsgraph.h" -#include -#include +#include "qgsfeature.h" +#include "qgsgeometry.h" QgsGraphBuilder::QgsGraphBuilder( const QgsCoordinateReferenceSystem &crs, bool otfEnabled, double topologyTolerance, const QString &ellipsoidID ) : QgsGraphBuilderInterface( crs, otfEnabled, topologyTolerance, ellipsoidID ) diff --git a/src/analysis/network/qgsgraphbuilder.h b/src/analysis/network/qgsgraphbuilder.h index bc0fc85b12b..d9d355d5f93 100644 --- a/src/analysis/network/qgsgraphbuilder.h +++ b/src/analysis/network/qgsgraphbuilder.h @@ -19,8 +19,7 @@ #include "qgsgraphbuilderinterface.h" #include "qgis.h" -#include -#include +#include "qgsspatialindex.h" #include "qgis_analysis.h" class QgsDistanceArea; diff --git a/src/analysis/network/qgsgraphbuilderinterface.h b/src/analysis/network/qgsgraphbuilderinterface.h index 45580da5905..cba6f061719 100644 --- a/src/analysis/network/qgsgraphbuilderinterface.h +++ b/src/analysis/network/qgsgraphbuilderinterface.h @@ -19,9 +19,9 @@ #include #include -#include -#include -#include +#include "qgspoint.h" +#include "qgscoordinatereferencesystem.h" +#include "qgsdistancearea.h" #include "qgis_analysis.h" #ifdef SIP_RUN diff --git a/src/analysis/network/qgsnetworkdistancestrategy.h b/src/analysis/network/qgsnetworkdistancestrategy.h index 3bd6bfad692..b185beb0eef 100644 --- a/src/analysis/network/qgsnetworkdistancestrategy.h +++ b/src/analysis/network/qgsnetworkdistancestrategy.h @@ -16,7 +16,7 @@ #ifndef QGSNETWORKDISTANCESTRATEGY_H #define QGSNETWORKDISTANCESTRATEGY_H -#include +#include "qgsnetworkstrategy.h" #include "qgis_analysis.h" /** \ingroup analysis diff --git a/src/analysis/network/qgsnetworkspeedstrategy.h b/src/analysis/network/qgsnetworkspeedstrategy.h index 65992fd708c..070b4cf8ae7 100644 --- a/src/analysis/network/qgsnetworkspeedstrategy.h +++ b/src/analysis/network/qgsnetworkspeedstrategy.h @@ -16,7 +16,7 @@ #ifndef QGSNETWORKSPEEDSTRATEGY_H #define QGSNETWORKSPEEDSTRATEGY_H -#include +#include "qgsnetworkstrategy.h" #include "qgis_analysis.h" /** \ingroup analysis diff --git a/src/analysis/network/qgsnetworkstrategy.h b/src/analysis/network/qgsnetworkstrategy.h index 099df2dbb9d..0aa58af2e5d 100644 --- a/src/analysis/network/qgsnetworkstrategy.h +++ b/src/analysis/network/qgsnetworkstrategy.h @@ -18,8 +18,8 @@ #include -#include -#include +#include "qgsfeature.h" +#include "qgsfeaturerequest.h" #include "qgis_analysis.h" #ifdef SIP_RUN diff --git a/src/app/composer/qgscompositionwidget.cpp b/src/app/composer/qgscompositionwidget.cpp index 9f80697d323..478a27468d2 100644 --- a/src/app/composer/qgscompositionwidget.cpp +++ b/src/app/composer/qgscompositionwidget.cpp @@ -14,7 +14,7 @@ * * ***************************************************************************/ -#include +#include "qgis.h" #include "qgscompositionwidget.h" #include "qgscomposition.h" #include "qgscomposermap.h" diff --git a/src/app/dwg/qgsdwgimporter.h b/src/app/dwg/qgsdwgimporter.h index c3442b6b250..b2edde1f2a5 100644 --- a/src/app/dwg/qgsdwgimporter.h +++ b/src/app/dwg/qgsdwgimporter.h @@ -20,7 +20,7 @@ #include #include -#include +#include "qgsabstractgeometry.h" class QgsCompoundCurve; class QgsQgsCoordinateReferenceSystem; diff --git a/src/app/gps/qgsgpsinformationwidget.h b/src/app/gps/qgsgpsinformationwidget.h index c88c4e48deb..cca2b9af206 100644 --- a/src/app/gps/qgsgpsinformationwidget.h +++ b/src/app/gps/qgsgpsinformationwidget.h @@ -19,9 +19,9 @@ #include "ui_qgsgpsinformationwidgetbase.h" -#include -#include -#include +#include "qgsmapcanvas.h" +#include "qgsgpsmarker.h" +#include "qgsmaptoolcapture.h" #include #ifdef WITH_QWTPOLAR #include diff --git a/src/app/pluginmanager/qgsapppluginmanagerinterface.cpp b/src/app/pluginmanager/qgsapppluginmanagerinterface.cpp index f801a60533a..a7c68601b08 100644 --- a/src/app/pluginmanager/qgsapppluginmanagerinterface.cpp +++ b/src/app/pluginmanager/qgsapppluginmanagerinterface.cpp @@ -16,7 +16,7 @@ #include "qgsapppluginmanagerinterface.h" #include "qgspluginmanager.h" -#include +#include "qgslogger.h" QgsAppPluginManagerInterface::QgsAppPluginManagerInterface( QgsPluginManager *pluginManager ) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 654d5b710a2..0cc8b8945c3 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -71,11 +71,11 @@ #include #include -#include -#include -#include -#include -#include +#include "qgssettings.h" +#include "qgsnetworkaccessmanager.h" +#include "qgsapplication.h" +#include "qgscomposition.h" +#include "qgslayerstylingwidget.h" #include "qgstaskmanager.h" #include "qgsziputils.h" diff --git a/src/app/qgsdecorationgrid.h b/src/app/qgsdecorationgrid.h index 477ffcf008c..977eed60229 100644 --- a/src/app/qgsdecorationgrid.h +++ b/src/app/qgsdecorationgrid.h @@ -19,7 +19,7 @@ #define QGSDECORATIONGRID_H #include "qgsdecorationitem.h" -#include +#include "qgis.h" class QPainter; class QgsLineSymbol; diff --git a/src/app/qgsdiagramproperties.h b/src/app/qgsdiagramproperties.h index 05900562a9a..a1b479894f9 100644 --- a/src/app/qgsdiagramproperties.h +++ b/src/app/qgsdiagramproperties.h @@ -20,7 +20,7 @@ #include #include "qgsdiagramrenderer.h" -#include +#include "ui_qgsdiagrampropertiesbase.h" #include #include "qgis_app.h" diff --git a/src/app/qgslabelengineconfigdialog.cpp b/src/app/qgslabelengineconfigdialog.cpp index aa769153fda..6c8eeae359a 100644 --- a/src/app/qgslabelengineconfigdialog.cpp +++ b/src/app/qgslabelengineconfigdialog.cpp @@ -16,7 +16,7 @@ #include "qgslabelingenginesettings.h" #include "qgsproject.h" -#include +#include "pal/pal.h" #include diff --git a/src/app/qgslabelingwidget.h b/src/app/qgslabelingwidget.h index 3ee0248014a..b530026b503 100644 --- a/src/app/qgslabelingwidget.h +++ b/src/app/qgslabelingwidget.h @@ -17,8 +17,8 @@ #include -#include -#include +#include "ui_qgslabelingwidget.h" +#include "qgspallabeling.h" #include "qgsvectorlayerlabeling.h" #include "qgsmaplayerconfigwidget.h" diff --git a/src/app/qgsmapcanvasdockwidget.h b/src/app/qgsmapcanvasdockwidget.h index f7a29f70a50..af39badad6f 100644 --- a/src/app/qgsmapcanvasdockwidget.h +++ b/src/app/qgsmapcanvasdockwidget.h @@ -15,7 +15,7 @@ #ifndef QGSMAPCANVASDOCKWIDGET_H #define QGSMAPCANVASDOCKWIDGET_H -#include +#include "ui_qgsmapcanvasdockwidgetbase.h" #include "qgsdockwidget.h" #include "qgspointxy.h" diff --git a/src/app/qgsmaptoolpinlabels.cpp b/src/app/qgsmaptoolpinlabels.cpp index 7f0657ffe84..733c40bb931 100644 --- a/src/app/qgsmaptoolpinlabels.cpp +++ b/src/app/qgsmaptoolpinlabels.cpp @@ -25,7 +25,7 @@ #include "qgsmaptoolselectutils.h" #include "qgsrubberband.h" -#include +#include "qgslogger.h" #include QgsMapToolPinLabels::QgsMapToolPinLabels( QgsMapCanvas *canvas ) diff --git a/src/app/qgsmaptoolshowhidelabels.cpp b/src/app/qgsmaptoolshowhidelabels.cpp index ff23d07f9a6..5a9f47067fc 100644 --- a/src/app/qgsmaptoolshowhidelabels.cpp +++ b/src/app/qgsmaptoolshowhidelabels.cpp @@ -25,7 +25,7 @@ #include "qgsmaptoolselectutils.h" #include "qgsrubberband.h" -#include +#include "qgslogger.h" #include diff --git a/src/app/qgsoptions.h b/src/app/qgsoptions.h index c7939e56e89..3001b74ccfa 100644 --- a/src/app/qgsoptions.h +++ b/src/app/qgsoptions.h @@ -24,7 +24,7 @@ #include "qgisapp.h" #include "qgshelp.h" -#include +#include "qgscoordinatereferencesystem.h" #include #include "qgis_app.h" diff --git a/src/app/qgsrulebasedlabelingwidget.h b/src/app/qgsrulebasedlabelingwidget.h index d7ad40c1e87..12f7c44dc43 100644 --- a/src/app/qgsrulebasedlabelingwidget.h +++ b/src/app/qgsrulebasedlabelingwidget.h @@ -19,7 +19,7 @@ #include "qgspanelwidget.h" -#include +#include "ui_qgsrulebasedlabelingwidget.h" #include "qgsrulebasedlabeling.h" #include "qgis_app.h" diff --git a/src/core/geometry/qgsrectangle.cpp b/src/core/geometry/qgsrectangle.cpp index 75c08eec6a7..1c35879d65f 100644 --- a/src/core/geometry/qgsrectangle.cpp +++ b/src/core/geometry/qgsrectangle.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include "qgspointxy.h" #include "qgsrectangle.h" diff --git a/src/core/pal/util.cpp b/src/core/pal/util.cpp index a00be763a79..435e0c866b9 100644 --- a/src/core/pal/util.cpp +++ b/src/core/pal/util.cpp @@ -34,7 +34,7 @@ #include "feature.h" #include "geomfunction.h" -#include +#include "qgslogger.h" #include #ifndef M_PI diff --git a/src/core/qgsactionmanager.cpp b/src/core/qgsactionmanager.cpp index 558dd8d442a..116bb3b5dd1 100644 --- a/src/core/qgsactionmanager.cpp +++ b/src/core/qgsactionmanager.cpp @@ -27,7 +27,7 @@ #include "qgsrunprocess.h" #include "qgsvectorlayer.h" #include "qgsproject.h" -#include +#include "qgslogger.h" #include "qgsexpression.h" #include diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index 694e7b0a85a..b51e0469e44 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -20,8 +20,8 @@ #include #include -#include -#include +#include "qgis.h" +#include "qgsconfig.h" class QgsActionScopeRegistry; class QgsRuntimeProfiler; diff --git a/src/core/qgslabelsearchtree.h b/src/core/qgslabelsearchtree.h index fc8e994e5da..f9577a95c05 100644 --- a/src/core/qgslabelsearchtree.h +++ b/src/core/qgslabelsearchtree.h @@ -23,8 +23,8 @@ #include "qgis_sip.h" #include #include -#include -#include +#include "pointset.h" +#include "labelposition.h" #include "qgspallabeling.h" #include "rtree.hpp" diff --git a/src/core/qgslocalec.cpp b/src/core/qgslocalec.cpp index d0f80bccb86..c6cb1b08119 100644 --- a/src/core/qgslocalec.cpp +++ b/src/core/qgslocalec.cpp @@ -15,7 +15,7 @@ * * ***************************************************************************/ -#include +#include "qgslocalec.h" #include #include diff --git a/src/core/qgsmessagelog.cpp b/src/core/qgsmessagelog.cpp index 00c7288e953..093c53efed3 100644 --- a/src/core/qgsmessagelog.cpp +++ b/src/core/qgsmessagelog.cpp @@ -15,7 +15,7 @@ #include "qgsmessagelog.h" #include "qgsapplication.h" -#include +#include "qgslogger.h" #include #include #include diff --git a/src/core/qgsnetworkaccessmanager.cpp b/src/core/qgsnetworkaccessmanager.cpp index 7d0d4f909de..9ae15e44b3a 100644 --- a/src/core/qgsnetworkaccessmanager.cpp +++ b/src/core/qgsnetworkaccessmanager.cpp @@ -19,12 +19,12 @@ * * ***************************************************************************/ -#include +#include "qgsnetworkaccessmanager.h" -#include -#include -#include -#include +#include "qgsapplication.h" +#include "qgsmessagelog.h" +#include "qgslogger.h" +#include "qgis.h" #include "qgssettings.h" #include "qgsnetworkdiskcache.h" #include "qgsauthmanager.h" diff --git a/src/core/qgsofflineediting.h b/src/core/qgsofflineediting.h index 2c57a6fe4c1..1ac346c3ca3 100644 --- a/src/core/qgsofflineediting.h +++ b/src/core/qgsofflineediting.h @@ -20,8 +20,8 @@ #define QGS_OFFLINE_EDITING_H #include "qgis_core.h" -#include -#include +#include "qgsfeature.h" +#include "qgsvectorlayer.h" #include #include diff --git a/src/core/qgspalgeometry.h b/src/core/qgspalgeometry.h index 1ea5edab5b2..a7c8e7d962b 100644 --- a/src/core/qgspalgeometry.h +++ b/src/core/qgspalgeometry.h @@ -17,7 +17,7 @@ #include "qgsgeometry.h" #include "qgspallabeling.h" -#include +#include "pal/feature.h" #include "qgslabelingengine.h" diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index ac7aef85b5f..2edde7d50fb 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -22,12 +22,12 @@ #include -#include -#include -#include -#include -#include -#include +#include "pal/pal.h" +#include "pal/feature.h" +#include "pal/layer.h" +#include "pal/palexception.h" +#include "pal/problem.h" +#include "pal/labelposition.h" #include @@ -46,15 +46,15 @@ #include "qgslabelingengine.h" #include "qgsvectorlayerlabeling.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qgslogger.h" +#include "qgsvectorlayer.h" +#include "qgsvectordataprovider.h" +#include "qgsvectorlayerdiagramprovider.h" +#include "qgsvectorlayerlabelprovider.h" +#include "qgsgeometry.h" +#include "qgsmarkersymbollayer.h" +#include "qgspainting.h" +#include "qgsproject.h" #include "qgsproperty.h" #include "qgssymbollayerutils.h" #include "qgsmaptopixelgeometrysimplifier.h" diff --git a/src/core/qgsrenderchecker.h b/src/core/qgsrenderchecker.h index 95a977fb518..35cfa6b661b 100644 --- a/src/core/qgsrenderchecker.h +++ b/src/core/qgsrenderchecker.h @@ -17,15 +17,15 @@ #define QGSRENDERCHECKER_H #include "qgis_core.h" -#include +#include "qgis.h" #include #include #include #include -#include -#include -#include +#include "qgslogger.h" +#include "qgsmapsettings.h" +#include "qgsdartmeasurement.h" class QImage; diff --git a/src/core/qgsscalecalculator.h b/src/core/qgsscalecalculator.h index b8fdb6990d2..3896fa35efe 100644 --- a/src/core/qgsscalecalculator.h +++ b/src/core/qgsscalecalculator.h @@ -20,7 +20,7 @@ #define QGSSCALECALCULATOR_H #include "qgis_core.h" -#include +#include "qgis.h" #include "qgsunittypes.h" class QString; diff --git a/src/core/qgstextlabelfeature.cpp b/src/core/qgstextlabelfeature.cpp index d7688ff0f2d..30762f25fdd 100644 --- a/src/core/qgstextlabelfeature.cpp +++ b/src/core/qgstextlabelfeature.cpp @@ -18,7 +18,7 @@ #include "qgsgeometry.h" #include "qgspallabeling.h" #include "qgsmaptopixel.h" -#include +#include "pal/feature.h" QgsTextLabelFeature::QgsTextLabelFeature( QgsFeatureId id, GEOSGeometry *geometry, QSizeF size ) diff --git a/src/gui/attributetable/qgsfeaturelistmodel.h b/src/gui/attributetable/qgsfeaturelistmodel.h index 2da2e42f0e1..2c23dd2ea8b 100644 --- a/src/gui/attributetable/qgsfeaturelistmodel.h +++ b/src/gui/attributetable/qgsfeaturelistmodel.h @@ -15,7 +15,7 @@ #ifndef QGSATTRIBUTEEDITORMODEL_H #define QGSATTRIBUTEEDITORMODEL_H -#include +#include "qgsexpression.h" #include "qgis.h" #include diff --git a/src/gui/attributetable/qgsorganizetablecolumnsdialog.cpp b/src/gui/attributetable/qgsorganizetablecolumnsdialog.cpp index ff6d6216129..b2e1af4f566 100644 --- a/src/gui/attributetable/qgsorganizetablecolumnsdialog.cpp +++ b/src/gui/attributetable/qgsorganizetablecolumnsdialog.cpp @@ -22,10 +22,10 @@ #include "qgsattributetableview.h" #include "qgsdockwidget.h" -#include -#include -#include -#include +#include "qgsapplication.h" +#include "qgsvectordataprovider.h" +#include "qgsvectorlayer.h" +#include "qgsexpression.h" #include "qgssearchquerybuilder.h" #include "qgslogger.h" diff --git a/src/gui/effects/qgseffectstackpropertieswidget.h b/src/gui/effects/qgseffectstackpropertieswidget.h index 009b441f944..a4b037cfa78 100644 --- a/src/gui/effects/qgseffectstackpropertieswidget.h +++ b/src/gui/effects/qgseffectstackpropertieswidget.h @@ -21,7 +21,7 @@ #include "qgis.h" #include #include -#include +#include "qgspanelwidget.h" #include "ui_qgseffectstackpropertieswidgetbase.h" #include "qgis_gui.h" diff --git a/src/gui/ogr/qgsvectorlayersaveasdialog.h b/src/gui/ogr/qgsvectorlayersaveasdialog.h index a07b32bc87c..206433db91b 100644 --- a/src/gui/ogr/qgsvectorlayersaveasdialog.h +++ b/src/gui/ogr/qgsvectorlayersaveasdialog.h @@ -18,7 +18,7 @@ #ifndef QGSVECTORLAYERSAVEASDIALOG_H #define QGSVECTORLAYERSAVEASDIALOG_H -#include +#include "ui_qgsvectorlayersaveasdialogbase.h" #include #include "qgshelp.h" #include "qgsfields.h" diff --git a/src/gui/qgsadvanceddigitizingdockwidget.h b/src/gui/qgsadvanceddigitizingdockwidget.h index b61a0b9bd0b..a88f4c6d06e 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.h +++ b/src/gui/qgsadvanceddigitizingdockwidget.h @@ -20,7 +20,7 @@ #include "qgsmapmouseevent.h" #include "qgsmessagebaritem.h" -#include +#include "ui_qgsadvanceddigitizingdockwidgetbase.h" #include "qgis_gui.h" #include "qgis.h" #include diff --git a/src/gui/qgsattributeeditorcontext.h b/src/gui/qgsattributeeditorcontext.h index 8a3d31e8f3d..295e274b7dd 100644 --- a/src/gui/qgsattributeeditorcontext.h +++ b/src/gui/qgsattributeeditorcontext.h @@ -19,9 +19,9 @@ #include #include -#include -#include -#include +#include "qgsdistancearea.h" +#include "qgsvectorlayer.h" +#include "qgsvectorlayertools.h" #include "qgis_gui.h" diff --git a/src/gui/qgsbrowserdockwidget.h b/src/gui/qgsbrowserdockwidget.h index 418cef1e635..83304179a00 100644 --- a/src/gui/qgsbrowserdockwidget.h +++ b/src/gui/qgsbrowserdockwidget.h @@ -15,10 +15,10 @@ #ifndef QGSBROWSERDOCKWIDGET_H #define QGSBROWSERDOCKWIDGET_H -#include -#include -#include -#include +#include "ui_qgsbrowserdockwidgetbase.h" +#include "ui_qgsbrowserlayerpropertiesbase.h" +#include "ui_qgsbrowserdirectorypropertiesbase.h" +#include "ui_qgsbrowserpropertiesdialogbase.h" #include "qgsdataitem.h" #include "qgsbrowsertreeview.h" diff --git a/src/gui/qgsbrowserdockwidget_p.h b/src/gui/qgsbrowserdockwidget_p.h index 780a2febe9b..b500b3779b7 100644 --- a/src/gui/qgsbrowserdockwidget_p.h +++ b/src/gui/qgsbrowserdockwidget_p.h @@ -33,10 +33,10 @@ // -#include -#include -#include -#include +#include "ui_qgsbrowserdockwidgetbase.h" +#include "ui_qgsbrowserlayerpropertiesbase.h" +#include "ui_qgsbrowserdirectorypropertiesbase.h" +#include "ui_qgsbrowserpropertiesdialogbase.h" #include "qgsdataitem.h" #include "qgsbrowsertreeview.h" diff --git a/src/gui/qgscredentialdialog.h b/src/gui/qgscredentialdialog.h index 9e384d1fd81..678d5d97fd0 100644 --- a/src/gui/qgscredentialdialog.h +++ b/src/gui/qgscredentialdialog.h @@ -17,7 +17,7 @@ #ifndef QGSCREDENTIALDIALOG_H #define QGSCREDENTIALDIALOG_H -#include +#include "ui_qgscredentialdialog.h" #include "qgsguiutils.h" #include "qgscredentials.h" diff --git a/src/gui/qgsdetaileditemwidget.h b/src/gui/qgsdetaileditemwidget.h index fcb308ccaef..83042587984 100644 --- a/src/gui/qgsdetaileditemwidget.h +++ b/src/gui/qgsdetaileditemwidget.h @@ -17,9 +17,9 @@ #ifndef QGSDETAILEDITEMWIDGET_H #define QGSDETAILEDITEMWIDGET_H -#include +#include "ui_qgsdetaileditemwidgetbase.h" #include "qgis.h" -#include +#include "qgsdetaileditemdata.h" #include "qgis_gui.h" /** \ingroup gui diff --git a/src/gui/qgslegendfilterbutton.cpp b/src/gui/qgslegendfilterbutton.cpp index ab4ecdee057..8285149578f 100644 --- a/src/gui/qgslegendfilterbutton.cpp +++ b/src/gui/qgslegendfilterbutton.cpp @@ -18,8 +18,8 @@ #include #include -#include -#include +#include "qgsapplication.h" +#include "qgsexpressionbuilderdialog.h" QgsLegendFilterButton::QgsLegendFilterButton( QWidget *parent ) : QToolButton( parent ) diff --git a/src/gui/qgsmapcanvasmap.h b/src/gui/qgsmapcanvasmap.h index f9cd8df37b3..eab8bfee7d1 100644 --- a/src/gui/qgsmapcanvasmap.h +++ b/src/gui/qgsmapcanvasmap.h @@ -16,7 +16,7 @@ #ifndef QGSMAPCANVASMAP_H #define QGSMAPCANVASMAP_H -#include +#include "qgsmapcanvasitem.h" class QgsMapSettings; class QgsMapCanvas; diff --git a/src/gui/qgsmessagebaritem.h b/src/gui/qgsmessagebaritem.h index 88cd73112be..c755ef7e78b 100644 --- a/src/gui/qgsmessagebaritem.h +++ b/src/gui/qgsmessagebaritem.h @@ -17,9 +17,9 @@ #ifndef qgsmessagebaritem_H #define qgsmessagebaritem_H -#include +#include "qgsmessagebaritem.h" #include "qgis.h" -#include +#include "qgsmessagebar.h" #include #include diff --git a/src/gui/qgsmessagelogviewer.h b/src/gui/qgsmessagelogviewer.h index 2fc390a2780..87bfb774c06 100644 --- a/src/gui/qgsmessagelogviewer.h +++ b/src/gui/qgsmessagelogviewer.h @@ -17,7 +17,7 @@ #ifndef QGSMESSAGELOGVIEWER_H #define QGSMESSAGELOGVIEWER_H -#include +#include "ui_qgsmessagelogviewer.h" #include "qgsguiutils.h" #include "qgsmessagelog.h" diff --git a/src/gui/qgsnewhttpconnection.h b/src/gui/qgsnewhttpconnection.h index 4e74fcf8beb..f458d570d5e 100644 --- a/src/gui/qgsnewhttpconnection.h +++ b/src/gui/qgsnewhttpconnection.h @@ -18,7 +18,7 @@ #ifndef QGSNEWHTTPCONNECTION_H #define QGSNEWHTTPCONNECTION_H -#include +#include "qgis_sip.h" #include "ui_qgsnewhttpconnectionbase.h" #include "qgsguiutils.h" #include "qgis_gui.h" diff --git a/src/gui/qgsprojectionselectiondialog.h b/src/gui/qgsprojectionselectiondialog.h index 281acdbac53..d8fd3f4f769 100644 --- a/src/gui/qgsprojectionselectiondialog.h +++ b/src/gui/qgsprojectionselectiondialog.h @@ -17,7 +17,7 @@ ***************************************************************************/ #ifndef QGSGENERICPROJECTIONSELECTOR_H #define QGSGENERICPROJECTIONSELECTOR_H -#include +#include "ui_qgsgenericprojectionselectorbase.h" #include "qgis.h" #include "qgsguiutils.h" diff --git a/src/gui/qgsprojectionselectiontreewidget.h b/src/gui/qgsprojectionselectiontreewidget.h index ae383ed6bf8..5e521cad933 100644 --- a/src/gui/qgsprojectionselectiontreewidget.h +++ b/src/gui/qgsprojectionselectiontreewidget.h @@ -11,7 +11,7 @@ #ifndef QGSCRSSELECTOR_H #define QGSCRSSELECTOR_H -#include +#include "ui_qgsprojectionselectorbase.h" #include #include diff --git a/src/gui/qgssqlcomposerdialog.h b/src/gui/qgssqlcomposerdialog.h index 068f5476aa7..c8d03e04fb8 100644 --- a/src/gui/qgssqlcomposerdialog.h +++ b/src/gui/qgssqlcomposerdialog.h @@ -20,7 +20,7 @@ email : even.rouault at spatialys.com #define QGSSQLCOMPOSERDIALOG_H #include "ui_qgssqlcomposerdialogbase.h" -#include +#include "qgis.h" #include "qgsguiutils.h" #include diff --git a/src/gui/qgssublayersdialog.h b/src/gui/qgssublayersdialog.h index be4085ff418..645d5557947 100644 --- a/src/gui/qgssublayersdialog.h +++ b/src/gui/qgssublayersdialog.h @@ -18,8 +18,8 @@ #include #include -#include -#include +#include "ui_qgssublayersdialogbase.h" +#include "qgis_sip.h" #include "qgis_gui.h" /** \ingroup gui diff --git a/src/gui/qgstextformatwidget.h b/src/gui/qgstextformatwidget.h index 22a45c14540..5da14ded964 100644 --- a/src/gui/qgstextformatwidget.h +++ b/src/gui/qgstextformatwidget.h @@ -17,7 +17,7 @@ #ifndef QGSTEXTFORMATWIDGET_H #define QGSTEXTFORMATWIDGET_H -#include +#include "ui_qgstextformatwidgetbase.h" #include "qgis.h" #include "qgstextrenderer.h" #include "qgsstringutils.h" diff --git a/src/gui/symbology/qgsdatadefinedsizelegendwidget.h b/src/gui/symbology/qgsdatadefinedsizelegendwidget.h index 2de823116f9..5680a4daa93 100644 --- a/src/gui/symbology/qgsdatadefinedsizelegendwidget.h +++ b/src/gui/symbology/qgsdatadefinedsizelegendwidget.h @@ -20,7 +20,7 @@ #include "qgis_gui.h" #include -#include +#include "ui_qgsdatadefinedsizelegendwidget.h" #include "qgspanelwidget.h" #include "qgsproperty.h" diff --git a/src/plugins/coordinate_capture/coordinatecapture.cpp b/src/plugins/coordinate_capture/coordinatecapture.cpp index 1186e6ea874..102fd13645e 100644 --- a/src/plugins/coordinate_capture/coordinatecapture.cpp +++ b/src/plugins/coordinate_capture/coordinatecapture.cpp @@ -19,15 +19,15 @@ // QGIS Specific includes // -#include +#include "qgisinterface.h" #include "qgsguiutils.h" #include "qgsapplication.h" -#include -#include -#include -#include -#include -#include +#include "qgspoint.h" +#include "qgsmapcanvas.h" +#include "qgis.h" +#include "qgscoordinatereferencesystem.h" +#include "qgscoordinatetransform.h" +#include "qgsprojectionselectiondialog.h" #include "qgsdockwidget.h" #include "coordinatecapture.h" diff --git a/src/plugins/coordinate_capture/coordinatecapture.h b/src/plugins/coordinate_capture/coordinatecapture.h index cf06514efac..6a4980afbc8 100644 --- a/src/plugins/coordinate_capture/coordinatecapture.h +++ b/src/plugins/coordinate_capture/coordinatecapture.h @@ -42,8 +42,8 @@ //QGIS includes #include "../qgisplugin.h" #include "coordinatecapturemaptool.h" -#include -#include +#include "qgscoordinatereferencesystem.h" +#include "qgscoordinatetransform.h" //forward declarations class QAction; diff --git a/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.h b/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.h index 7df30aa5dfe..3df7404f790 100644 --- a/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.h +++ b/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.h @@ -27,7 +27,7 @@ #ifndef eVisDatabaseConnectionGUI_H #define eVisDatabaseConnectionGUI_H -#include +#include "ui_evisdatabaseconnectionguibase.h" #include "evisdatabaseconnection.h" #include "evisdatabaselayerfieldselectiongui.h" #include "evisquerydefinition.h" diff --git a/src/plugins/evis/databaseconnection/evisdatabaselayerfieldselectiongui.h b/src/plugins/evis/databaseconnection/evisdatabaselayerfieldselectiongui.h index f97c79ebc81..cadaf46896a 100644 --- a/src/plugins/evis/databaseconnection/evisdatabaselayerfieldselectiongui.h +++ b/src/plugins/evis/databaseconnection/evisdatabaselayerfieldselectiongui.h @@ -28,7 +28,7 @@ #define eVisDatabaseLayerFieldSelectionGui_H #include -#include +#include "ui_evisdatabaselayerfieldselectionguibase.h" /** * \class eVisDatabaseLayerFieldSelectionGui diff --git a/src/plugins/evis/evis.cpp b/src/plugins/evis/evis.cpp index d5f7167bd40..528bbf8c7cc 100644 --- a/src/plugins/evis/evis.cpp +++ b/src/plugins/evis/evis.cpp @@ -50,10 +50,10 @@ // // QGIS Specific includes // -#include -#include -#include -#include +#include "qgsapplication.h" +#include "qgsrasterlayer.h" +#include "qgisinterface.h" +#include "qgsmaplayer.h" #include "qgsguiutils.h" //the gui subclass diff --git a/src/plugins/evis/evis.h b/src/plugins/evis/evis.h index d79e68b57f3..ffe91afff2e 100644 --- a/src/plugins/evis/evis.h +++ b/src/plugins/evis/evis.h @@ -70,8 +70,8 @@ #include -#include -#include +#include "qgisplugin.h" +#include "qgisinterface.h" //forward declarations class QAction; diff --git a/src/plugins/georeferencer/qgsgeorefplugin.cpp b/src/plugins/georeferencer/qgsgeorefplugin.cpp index d7aee3873e3..9a646ca5bc4 100644 --- a/src/plugins/georeferencer/qgsgeorefplugin.cpp +++ b/src/plugins/georeferencer/qgsgeorefplugin.cpp @@ -45,10 +45,10 @@ // Required qgis includes // -#include -#include -#include -#include +#include "qgisinterface.h" +#include "qgsapplication.h" +#include "qgsmaplayer.h" +#include "qgsrasterlayer.h" #include "qgsgeorefplugin.h" #include diff --git a/src/plugins/georeferencer/qgsgeorefplugin.h b/src/plugins/georeferencer/qgsgeorefplugin.h index e051915288b..d432e1c1cbd 100644 --- a/src/plugins/georeferencer/qgsgeorefplugin.h +++ b/src/plugins/georeferencer/qgsgeorefplugin.h @@ -43,7 +43,7 @@ // //QGIS Includes // -#include +#include "qgisplugin.h" class QgisInterface; class QgsGeorefPluginGui; diff --git a/src/plugins/georeferencer/qgsgeoreftransform.h b/src/plugins/georeferencer/qgsgeoreftransform.h index e979c5f2249..2848887167b 100644 --- a/src/plugins/georeferencer/qgsgeoreftransform.h +++ b/src/plugins/georeferencer/qgsgeoreftransform.h @@ -19,7 +19,7 @@ #include // just needed for GDALTransformerFunc, forward? -#include +#include "qgspoint.h" #include #include diff --git a/src/plugins/georeferencer/qgsmapcoordsdialog.h b/src/plugins/georeferencer/qgsmapcoordsdialog.h index 3c09acd6e73..d99b6b9ee33 100644 --- a/src/plugins/georeferencer/qgsmapcoordsdialog.h +++ b/src/plugins/georeferencer/qgsmapcoordsdialog.h @@ -21,7 +21,7 @@ #include "qgsvertexmarker.h" #include "qgsmapcanvas.h" -#include +#include "ui_qgsmapcoordsdialogbase.h" class QPushButton; diff --git a/src/plugins/georeferencer/qgsrasterchangecoords.cpp b/src/plugins/georeferencer/qgsrasterchangecoords.cpp index 30d371768e2..6a7fd761ee4 100644 --- a/src/plugins/georeferencer/qgsrasterchangecoords.cpp +++ b/src/plugins/georeferencer/qgsrasterchangecoords.cpp @@ -15,7 +15,7 @@ #include "qgsrasterchangecoords.h" -#include +#include "qgspoint.h" #include #include diff --git a/src/plugins/globe/featuresource/qgsglobefeaturecursor.h b/src/plugins/globe/featuresource/qgsglobefeaturecursor.h index 5bcc92d71d0..4897142a653 100644 --- a/src/plugins/globe/featuresource/qgsglobefeaturecursor.h +++ b/src/plugins/globe/featuresource/qgsglobefeaturecursor.h @@ -17,9 +17,9 @@ #define QGSGLOBEFEATURECURSOR_H #include -#include -#include -#include +#include "qgsfeature.h" +#include "qgsfeatureiterator.h" +#include "qgsvectorlayer.h" #include "qgsglobefeatureutils.h" diff --git a/src/plugins/globe/featuresource/qgsglobefeatureutils.h b/src/plugins/globe/featuresource/qgsglobefeatureutils.h index 85df4d972ec..501a4354f9a 100644 --- a/src/plugins/globe/featuresource/qgsglobefeatureutils.h +++ b/src/plugins/globe/featuresource/qgsglobefeatureutils.h @@ -19,14 +19,14 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qgsfield.h" +#include "qgsgeometry.h" +#include "qgsmultipoint.h" +#include "qgsmultilinestring.h" +#include "qgsmultipolygon.h" +#include "qgspolygon.h" +#include "qgslinestring.h" +#include "qgsvectorlayer.h" class QgsGlobeFeatureUtils { diff --git a/src/plugins/globe/globe_plugin.h b/src/plugins/globe/globe_plugin.h index 79dc6b7186a..256d2102816 100644 --- a/src/plugins/globe/globe_plugin.h +++ b/src/plugins/globe/globe_plugin.h @@ -19,7 +19,7 @@ #ifndef QGS_GLOBE_PLUGIN_H #define QGS_GLOBE_PLUGIN_H -#include +#include "qgisplugin.h" #include #include #include diff --git a/src/plugins/globe/qgsglobeplugindialog.h b/src/plugins/globe/qgsglobeplugindialog.h index 4ba949d243d..ceab3f675f2 100644 --- a/src/plugins/globe/qgsglobeplugindialog.h +++ b/src/plugins/globe/qgsglobeplugindialog.h @@ -15,7 +15,7 @@ #ifndef QGSGLOBEPLUGINDIALOG_H #define QGSGLOBEPLUGINDIALOG_H -#include +#include "ui_qgsglobeplugindialog.h" #include class GlobePlugin; diff --git a/src/plugins/globe/qgsglobevectorlayerproperties.h b/src/plugins/globe/qgsglobevectorlayerproperties.h index 10c59d2bc32..753cecdaa91 100644 --- a/src/plugins/globe/qgsglobevectorlayerproperties.h +++ b/src/plugins/globe/qgsglobevectorlayerproperties.h @@ -19,8 +19,8 @@ #include #include "ui_qgsglobevectorlayerpropertiespage.h" -#include -#include +#include "qgsmaplayerconfigwidget.h" +#include "qgsmaplayerconfigwidgetfactory.h" #include class QgsGlobeVectorLayerConfig; diff --git a/src/plugins/offline_editing/offline_editing_plugin.cpp b/src/plugins/offline_editing/offline_editing_plugin.cpp index 671dd833f95..ca6b68b9206 100644 --- a/src/plugins/offline_editing/offline_editing_plugin.cpp +++ b/src/plugins/offline_editing/offline_editing_plugin.cpp @@ -20,11 +20,11 @@ #include "offline_editing_plugin_gui.h" #include "offline_editing_progress_dialog.h" -#include +#include "qgisinterface.h" #include "qgsguiutils.h" -#include -#include -#include +#include "qgsproject.h" +#include "qgsmessagebar.h" +#include "qgsmapcanvas.h" #include diff --git a/src/plugins/offline_editing/offline_editing_plugin.h b/src/plugins/offline_editing/offline_editing_plugin.h index 2e76e009ebf..b68f1ccc9b5 100644 --- a/src/plugins/offline_editing/offline_editing_plugin.h +++ b/src/plugins/offline_editing/offline_editing_plugin.h @@ -20,7 +20,7 @@ #define QGS_OFFLINE_EDITING_PLUGIN_H #include "../qgisplugin.h" -#include +#include "qgsofflineediting.h" #include class QAction; diff --git a/src/plugins/spatialquery/qgsreaderfeatures.cpp b/src/plugins/spatialquery/qgsreaderfeatures.cpp index 4dcaea72e1e..a4f542469e2 100644 --- a/src/plugins/spatialquery/qgsreaderfeatures.cpp +++ b/src/plugins/spatialquery/qgsreaderfeatures.cpp @@ -16,7 +16,7 @@ * * ***************************************************************************/ -#include +#include "qgsvectordataprovider.h" #include "qgsreaderfeatures.h" diff --git a/src/plugins/spatialquery/qgsreaderfeatures.h b/src/plugins/spatialquery/qgsreaderfeatures.h index 132190c1f94..5c0b8cce5b3 100644 --- a/src/plugins/spatialquery/qgsreaderfeatures.h +++ b/src/plugins/spatialquery/qgsreaderfeatures.h @@ -18,8 +18,8 @@ #ifndef READERFEATURES_H #define READERFEATURES_H -#include -#include +#include "qgsvectorlayer.h" +#include "qgsfeature.h" #include "qgsfeatureiterator.h" /** diff --git a/src/plugins/spatialquery/qgsspatialquery.h b/src/plugins/spatialquery/qgsspatialquery.h index 71deb33e0d6..a2be7e06e46 100644 --- a/src/plugins/spatialquery/qgsspatialquery.h +++ b/src/plugins/spatialquery/qgsspatialquery.h @@ -18,8 +18,8 @@ #ifndef SPATIALQUERY_H #define SPATIALQUERY_H -#include -#include +#include "qgsvectorlayer.h" +#include "qgsspatialindex.h" #include "qgsmngprogressbar.h" #include "qgsreaderfeatures.h" diff --git a/src/plugins/topology/checkDock.h b/src/plugins/topology/checkDock.h index 84022c202d4..3acc9c5d34a 100644 --- a/src/plugins/topology/checkDock.h +++ b/src/plugins/topology/checkDock.h @@ -20,9 +20,9 @@ #include "qgsdockwidget.h" -#include -#include -//#include +#include "qgsvectorlayer.h" +#include "qgsgeometry.h" +//#include "qgsvertexmarker.h" #include "qgsspatialindex.h" #include "ui_checkDock.h" diff --git a/src/plugins/topology/rulesDialog.cpp b/src/plugins/topology/rulesDialog.cpp index 044e3220ead..68223a09329 100644 --- a/src/plugins/topology/rulesDialog.cpp +++ b/src/plugins/topology/rulesDialog.cpp @@ -18,13 +18,13 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include "qgsvectordataprovider.h" +#include "qgsvectorlayer.h" +#include "qgsmaplayer.h" +#include "qgsproviderregistry.h" +#include "qgslogger.h" +#include "qgisinterface.h" +#include "qgsproject.h" #include "qgsapplication.h" #include "rulesDialog.h" #include "topolTest.h" diff --git a/src/plugins/topology/rulesDialog.h b/src/plugins/topology/rulesDialog.h index 4178b7d5d80..a8aaed49d73 100644 --- a/src/plugins/topology/rulesDialog.h +++ b/src/plugins/topology/rulesDialog.h @@ -20,7 +20,7 @@ #include -#include +#include "qgsvectorlayer.h" #include "ui_rulesDialog.h" #include "topolTest.h" diff --git a/src/plugins/topology/topol.cpp b/src/plugins/topology/topol.cpp index 8fcea1146a4..6824ed240a9 100644 --- a/src/plugins/topology/topol.cpp +++ b/src/plugins/topology/topol.cpp @@ -16,9 +16,9 @@ ***************************************************************************/ // QGIS Specific includes -#include -#include -#include +#include "qgsmaplayer.h" +#include "qgsapplication.h" +#include "qgisinterface.h" #include "qgsguiutils.h" // Qt4 Related Includes diff --git a/src/plugins/topology/topolError.cpp b/src/plugins/topology/topolError.cpp index 2bd525ecd70..6756e4e00a9 100644 --- a/src/plugins/topology/topolError.cpp +++ b/src/plugins/topology/topolError.cpp @@ -16,7 +16,7 @@ ***************************************************************************/ #include "topolError.h" -#include +#include "qgsmessagelog.h" #include "qgsfeatureiterator.h" //TODO: tell dock to parse errorlist when feature is deleted diff --git a/src/plugins/topology/topolError.h b/src/plugins/topology/topolError.h index de54a594bd1..283f1b34f0d 100644 --- a/src/plugins/topology/topolError.h +++ b/src/plugins/topology/topolError.h @@ -18,9 +18,9 @@ #ifndef TOPOLERROR_H #define TOPOLERROR_H -#include -#include -#include +#include "qgsvectorlayer.h" +#include "qgsgeometry.h" +#include "qgsrectangle.h" class TopolError; typedef QList ErrorList; diff --git a/src/plugins/topology/topolTest.cpp b/src/plugins/topology/topolTest.cpp index b169c620ee8..05b12016841 100644 --- a/src/plugins/topology/topolTest.cpp +++ b/src/plugins/topology/topolTest.cpp @@ -17,16 +17,16 @@ #include "topolTest.h" -#include +#include "qgsvectorlayer.h" #include "qgsfeatureiterator.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include "qgsmaplayer.h" +#include "qgsmapcanvas.h" +#include "qgsgeometry.h" +#include "qgsfeature.h" +#include "qgsspatialindex.h" +#include "qgisinterface.h" +#include "qgslogger.h" +#include "qgsmessagelog.h" #include #include #include diff --git a/src/plugins/topology/topolTest.h b/src/plugins/topology/topolTest.h index 0db63075bda..95bb4916c78 100644 --- a/src/plugins/topology/topolTest.h +++ b/src/plugins/topology/topolTest.h @@ -20,8 +20,8 @@ #include -#include -#include +#include "qgsvectorlayer.h" +#include "qgsgeometry.h" #include "qgsspatialindex.h" #include "topolError.h" diff --git a/src/providers/db2/qgsdb2dataitems.h b/src/providers/db2/qgsdb2dataitems.h index 965a57782b8..73dd3ea505b 100644 --- a/src/providers/db2/qgsdb2dataitems.h +++ b/src/providers/db2/qgsdb2dataitems.h @@ -18,7 +18,7 @@ #include "qgsdb2provider.h" #include "qgsdb2tablemodel.h" -#include +#include "qgsdataitem.h" class QgsDb2RootItem; class QgsDb2Connection; diff --git a/src/providers/db2/qgsdb2geometrycolumns.cpp b/src/providers/db2/qgsdb2geometrycolumns.cpp index 35b579dcf77..894335aa8ed 100644 --- a/src/providers/db2/qgsdb2geometrycolumns.cpp +++ b/src/providers/db2/qgsdb2geometrycolumns.cpp @@ -17,7 +17,7 @@ #include "qgsdb2geometrycolumns.h" #include "qgsdb2tablemodel.h" // needed for QgsDB2LayerProperty #include -#include +#include "qgslogger.h" QgsDb2GeometryColumns::QgsDb2GeometryColumns( const QSqlDatabase &db ) diff --git a/src/providers/db2/qgsdb2provider.cpp b/src/providers/db2/qgsdb2provider.cpp index 294f2bbda1d..ad6ab4b412d 100644 --- a/src/providers/db2/qgsdb2provider.cpp +++ b/src/providers/db2/qgsdb2provider.cpp @@ -20,9 +20,9 @@ #include "qgsdb2dataitems.h" #include "qgsdb2featureiterator.h" #include "qgsdb2geometrycolumns.h" -#include -#include -#include +#include "qgscoordinatereferencesystem.h" +#include "qgsdataitem.h" +#include "qgslogger.h" #include "qgscredentials.h" #ifdef HAVE_GUI diff --git a/src/providers/db2/qgsdb2provider.h b/src/providers/db2/qgsdb2provider.h index 657fa8f1314..124fdacecde 100644 --- a/src/providers/db2/qgsdb2provider.h +++ b/src/providers/db2/qgsdb2provider.h @@ -20,7 +20,7 @@ #include "qgsvectordataprovider.h" #include "qgsvectorlayerexporter.h" -#include +#include "qgscoordinatereferencesystem.h" #include "qgsgeometry.h" #include "qgsfields.h" #include diff --git a/src/providers/db2/qgsdb2tablemodel.h b/src/providers/db2/qgsdb2tablemodel.h index f8933b2d7a7..e06d6a7041d 100644 --- a/src/providers/db2/qgsdb2tablemodel.h +++ b/src/providers/db2/qgsdb2tablemodel.h @@ -19,7 +19,7 @@ #define QGSDB2TABLEMODEL_H #include -#include +#include "qgsdataitem.h" #include "qgis.h" //! Layer Property structure diff --git a/src/providers/gpx/gpsdata.cpp b/src/providers/gpx/gpsdata.cpp index fab013ffdcb..82abea5386a 100644 --- a/src/providers/gpx/gpsdata.cpp +++ b/src/providers/gpx/gpsdata.cpp @@ -26,7 +26,7 @@ #include #include "gpsdata.h" -#include +#include "qgslogger.h" #define OUTPUT_PRECISION 12 diff --git a/src/providers/grass/qgsgrass.h b/src/providers/grass/qgsgrass.h index 19cc594ccd8..898855654b0 100644 --- a/src/providers/grass/qgsgrass.h +++ b/src/providers/grass/qgsgrass.h @@ -35,7 +35,7 @@ extern "C" #include "qgsexception.h" #include "qgsfeature.h" #include "qgsfields.h" -#include +#include "qgsrectangle.h" #include #include #include diff --git a/src/providers/grass/qgsgrassgislib.h b/src/providers/grass/qgsgrassgislib.h index 7ca998c9b51..194626b77a1 100644 --- a/src/providers/grass/qgsgrassgislib.h +++ b/src/providers/grass/qgsgrassgislib.h @@ -23,13 +23,13 @@ extern "C" } #include -#include -#include -#include -#include -#include -#include -#include +#include "qgscoordinatereferencesystem.h" +#include "qgsdistancearea.h" +#include "qgsexception.h" +#include "qgsproviderregistry.h" +#include "qgsrectangle.h" +#include "qgsrasterdataprovider.h" +#include "qgsrasterprojector.h" #include #include diff --git a/src/providers/ogr/qgsogrsourceselect.h b/src/providers/ogr/qgsogrsourceselect.h index 8850781cdeb..4233e8486fc 100644 --- a/src/providers/ogr/qgsogrsourceselect.h +++ b/src/providers/ogr/qgsogrsourceselect.h @@ -27,7 +27,7 @@ #ifndef QGSOGRSOURCESELECT_H #define QGSOGRSOURCESELECT_H -#include +#include "ui_qgsogrsourceselectbase.h" #include #include "qgshelp.h" #include "qgsproviderregistry.h" diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index f3c164f1896..9c178ce6ec9 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -15,16 +15,16 @@ * * ***************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qgsapplication.h" +#include "qgsfeature.h" +#include "qgsfield.h" +#include "qgsgeometry.h" +#include "qgsmessageoutput.h" +#include "qgsmessagelog.h" +#include "qgsrectangle.h" +#include "qgscoordinatereferencesystem.h" +#include "qgsxmlutils.h" +#include "qgsvectorlayer.h" #include diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 4a4811efdd5..0077957f147 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -31,8 +31,8 @@ email : a.furieri@lqt.it #include "qgsspatialitefeatureiterator.h" #include "qgsfeedback.h" -#include -#include +#include "qgsjsonutils.h" +#include "qgsvectorlayer.h" #include #include diff --git a/src/providers/virtual/qgsembeddedlayerselectdialog.cpp b/src/providers/virtual/qgsembeddedlayerselectdialog.cpp index 27c0a6735ba..493aafa3f48 100644 --- a/src/providers/virtual/qgsembeddedlayerselectdialog.cpp +++ b/src/providers/virtual/qgsembeddedlayerselectdialog.cpp @@ -20,14 +20,14 @@ email : hugo dot mercier at oslandia dot com #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "qgsvectorlayer.h" +#include "layertree/qgslayertreeview.h" +#include "layertree/qgslayertreemodel.h" +#include "layertree/qgslayertreegroup.h" +#include "layertree/qgslayertreelayer.h" +#include "layertree/qgslayertree.h" +#include "qgsproviderregistry.h" +#include "qgsvectordataprovider.h" QgsEmbeddedLayerSelectDialog::QgsEmbeddedLayerSelectDialog( QWidget *parent, QgsLayerTreeView *tv ) : QDialog( parent ), diff --git a/src/providers/virtual/qgsvirtuallayerblob.h b/src/providers/virtual/qgsvirtuallayerblob.h index 7d05786fb5c..50fc26201e5 100644 --- a/src/providers/virtual/qgsvirtuallayerblob.h +++ b/src/providers/virtual/qgsvirtuallayerblob.h @@ -19,7 +19,7 @@ email : hugo dot mercier at oslandia dot com #include -#include +#include "qgsgeometry.h" // BLOB header // name size value diff --git a/src/providers/virtual/qgsvirtuallayerprovider.h b/src/providers/virtual/qgsvirtuallayerprovider.h index 80528f81313..c514e607b63 100644 --- a/src/providers/virtual/qgsvirtuallayerprovider.h +++ b/src/providers/virtual/qgsvirtuallayerprovider.h @@ -17,7 +17,7 @@ email : hugo dot mercier at oslandia dot com #ifndef QGSVIRTUAL_LAYER_PROVIDER_H #define QGSVIRTUAL_LAYER_PROVIDER_H -#include +#include "qgsvectordataprovider.h" #include "qgscoordinatereferencesystem.h" #include "qgsvirtuallayerdefinition.h" diff --git a/src/providers/virtual/qgsvirtuallayerqueryparser.h b/src/providers/virtual/qgsvirtuallayerqueryparser.h index 20265e4c9c5..9c392665d7d 100644 --- a/src/providers/virtual/qgsvirtuallayerqueryparser.h +++ b/src/providers/virtual/qgsvirtuallayerqueryparser.h @@ -17,9 +17,9 @@ email : hugo dot mercier at oslandia dot com #ifndef QGSVIRTUALLAYER_QUERY_PARSER_H #define QGSVIRTUALLAYER_QUERY_PARSER_H -#include -#include -#include +#include "qgis.h" +#include "qgswkbtypes.h" +#include "qgsvectorlayer.h" namespace QgsVirtualLayerQueryParser { diff --git a/src/providers/virtual/qgsvirtuallayersourceselect.cpp b/src/providers/virtual/qgsvirtuallayersourceselect.cpp index e595dbf7345..670d466982e 100644 --- a/src/providers/virtual/qgsvirtuallayersourceselect.cpp +++ b/src/providers/virtual/qgsvirtuallayersourceselect.cpp @@ -18,16 +18,16 @@ email : hugo dot mercier at oslandia dot com #include "qgsvirtuallayersourceselect.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "layertree/qgslayertreeview.h" +#include "qgsvectorlayer.h" +#include "qgsvectordataprovider.h" +#include "qgsproject.h" +#include "qgsprojectionselectiondialog.h" +#include "layertree/qgslayertreemodel.h" +#include "layertree/qgslayertreegroup.h" +#include "layertree/qgslayertreelayer.h" +#include "layertree/qgslayertree.h" +#include "qgsproviderregistry.h" #include "qgsembeddedlayerselectdialog.h" diff --git a/src/providers/virtual/qgsvirtuallayersourceselect.h b/src/providers/virtual/qgsvirtuallayersourceselect.h index cd441b47a13..3de21bcd3d5 100644 --- a/src/providers/virtual/qgsvirtuallayersourceselect.h +++ b/src/providers/virtual/qgsvirtuallayersourceselect.h @@ -20,9 +20,9 @@ email : hugo dot mercier at oslandia dot com #define QGSVIRTUAL_LAYER_SOURCE_SELECT_H #include "ui_qgsvirtuallayersourceselectbase.h" -#include +#include "qgis.h" #include "qgsguiutils.h" -#include +#include "qgsvirtuallayerdefinition.h" #include "qgsproviderregistry.h" #include "qgsabstractdatasourcewidget.h" diff --git a/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp b/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp index 59d0da0afc7..75748a02fc4 100644 --- a/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp +++ b/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp @@ -22,12 +22,12 @@ email : hugo dot mercier at oslandia dot com #include #include -#include -#include -#include -#include -#include -#include +#include "qgsapplication.h" +#include "qgsvectorlayer.h" +#include "qgsvectordataprovider.h" +#include "qgsgeometry.h" +#include "qgsproject.h" +#include "qgsproviderregistry.h" #include "qgsinterval.h" #include #include diff --git a/src/providers/virtual/qgsvirtuallayersqlitemodule.h b/src/providers/virtual/qgsvirtuallayersqlitemodule.h index 5e933a8e4de..5f7a1085688 100644 --- a/src/providers/virtual/qgsvirtuallayersqlitemodule.h +++ b/src/providers/virtual/qgsvirtuallayersqlitemodule.h @@ -44,7 +44,7 @@ int qgsvlayerModuleInit( sqlite3 *db, #ifdef __cplusplus } -#include +#include "qgsgeometry.h" /** * Init the SQLite file with proper metadata tables From eaf861c25890d5d0bd9376d5ea600f9479362029 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 05:09:29 +1000 Subject: [PATCH 130/364] Replace some more c headers with c++ headers --- src/app/dwg/libdxfrw/drw_header.cpp | 2 +- src/app/qgsattributetabledialog.h | 2 +- src/core/qgis.h | 2 +- src/providers/virtual/qgsvirtuallayersqlitemodule.cpp | 4 ++-- src/server/qgsmslayercache.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/dwg/libdxfrw/drw_header.cpp b/src/app/dwg/libdxfrw/drw_header.cpp index 89299534fb4..4391ed65a18 100644 --- a/src/app/dwg/libdxfrw/drw_header.cpp +++ b/src/app/dwg/libdxfrw/drw_header.cpp @@ -19,7 +19,7 @@ #include "qgslogger.h" #include -#include +#include DRW_Header::DRW_Header() : curr( nullptr ) diff --git a/src/app/qgsattributetabledialog.h b/src/app/qgsattributetabledialog.h index d4150c258bb..a378430b246 100644 --- a/src/app/qgsattributetabledialog.h +++ b/src/app/qgsattributetabledialog.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include "ui_qgsattributetabledialog.h" #include "qgssearchwidgetwrapper.h" diff --git a/src/core/qgis.h b/src/core/qgis.h index 4d8a9d7ea38..69d6482a68e 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp b/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp index 75748a02fc4..97f14ef0438 100644 --- a/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp +++ b/src/providers/virtual/qgsvirtuallayersqlitemodule.cpp @@ -16,7 +16,7 @@ email : hugo dot mercier at oslandia dot com #include #include -#include +#include #include #include @@ -31,7 +31,7 @@ email : hugo dot mercier at oslandia dot com #include "qgsinterval.h" #include #include -#include +#include #include "qgsvirtuallayersqlitemodule.h" #include "qgsvirtuallayerblob.h" #include "qgsslottofunction.h" diff --git a/src/server/qgsmslayercache.h b/src/server/qgsmslayercache.h index 16c5cea4139..a18ca515c93 100644 --- a/src/server/qgsmslayercache.h +++ b/src/server/qgsmslayercache.h @@ -21,7 +21,7 @@ #define SIP_NO_FILE -#include +#include #include #include #include From 0fbca6fe0af604cab07df8d570e66cd7bc71ddb0 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 06:28:07 +1000 Subject: [PATCH 131/364] Fix build --- src/providers/grass/qgis.r.in.cpp | 2 +- src/providers/grass/qgis.v.in.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/grass/qgis.r.in.cpp b/src/providers/grass/qgis.r.in.cpp index c741232369b..188a2f9e03a 100644 --- a/src/providers/grass/qgis.r.in.cpp +++ b/src/providers/grass/qgis.r.in.cpp @@ -18,7 +18,7 @@ extern "C" #include #include #include -#include +#include #ifdef WIN32 #include #include diff --git a/src/providers/grass/qgis.v.in.cpp b/src/providers/grass/qgis.v.in.cpp index f8595f8ebf1..dc663749f91 100644 --- a/src/providers/grass/qgis.v.in.cpp +++ b/src/providers/grass/qgis.v.in.cpp @@ -18,7 +18,7 @@ extern "C" #include #include #include -#include +#include #include #ifdef WIN32 #include From 578045d1d5479821d5d2879b128d88a8b131fd4d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 06:29:04 +1000 Subject: [PATCH 132/364] Update sip bindings --- python/core/qgsstatisticalsummary.sip | 1 - 1 file changed, 1 deletion(-) diff --git a/python/core/qgsstatisticalsummary.sip b/python/core/qgsstatisticalsummary.sip index afd2e50b72c..9896ae80970 100644 --- a/python/core/qgsstatisticalsummary.sip +++ b/python/core/qgsstatisticalsummary.sip @@ -10,7 +10,6 @@ - class QgsStatisticalSummary { %Docstring From d573c43e3e3a0a6740b1a471abfa45b7f9114bec Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 15:58:57 +1000 Subject: [PATCH 133/364] Better test debugging --- python/plugins/processing/tests/AlgorithmsTestBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 7f4837fe62d..c0620350994 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -307,7 +307,7 @@ class AlgorithmsTest(object): strhash = hashlib.sha224(dataArray.data).hexdigest() if not isinstance(expected_result['hash'], str): - self.assertTrue(strhash in expected_result['hash']) + self.assertIn(strhash, expected_result['hash']) else: self.assertEqual(strhash, expected_result['hash']) elif 'file' == expected_result['type']: From 641d78bb4c471dcbd59d4d019c3d55d230d70d31 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 16:21:42 +1000 Subject: [PATCH 134/364] Add new raster hash for hillshade test (verified ok) --- .../plugins/processing/tests/testdata/qgis_algorithm_tests.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index aed4606b561..39ebd6d0a99 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -1386,6 +1386,7 @@ tests: hash: - 58365b3715b925d6286e7f082ebd9c2a20f09fa1c922176d3f238002 - 75cca4c1a870a1e21185a2d85b33b6d9958a69fc6ebb04e4d6ceb8a3 + - c05cd8dbfb00200a3803dcdc74ad177588eb8379867c4046463f73f1 type: rasterhash - algorithm: qgis:relief From 2d192791c29980429104babca34f3e82a051a861 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 25 Aug 2017 16:37:54 +1000 Subject: [PATCH 135/364] qMin/qMax -> std::min/max --- .../interpolation/DualEdgeTriangulation.cc | 2 +- src/analysis/interpolation/MathUtils.cc | 4 +- src/analysis/raster/qgshillshadefilter.cpp | 2 +- src/analysis/raster/qgskde.cpp | 4 +- src/analysis/vector/qgsgeometrysnapper.cpp | 8 ++-- src/analysis/vector/qgszonalstatistics.h | 4 +- src/app/nodetool/qgsnodetool.cpp | 2 +- src/app/qgsaddattrdialog.cpp | 2 +- src/app/qgsattributetabledialog.cpp | 2 +- src/app/qgsdiagramproperties.cpp | 4 +- src/app/qgsfieldcalculator.cpp | 2 +- src/app/qgsidentifyresultsdialog.cpp | 4 +- src/app/qgsmeasuredialog.cpp | 2 +- src/app/qgsrasterlayerproperties.cpp | 6 +-- src/app/qgsrulebasedlabelingwidget.cpp | 4 +- src/app/qgsvariantdelegate.cpp | 8 ++-- src/app/qgswelcomepageitemsmodel.cpp | 2 +- src/core/composer/qgscomposerarrow.cpp | 10 ++-- .../qgscomposerattributetablemodelv2.cpp | 4 +- src/core/composer/qgscomposerframe.cpp | 4 +- src/core/composer/qgscomposerhtml.cpp | 4 +- src/core/composer/qgscomposerlabel.cpp | 2 +- src/core/composer/qgscomposermap.cpp | 8 ++-- src/core/composer/qgscomposermapgrid.cpp | 38 +++++++-------- src/core/composer/qgscomposertablev2.cpp | 14 +++--- src/core/composer/qgscomposition.cpp | 12 ++--- src/core/diagram/qgshistogramdiagram.cpp | 6 +-- src/core/diagram/qgspiediagram.cpp | 2 +- src/core/diagram/qgstextdiagram.cpp | 2 +- src/core/effects/qgsimageoperation.cpp | 32 ++++++------- src/core/expression/qgsexpressionfunction.cpp | 2 +- src/core/geometry/qgsbox3d.cpp | 12 ++--- src/core/geometry/qgscircularstring.cpp | 4 +- src/core/geometry/qgsgeos.cpp | 2 +- .../geometry/qgsinternalgeometryengine.cpp | 6 +-- src/core/geometry/qgslinestring.cpp | 2 +- src/core/geometry/qgspolygon.cpp | 2 +- src/core/geometry/qgsrectangle.cpp | 2 +- src/core/layertree/qgslayertreemodel.cpp | 2 +- .../layertree/qgslayertreemodellegendnode.cpp | 10 ++-- src/core/layout/qgslayoutitempage.cpp | 4 +- src/core/layout/qgslayoutpagecollection.cpp | 2 +- src/core/pal/feature.cpp | 14 +++--- src/core/pal/labelposition.cpp | 10 ++-- src/core/pal/pointset.cpp | 2 +- src/core/pal/rtree.hpp | 4 +- src/core/qgis.cpp | 4 +- src/core/qgscolorramp.cpp | 16 +++---- src/core/qgsdatadefinedsizelegend.cpp | 2 +- src/core/qgsdatetimestatisticalsummary.cpp | 4 +- src/core/qgsdiagramrenderer.cpp | 2 +- src/core/qgslegendrenderer.cpp | 38 +++++++-------- src/core/qgsmaptopixelgeometrysimplifier.cpp | 2 +- src/core/qgsmapunitscale.cpp | 4 +- src/core/qgspallabeling.cpp | 4 +- src/core/qgspointxy.cpp | 8 ++-- src/core/qgsrenderchecker.cpp | 8 ++-- src/core/qgsrendercontext.cpp | 12 ++--- src/core/qgssnappingutils.cpp | 2 +- src/core/qgsstatisticalsummary.cpp | 4 +- src/core/qgsstringstatisticalsummary.cpp | 8 ++-- src/core/qgsstringutils.cpp | 2 +- src/core/qgstextrenderer.cpp | 4 +- src/core/qgsvectorfilewriter.cpp | 2 +- src/core/qgsvectorlayereditbuffer.cpp | 2 +- src/core/qgsvectorlayerlabeling.cpp | 4 +- src/core/raster/qgscolorrampshader.cpp | 4 +- src/core/raster/qgshillshaderenderer.cpp | 2 +- src/core/raster/qgshuesaturationfilter.cpp | 4 +- src/core/raster/qgsrasterblock.cpp | 12 ++--- src/core/raster/qgsrasterdataprovider.cpp | 4 +- src/core/raster/qgsrasterinterface.cpp | 10 ++-- src/core/raster/qgsrasteriterator.cpp | 4 +- src/core/raster/qgsrasterpipe.cpp | 8 ++-- src/core/simplify/effectivearea.h | 2 +- src/core/symbology/qgsfillsymbollayer.cpp | 20 ++++---- .../symbology/qgsgraduatedsymbolrenderer.cpp | 10 ++-- src/core/symbology/qgsheatmaprenderer.cpp | 6 +-- src/core/symbology/qgslinesymbollayer.cpp | 2 +- .../qgspointdisplacementrenderer.cpp | 12 ++--- src/core/symbology/qgssymbollayerutils.cpp | 8 ++-- .../qgsattributetablefiltermodel.cpp | 2 +- .../attributetable/qgsattributetablemodel.cpp | 2 +- .../attributetable/qgsattributetableview.cpp | 4 +- src/gui/attributetable/qgsfeaturelistview.cpp | 4 +- .../qgsfeaturelistviewdelegate.cpp | 2 +- src/gui/editorwidgets/qgsdatetimeedit.cpp | 4 +- src/gui/editorwidgets/qgsdoublespinbox.cpp | 4 +- src/gui/editorwidgets/qgsspinbox.cpp | 4 +- .../qgseffectstackpropertieswidget.cpp | 2 +- src/gui/locator/qgslocatorwidget.cpp | 4 +- src/gui/qgscolorbutton.cpp | 2 +- src/gui/qgscolorswatchgrid.cpp | 6 +-- src/gui/qgscolorwidgets.cpp | 24 +++++----- src/gui/qgscomposerview.cpp | 4 +- src/gui/qgscompoundcolorwidget.cpp | 2 +- src/gui/qgscurveeditorwidget.cpp | 4 +- src/gui/qgsfontbutton.cpp | 16 +++---- src/gui/qgshighlight.cpp | 2 +- src/gui/qgshistogramwidget.cpp | 4 +- src/gui/qgsmapcanvas.cpp | 4 +- src/gui/qgsmapcanvasannotationitem.cpp | 8 ++-- src/gui/qgsmaptoolzoom.cpp | 2 +- src/gui/qgsprevieweffect.cpp | 6 +-- src/gui/qgspropertyassistantwidget.cpp | 6 +-- src/gui/qgsrasterlayersaveasdialog.cpp | 4 +- src/gui/qgssymbolbutton.cpp | 2 +- src/gui/qgstextpreview.cpp | 6 +-- src/gui/raster/qgspalettedrendererwidget.cpp | 4 +- .../raster/qgsrastertransparencywidget.cpp | 4 +- src/gui/symbology/characterwidget.cpp | 6 +-- .../symbology/qgsellipsesymbollayerwidget.cpp | 2 +- .../qgspointdisplacementrendererwidget.cpp | 2 +- .../symbology/qgsrulebasedrendererwidget.cpp | 4 +- src/gui/symbology/qgssvgselectorwidget.cpp | 2 +- src/gui/symbology/qgssymbollayerwidget.cpp | 4 +- .../checks/qgsgeometrysliverpolygoncheck.h | 2 +- .../ui/qgsgeometrycheckerfixsummarydialog.cpp | 2 +- .../ui/qgsgeometrycheckerresulttab.cpp | 4 +- .../ui/qgsgeometrycheckersetuptab.cpp | 2 +- .../utils/qgsgeometrycheckerutils.cpp | 6 +-- .../georeferencer/qgsgeorefplugingui.cpp | 8 ++-- .../georeferencer/qgsgeoreftransform.cpp | 2 +- src/plugins/georeferencer/qgsimagewarper.cpp | 2 +- src/plugins/georeferencer/qgsleastsquares.cpp | 2 +- src/plugins/grass/qtermwidget/ColorScheme.cpp | 4 +- src/plugins/grass/qtermwidget/History.cpp | 4 +- .../grass/qtermwidget/HistorySearch.cpp | 2 +- src/plugins/grass/qtermwidget/Screen.cpp | 46 +++++++++---------- .../grass/qtermwidget/ScreenWindow.cpp | 14 +++--- src/plugins/grass/qtermwidget/Session.cpp | 4 +- .../qtermwidget/TerminalCharacterDecoder.cpp | 2 +- .../grass/qtermwidget/TerminalDisplay.cpp | 40 ++++++++-------- .../grass/qtermwidget/Vt102Emulation.cpp | 4 +- src/plugins/grass/qtermwidget/kpty.cpp | 2 +- src/plugins/grass/qtermwidget/kptydevice.cpp | 4 +- src/plugins/grass/qtermwidget/kptydevice.h | 10 ++-- src/providers/arcgisrest/qgsafsshareddata.cpp | 2 +- src/providers/arcgisrest/qgsamsprovider.cpp | 6 +-- .../grass/qgsgrassfeatureiterator.cpp | 2 +- src/providers/grass/qgsgrassvector.cpp | 2 +- .../grass/qgsgrassvectormaplayer.cpp | 8 ++-- src/providers/ogr/qgsogrprovider.cpp | 8 ++-- src/providers/wcs/qgswcsprovider.cpp | 4 +- src/providers/wfs/qgswfsfeatureiterator.cpp | 2 +- src/providers/wms/qgswmsprovider.cpp | 10 ++-- src/server/qgsmslayercache.cpp | 4 +- src/server/qgsserverprojectutils.cpp | 2 +- src/server/qgswmsconfigparser.cpp | 2 +- src/server/qgswmsprojectparser.cpp | 2 +- src/server/services/wms/qgswmsparameters.cpp | 2 +- 151 files changed, 452 insertions(+), 452 deletions(-) diff --git a/src/analysis/interpolation/DualEdgeTriangulation.cc b/src/analysis/interpolation/DualEdgeTriangulation.cc index 06ddd24f005..627480b13a5 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.cc +++ b/src/analysis/interpolation/DualEdgeTriangulation.cc @@ -387,7 +387,7 @@ int DualEdgeTriangulation::addPoint( QgsPoint *p ) //Take the higher z-Value in case of two equal points QgsPoint *newPoint = mPointVector[mPointVector.count() - 1]; QgsPoint *existingPoint = mPointVector[mTwiceInsPoint]; - existingPoint->setZ( qMax( newPoint->z(), existingPoint->z() ) ); + existingPoint->setZ( std::max( newPoint->z(), existingPoint->z() ) ); mPointVector.remove( mPointVector.count() - 1 ); delete newPoint; diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index a83fe7e2e0e..ce659b66f14 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -253,8 +253,8 @@ bool MathUtils::inCircle( QgsPoint *testp, QgsPoint *p1, QgsPoint *p2, QgsPoint double px = testp->x(); double py = testp->y(); - double xmin = qMin( qMin( ax, px ), qMin( bx, cx ) ); - double ymin = qMin( qMin( ay, py ), qMin( by, cy ) ); + double xmin = std::min( std::min( ax, px ), std::min( bx, cx ) ); + double ymin = std::min( std::min( ay, py ), std::min( by, cy ) ); ax -= xmin; bx -= xmin; cx -= xmin; diff --git a/src/analysis/raster/qgshillshadefilter.cpp b/src/analysis/raster/qgshillshadefilter.cpp index 3da206fbc24..d62529c8aad 100644 --- a/src/analysis/raster/qgshillshadefilter.cpp +++ b/src/analysis/raster/qgshillshadefilter.cpp @@ -49,5 +49,5 @@ float QgsHillshadeFilter::processNineCellWindow( float *x11, float *x21, float * { aspect_rad = M_PI + std::atan2( derX, derY ); } - return qMax( 0.0, 255.0 * ( ( std::cos( zenith_rad ) * std::cos( slope_rad ) ) + ( std::sin( zenith_rad ) * std::sin( slope_rad ) * std::cos( azimuth_rad - aspect_rad ) ) ) ); + return std::max( 0.0, 255.0 * ( ( std::cos( zenith_rad ) * std::cos( slope_rad ) ) + ( std::sin( zenith_rad ) * std::sin( slope_rad ) * std::cos( azimuth_rad - aspect_rad ) ) ) ); } diff --git a/src/analysis/raster/qgskde.cpp b/src/analysis/raster/qgskde.cpp index 69df88cc001..e63e8477a15 100644 --- a/src/analysis/raster/qgskde.cpp +++ b/src/analysis/raster/qgskde.cpp @@ -87,8 +87,8 @@ QgsKernelDensityEstimation::Result QgsKernelDensityEstimation::prepare() if ( mBounds.isNull() ) return InvalidParameters; - int rows = qMax( std::ceil( mBounds.height() / mPixelSize ) + 1, 1.0 ); - int cols = qMax( std::ceil( mBounds.width() / mPixelSize ) + 1, 1.0 ); + int rows = std::max( std::ceil( mBounds.height() / mPixelSize ) + 1, 1.0 ); + int cols = std::max( std::ceil( mBounds.width() / mPixelSize ) + 1, 1.0 ); if ( !createEmptyLayer( driver, mBounds, rows, cols ) ) return FileCreationError; diff --git a/src/analysis/vector/qgsgeometrysnapper.cpp b/src/analysis/vector/qgsgeometrysnapper.cpp index 9e8e3d478f9..e46d53b47fc 100644 --- a/src/analysis/vector/qgsgeometrysnapper.cpp +++ b/src/analysis/vector/qgsgeometrysnapper.cpp @@ -233,8 +233,8 @@ const QgsSnapIndex::Cell *QgsSnapIndex::GridRow::getCell( int col ) const QList QgsSnapIndex::GridRow::getSnapItems( int colStart, int colEnd ) const { - colStart = qMax( colStart, mColStartIdx ); - colEnd = qMin( colEnd, mColStartIdx + mCells.size() - 1 ); + colStart = std::max( colStart, mColStartIdx ); + colEnd = std::min( colEnd, mColStartIdx + mCells.size() - 1 ); QList items; @@ -403,8 +403,8 @@ QgsSnapIndex::SnapItem *QgsSnapIndex::getSnapItem( const QgsPoint &pos, double t int colEnd = std::floor( ( pos.x() + tol - mOrigin.x() ) / mCellSize ); int rowEnd = std::floor( ( pos.y() + tol - mOrigin.y() ) / mCellSize ); - rowStart = qMax( rowStart, mRowsStartIdx ); - rowEnd = qMin( rowEnd, mRowsStartIdx + mGridRows.size() - 1 ); + rowStart = std::max( rowStart, mRowsStartIdx ); + rowEnd = std::min( rowEnd, mRowsStartIdx + mGridRows.size() - 1 ); QList items; for ( int row = rowStart; row <= rowEnd; ++row ) diff --git a/src/analysis/vector/qgszonalstatistics.h b/src/analysis/vector/qgszonalstatistics.h index b2a99736a3f..94277f1d2e1 100644 --- a/src/analysis/vector/qgszonalstatistics.h +++ b/src/analysis/vector/qgszonalstatistics.h @@ -98,8 +98,8 @@ class ANALYSIS_EXPORT QgsZonalStatistics sum += value; ++count; } - min = qMin( min, value ); - max = qMax( max, value ); + min = std::min( min, value ); + max = std::max( max, value ); if ( mStoreValueCounts ) valueCount.insert( value, valueCount.value( value, 0 ) + 1 ); if ( mStoreValues ) diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index 329765174bd..9de9941975e 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -976,7 +976,7 @@ void QgsNodeTool::deleteNodeEditorSelection() if ( mSelectedFeature->geometry()->type() == QgsWkbTypes::LineGeometry ) { // for lines we don't wrap around vertex selection when deleting nodes from end of line - nextVertexToSelect = qMin( nextVertexToSelect, mSelectedFeature->geometry()->geometry()->nCoordinates() - 1 ); + nextVertexToSelect = std::min( nextVertexToSelect, mSelectedFeature->geometry()->geometry()->nCoordinates() - 1 ); } _safeSelectVertex( *mSelectedFeature, nextVertexToSelect ); diff --git a/src/app/qgsaddattrdialog.cpp b/src/app/qgsaddattrdialog.cpp index 16452050a90..181ca6343f7 100644 --- a/src/app/qgsaddattrdialog.cpp +++ b/src/app/qgsaddattrdialog.cpp @@ -86,7 +86,7 @@ void QgsAddAttrDialog::setPrecisionMinMax() mPrec->setVisible( minPrecType < maxPrecType ); mPrecLabel->setVisible( minPrecType < maxPrecType ); mPrec->setMinimum( minPrecType ); - mPrec->setMaximum( qMax( minPrecType, qMin( maxPrecType, mLength->value() ) ) ); + mPrec->setMaximum( std::max( minPrecType, std::min( maxPrecType, mLength->value() ) ) ); } void QgsAddAttrDialog::accept() diff --git a/src/app/qgsattributetabledialog.cpp b/src/app/qgsattributetabledialog.cpp index 5a810cce4ff..2af8abbb203 100644 --- a/src/app/qgsattributetabledialog.cpp +++ b/src/app/qgsattributetabledialog.cpp @@ -344,7 +344,7 @@ void QgsAttributeTableDialog::updateTitle() QWidget *w = mDock ? qobject_cast( mDock ) : qobject_cast( this ); w->setWindowTitle( tr( " %1 :: Features Total: %2, Filtered: %3, Selected: %4" ) .arg( mLayer->name() ) - .arg( qMax( static_cast< long >( mMainView->featureCount() ), mLayer->featureCount() ) ) // layer count may be estimated, so use larger of the two + .arg( std::max( static_cast< long >( mMainView->featureCount() ), mLayer->featureCount() ) ) // layer count may be estimated, so use larger of the two .arg( mMainView->filteredFeatureCount() ) .arg( mLayer->selectedFeatureCount() ) ); diff --git a/src/app/qgsdiagramproperties.cpp b/src/app/qgsdiagramproperties.cpp index aaf6321eed4..c558d87277f 100644 --- a/src/app/qgsdiagramproperties.cpp +++ b/src/app/qgsdiagramproperties.cpp @@ -593,7 +593,7 @@ void QgsDiagramProperties::on_mFindMaximumValueButton_clicked() while ( features.nextFeature( *&feature ) ) { context.setFeature( feature ); - maxValue = qMax( maxValue, exp.evaluate( &context ).toFloat() ); + maxValue = std::max( maxValue, exp.evaluate( &context ).toFloat() ); } } else @@ -689,7 +689,7 @@ void QgsDiagramProperties::apply() bool ok = false; double val = provider->maximumValue( fld ).toDouble( &ok ); if ( ok ) - maxVal = qMax( maxVal, val ); + maxVal = std::max( maxVal, val ); } } } diff --git a/src/app/qgsfieldcalculator.cpp b/src/app/qgsfieldcalculator.cpp index 975675caee9..b15f857149a 100644 --- a/src/app/qgsfieldcalculator.cpp +++ b/src/app/qgsfieldcalculator.cpp @@ -481,7 +481,7 @@ void QgsFieldCalculator::setPrecisionMinMax() int maxPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole + 5 ).toInt(); mOutputFieldPrecisionSpinBox->setEnabled( minPrecType < maxPrecType ); mOutputFieldPrecisionSpinBox->setMinimum( minPrecType ); - mOutputFieldPrecisionSpinBox->setMaximum( qMax( minPrecType, qMin( maxPrecType, mOutputFieldWidthSpinBox->value() ) ) ); + mOutputFieldPrecisionSpinBox->setMaximum( std::max( minPrecType, std::min( maxPrecType, mOutputFieldWidthSpinBox->value() ) ) ); } void QgsFieldCalculator::showHelp() diff --git a/src/app/qgsidentifyresultsdialog.cpp b/src/app/qgsidentifyresultsdialog.cpp index 73e0658001c..a62d32a641c 100644 --- a/src/app/qgsidentifyresultsdialog.cpp +++ b/src/app/qgsidentifyresultsdialog.cpp @@ -234,7 +234,7 @@ QSize QgsIdentifyResultsWebView::sizeHint() const // correct size, see #9377. int max = widget->size().height() * 0.9; QgsDebugMsg( QString( "parent widget height = %1 max height = %2" ).arg( widget->size().height() ).arg( max ) ); - height = qMin( height, max ); + height = std::min( height, max ); } else { @@ -243,7 +243,7 @@ QSize QgsIdentifyResultsWebView::sizeHint() const // Always keep some minimum size, e.g. if page is not yet loaded // or parent has wrong size - height = qMax( height, 100 ); + height = std::max( height, 100 ); s = QSize( size().width(), height ); QgsDebugMsg( QString( "size: %1 x %2" ).arg( s.width() ).arg( s.height() ) ); diff --git a/src/app/qgsmeasuredialog.cpp b/src/app/qgsmeasuredialog.cpp index fba4334d918..6249a30626d 100644 --- a/src/app/qgsmeasuredialog.cpp +++ b/src/app/qgsmeasuredialog.cpp @@ -281,7 +281,7 @@ QString QgsMeasureDialog::formatDistance( double distance, bool convertUnits ) c // special handling for degrees - because we can't use smaller units (eg m->mm), we need to make sure there's // enough decimal places to show a usable measurement value int minPlaces = std::round( std::log10( 1.0 / distance ) ) + 1; - decimals = qMax( decimals, minPlaces ); + decimals = std::max( decimals, minPlaces ); } return QgsDistanceArea::formatDistance( distance, decimals, mDistanceUnits, baseUnit ); } diff --git a/src/app/qgsrasterlayerproperties.cpp b/src/app/qgsrasterlayerproperties.cpp index 729ceda1d2f..9599b2ee90b 100644 --- a/src/app/qgsrasterlayerproperties.cpp +++ b/src/app/qgsrasterlayerproperties.cpp @@ -1281,8 +1281,8 @@ void QgsRasterLayerProperties::adjustTransparencyCellWidth( int row, int column QLineEdit *lineEdit = dynamic_cast( tableTransparency->cellWidget( row, column ) ); if ( !lineEdit ) return; - int width = qMax( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 ); - width = qMax( width, tableTransparency->columnWidth( column ) ); + int width = std::max( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 ); + width = std::max( width, tableTransparency->columnWidth( column ) ); lineEdit->setFixedWidth( width ); } @@ -1850,4 +1850,4 @@ void QgsRasterLayerProperties::onCancel() void QgsRasterLayerProperties::showHelp() { QgsHelp::openHelp( QStringLiteral( "working_with_raster/raster_properties.html" ) ); -} \ No newline at end of file +} diff --git a/src/app/qgsrulebasedlabelingwidget.cpp b/src/app/qgsrulebasedlabelingwidget.cpp index 2ade17f23d7..05ea50e8ba3 100644 --- a/src/app/qgsrulebasedlabelingwidget.cpp +++ b/src/app/qgsrulebasedlabelingwidget.cpp @@ -587,8 +587,8 @@ QgsLabelingRulePropsWidget::QgsLabelingRulePropsWidget( QgsRuleBasedLabeling::Ru { groupScale->setChecked( true ); // caution: rule uses scale denom, scale widget uses true scales - mScaleRangeWidget->setMaximumScale( qMax( rule->maximumScale(), 0.0 ) ); - mScaleRangeWidget->setMinimumScale( qMax( rule->minimumScale(), 0.0 ) ); + mScaleRangeWidget->setMaximumScale( std::max( rule->maximumScale(), 0.0 ) ); + mScaleRangeWidget->setMinimumScale( std::max( rule->minimumScale(), 0.0 ) ); } mScaleRangeWidget->setMapCanvas( mMapCanvas ); diff --git a/src/app/qgsvariantdelegate.cpp b/src/app/qgsvariantdelegate.cpp index 94672743f97..64e79b17eea 100644 --- a/src/app/qgsvariantdelegate.cpp +++ b/src/app/qgsvariantdelegate.cpp @@ -190,10 +190,10 @@ void QgsVariantDelegate::setModelData( QWidget *editor, QAbstractItemModel *mode break; case QVariant::Color: ( void )mColorExp.exactMatch( text ); - value = QColor( qMin( mColorExp.cap( 1 ).toInt(), 255 ), - qMin( mColorExp.cap( 2 ).toInt(), 255 ), - qMin( mColorExp.cap( 3 ).toInt(), 255 ), - qMin( mColorExp.cap( 4 ).toInt(), 255 ) ); + value = QColor( std::min( mColorExp.cap( 1 ).toInt(), 255 ), + std::min( mColorExp.cap( 2 ).toInt(), 255 ), + std::min( mColorExp.cap( 3 ).toInt(), 255 ), + std::min( mColorExp.cap( 4 ).toInt(), 255 ) ); break; case QVariant::Date: { diff --git a/src/app/qgswelcomepageitemsmodel.cpp b/src/app/qgswelcomepageitemsmodel.cpp index 55cdc73433c..d82d97a4c31 100644 --- a/src/app/qgswelcomepageitemsmodel.cpp +++ b/src/app/qgswelcomepageitemsmodel.cpp @@ -117,7 +117,7 @@ QSize QgsWelcomePageItemDelegate::sizeHint( const QStyleOptionViewItem &option, index.data( QgsWelcomePageItemsModel::CrsRole ).toString() ) ); doc.setTextWidth( width - ( !icon.isNull() ? icon.width() + 35 : 35 ) ); - return QSize( width, qMax( ( double ) doc.size().height() + 10, ( double )icon.height() ) + 20 ); + return QSize( width, std::max( ( double ) doc.size().height() + 10, ( double )icon.height() ) + 20 ); } QgsWelcomePageItemsModel::QgsWelcomePageItemsModel( QObject *parent ) diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp index ea960ba1035..d8320e3293c 100644 --- a/src/core/composer/qgscomposerarrow.cpp +++ b/src/core/composer/qgscomposerarrow.cpp @@ -380,8 +380,8 @@ double QgsComposerArrow::computeMarkerMargin() const } else if ( mMarkerMode == SVGMarker ) { - double maxArrowHeight = qMax( mStartArrowHeadHeight, mStopArrowHeadHeight ); - margin = mPen.widthF() / 2 + qMax( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 ); + double maxArrowHeight = std::max( mStartArrowHeadHeight, mStopArrowHeadHeight ); + margin = mPen.widthF() / 2 + std::max( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 ); } } else @@ -398,8 +398,8 @@ double QgsComposerArrow::computeMarkerMargin() const { double startMarkerMargin = std::sqrt( 0.25 * ( mStartArrowHeadHeight * mStartArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) ); double stopMarkerMargin = std::sqrt( 0.25 * ( mStopArrowHeadHeight * mStopArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) ); - double markerMargin = qMax( startMarkerMargin, stopMarkerMargin ); - margin = qMax( mPen.widthF() / std::sqrt( 2.0 ), markerMargin ); + double markerMargin = std::max( startMarkerMargin, stopMarkerMargin ); + margin = std::max( mPen.widthF() / std::sqrt( 2.0 ), markerMargin ); } } return margin; @@ -408,7 +408,7 @@ double QgsComposerArrow::computeMarkerMargin() const void QgsComposerArrow::adaptItemSceneRect() { //rectangle containing start and end point - QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ), + QRectF rect = QRectF( std::min( mStartPoint.x(), mStopPoint.x() ), std::min( mStartPoint.y(), mStopPoint.y() ), std::fabs( mStopPoint.x() - mStartPoint.x() ), std::fabs( mStopPoint.y() - mStartPoint.y() ) ); double enlarge = computeMarkerMargin(); rect.adjust( -enlarge, -enlarge, enlarge, enlarge ); diff --git a/src/core/composer/qgscomposerattributetablemodelv2.cpp b/src/core/composer/qgscomposerattributetablemodelv2.cpp index 68786fb47e9..a393f9683dc 100644 --- a/src/core/composer/qgscomposerattributetablemodelv2.cpp +++ b/src/core/composer/qgscomposerattributetablemodelv2.cpp @@ -263,7 +263,7 @@ bool QgsComposerAttributeTableColumnModelV2::removeRows( int row, int count, con { Q_UNUSED( parent ); - int maxRow = qMin( row + count - 1, mComposerTable->columns()->length() - 1 ); + int maxRow = std::min( row + count - 1, mComposerTable->columns()->length() - 1 ); beginRemoveRows( QModelIndex(), row, maxRow ); //move backwards through rows, removing each corresponding QgsComposerTableColumn for ( int i = maxRow; i >= row; --i ) @@ -357,7 +357,7 @@ void QgsComposerAttributeTableColumnModelV2::setColumnAsSorted( QgsComposerTable QList::const_iterator columnIt = mComposerTable->columns()->constBegin(); for ( ; columnIt != mComposerTable->columns()->constEnd(); ++columnIt ) { - highestRank = qMax( highestRank, ( *columnIt )->sortByRank() ); + highestRank = std::max( highestRank, ( *columnIt )->sortByRank() ); } column->setSortByRank( highestRank + 1 ); diff --git a/src/core/composer/qgscomposerframe.cpp b/src/core/composer/qgscomposerframe.cpp index 72465973d74..b24a16dd348 100644 --- a/src/core/composer/qgscomposerframe.cpp +++ b/src/core/composer/qgscomposerframe.cpp @@ -160,8 +160,8 @@ void QgsComposerFrame::setSceneRect( const QRectF &rectangle ) //check minimum size QSizeF minSize = mMultiFrame->minFrameSize( frameIndex ); - fixedRect.setWidth( qMax( minSize.width(), fixedRect.width() ) ); - fixedRect.setHeight( qMax( minSize.height(), fixedRect.height() ) ); + fixedRect.setWidth( std::max( minSize.width(), fixedRect.width() ) ); + fixedRect.setHeight( std::max( minSize.height(), fixedRect.height() ) ); } QgsComposerItem::setSceneRect( fixedRect ); diff --git a/src/core/composer/qgscomposerhtml.cpp b/src/core/composer/qgscomposerhtml.cpp index 62a41844b21..0ee011e51d5 100644 --- a/src/core/composer/qgscomposerhtml.cpp +++ b/src/core/composer/qgscomposerhtml.cpp @@ -231,7 +231,7 @@ double QgsComposerHtml::maxFrameWidth() const QList::const_iterator frameIt = mFrameItems.constBegin(); for ( ; frameIt != mFrameItems.constEnd(); ++frameIt ) { - maxWidth = qMax( maxWidth, static_cast< double >( ( *frameIt )->boundingRect().width() ) ); + maxWidth = std::max( maxWidth, static_cast< double >( ( *frameIt )->boundingRect().width() ) ); } return maxWidth; @@ -379,7 +379,7 @@ double QgsComposerHtml::findNearbyPageBreak( double yPos ) bool previousPixelTransparent = false; QRgb pixelColor; QList< QPair > candidates; - int minRow = qMax( idealPos - maxSearchDistance, 0 ); + int minRow = std::max( idealPos - maxSearchDistance, 0 ); for ( int candidateRow = idealPos; candidateRow >= minRow; --candidateRow ) { changes = 0; diff --git a/src/core/composer/qgscomposerlabel.cpp b/src/core/composer/qgscomposerlabel.cpp index 20f1050cfd1..339745eb2e2 100644 --- a/src/core/composer/qgscomposerlabel.cpp +++ b/src/core/composer/qgscomposerlabel.cpp @@ -609,7 +609,7 @@ void QgsComposerLabel::itemShiftAdjustSize( double newWidth, double newHeight, d QUrl QgsComposerLabel::createStylesheetUrl() const { QString stylesheet; - stylesheet += QStringLiteral( "body { margin: %1 %2;" ).arg( qMax( mMarginY * mHtmlUnitsToMM, 0.0 ) ).arg( qMax( mMarginX * mHtmlUnitsToMM, 0.0 ) ); + stylesheet += QStringLiteral( "body { margin: %1 %2;" ).arg( std::max( mMarginY * mHtmlUnitsToMM, 0.0 ) ).arg( std::max( mMarginX * mHtmlUnitsToMM, 0.0 ) ); stylesheet += QgsFontUtils::asCSS( mFont, 0.352778 * mHtmlUnitsToMM ); stylesheet += QStringLiteral( "color: %1;" ).arg( mFontColor.name() ); stylesheet += QStringLiteral( "text-align: %1; }" ).arg( mHAlignment == Qt::AlignLeft ? "left" : mHAlignment == Qt::AlignRight ? "right" : "center" ); diff --git a/src/core/composer/qgscomposermap.cpp b/src/core/composer/qgscomposermap.cpp index 4e414da3c50..4210221712a 100644 --- a/src/core/composer/qgscomposermap.cpp +++ b/src/core/composer/qgscomposermap.cpp @@ -1584,10 +1584,10 @@ void QgsComposerMap::updateBoundingRect() if ( mGridStack ) mGridStack->calculateMaxGridExtension( topExtension, rightExtension, bottomExtension, leftExtension ); - topExtension = qMax( topExtension, frameExtension ); - rightExtension = qMax( rightExtension, frameExtension ); - bottomExtension = qMax( bottomExtension, frameExtension ); - leftExtension = qMax( leftExtension, frameExtension ); + topExtension = std::max( topExtension, frameExtension ); + rightExtension = std::max( rightExtension, frameExtension ); + bottomExtension = std::max( bottomExtension, frameExtension ); + leftExtension = std::max( leftExtension, frameExtension ); rectangle.setLeft( rectangle.left() - leftExtension ); rectangle.setRight( rectangle.right() + rightExtension ); diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index dad47f9dd1b..75f15b8fc3d 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -129,7 +129,7 @@ double QgsComposerMapGridStack::maxGridExtension() const double bottom = 0.0; double left = 0.0; calculateMaxGridExtension( top, right, bottom, left ); - return qMax( qMax( qMax( top, right ), bottom ), left ); + return std::max( std::max( std::max( top, right ), bottom ), left ); } void QgsComposerMapGridStack::calculateMaxGridExtension( double &top, double &right, double &bottom, double &left ) const @@ -149,10 +149,10 @@ void QgsComposerMapGridStack::calculateMaxGridExtension( double &top, double &ri double gridBottom = 0.0; double gridLeft = 0.0; grid->calculateMaxExtension( gridTop, gridRight, gridBottom, gridLeft ); - top = qMax( top, gridTop ); - right = qMax( right, gridRight ); - bottom = qMax( bottom, gridBottom ); - left = qMax( left, gridLeft ); + top = std::max( top, gridTop ); + right = std::max( right, gridRight ); + bottom = std::max( bottom, gridBottom ); + left = std::max( left, gridLeft ); } } } @@ -1149,7 +1149,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos += textWidth / 2.0; rotation = 270; if ( extension ) - extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight ); + extension->left = std::max( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight ); } else if ( mLeftGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending ) { @@ -1157,14 +1157,14 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos -= textWidth / 2.0; rotation = 90; if ( extension ) - extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight ); + extension->left = std::max( extension->left, mAnnotationFrameDistance + gridFrameDistance + textHeight ); } else { xpos -= ( textWidth + mAnnotationFrameDistance + gridFrameDistance ); ypos += textHeight / 2.0; if ( extension ) - extension->left = qMax( extension->left, mAnnotationFrameDistance + gridFrameDistance + textWidth ); + extension->left = std::max( extension->left, mAnnotationFrameDistance + gridFrameDistance + textWidth ); } } else @@ -1221,7 +1221,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos += textWidth / 2.0; rotation = 270; if ( extension ) - extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight ); + extension->right = std::max( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight ); } else if ( mRightGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending || mRightGridAnnotationDirection == QgsComposerMapGrid::BoundaryDirection ) { @@ -1229,14 +1229,14 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos -= textWidth / 2.0; rotation = 90; if ( extension ) - extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight ); + extension->right = std::max( extension->right, mAnnotationFrameDistance + gridFrameDistance + textHeight ); } else //Horizontal { xpos += ( mAnnotationFrameDistance + gridFrameDistance ); ypos += textHeight / 2.0; if ( extension ) - extension->right = qMax( extension->right, mAnnotationFrameDistance + gridFrameDistance + textWidth ); + extension->right = std::max( extension->right, mAnnotationFrameDistance + gridFrameDistance + textWidth ); } } else @@ -1292,7 +1292,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance ); xpos -= textWidth / 2.0; if ( extension ) - extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textHeight ); + extension->bottom = std::max( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textHeight ); } else if ( mBottomGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending ) { @@ -1300,7 +1300,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos += gridFrameDistance + mAnnotationFrameDistance; rotation = 90; if ( extension ) - extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth ); + extension->bottom = std::max( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth ); } else //Vertical { @@ -1308,7 +1308,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos += ( textWidth + mAnnotationFrameDistance + gridFrameDistance ); rotation = 270; if ( extension ) - extension->bottom = qMax( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth ); + extension->bottom = std::max( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textWidth ); } } else @@ -1364,7 +1364,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con xpos -= textWidth / 2.0; ypos -= ( mAnnotationFrameDistance + gridFrameDistance ); if ( extension ) - extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textHeight ); + extension->top = std::max( extension->top, mAnnotationFrameDistance + gridFrameDistance + textHeight ); } else if ( mTopGridAnnotationDirection == QgsComposerMapGrid::VerticalDescending ) { @@ -1372,7 +1372,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos -= textWidth + mAnnotationFrameDistance + gridFrameDistance; rotation = 90; if ( extension ) - extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth ); + extension->top = std::max( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth ); } else //Vertical { @@ -1380,7 +1380,7 @@ void QgsComposerMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, con ypos -= ( mAnnotationFrameDistance + gridFrameDistance ); rotation = 270; if ( extension ) - extension->top = qMax( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth ); + extension->top = std::max( extension->top, mAnnotationFrameDistance + gridFrameDistance + textWidth ); } } else @@ -1942,7 +1942,7 @@ QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( QPointF p return QgsComposerMapGrid::Left; } - double tolerance = qMax( mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.0, 1.0 ); + double tolerance = std::max( mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.0, 1.0 ); //check for corner coordinates if ( ( p.y() <= tolerance && p.x() <= tolerance ) // top left @@ -2049,7 +2049,7 @@ double QgsComposerMapGrid::maxExtension() double bottom = 0.0; double left = 0.0; calculateMaxExtension( top, right, bottom, left ); - return qMax( qMax( qMax( top, right ), bottom ), left ); + return std::max( std::max( std::max( top, right ), bottom ), left ); } void QgsComposerMapGrid::calculateMaxExtension( double &top, double &right, double &bottom, double &left ) diff --git a/src/core/composer/qgscomposertablev2.cpp b/src/core/composer/qgscomposertablev2.cpp index fc1d29dac42..3dd9a77e0d4 100644 --- a/src/core/composer/qgscomposertablev2.cpp +++ b/src/core/composer/qgscomposertablev2.cpp @@ -278,7 +278,7 @@ int QgsComposerTableV2::rowsVisible( double frameHeight, int firstRow, bool incl if ( includeEmptyRows && contentHeight > 0 ) { double rowHeight = ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont ); - currentRow += qMax( std::floor( contentHeight / rowHeight ), 0.0 ); + currentRow += std::max( std::floor( contentHeight / rowHeight ), 0.0 ); } return currentRow - firstRow - 1; @@ -320,9 +320,9 @@ QPair QgsComposerTableV2::rowRange( const int frameIndex ) const } //using zero based indexes - int firstVisible = qMin( rowsAlreadyShown, mTableContents.length() ); + int firstVisible = std::min( rowsAlreadyShown, mTableContents.length() ); int possibleRowsVisible = rowsVisible( frameIndex, rowsAlreadyShown, false ); - int lastVisible = qMin( firstVisible + possibleRowsVisible, mTableContents.length() ); + int lastVisible = std::min( firstVisible + possibleRowsVisible, mTableContents.length() ); return qMakePair( firstVisible, lastVisible ); } @@ -947,7 +947,7 @@ bool QgsComposerTableV2::calculateMaxColumnWidths() currentCellTextWidth = 0; Q_FOREACH ( const QString &line, multiLineSplit ) { - currentCellTextWidth = qMax( currentCellTextWidth, QgsComposerUtils::textWidthMM( mContentFont, line ) ); + currentCellTextWidth = std::max( currentCellTextWidth, QgsComposerUtils::textWidthMM( mContentFont, line ) ); } widths[ row * cols + col ] = currentCellTextWidth; } @@ -967,7 +967,7 @@ bool QgsComposerTableV2::calculateMaxColumnWidths() double maxColWidth = 0; for ( int row = 0; row < mTableContents.count() + 1; ++row ) { - maxColWidth = qMax( widths[ row * cols + col ], maxColWidth ); + maxColWidth = std::max( widths[ row * cols + col ], maxColWidth ); } mMaxColumnWidthMap.insert( col, maxColWidth ); } @@ -1024,7 +1024,7 @@ bool QgsComposerTableV2::calculateMaxRowHeights() double maxRowHeight = 0; for ( int col = 0; col < cols; ++col ) { - maxRowHeight = qMax( heights[ row * cols + col ], maxRowHeight ); + maxRowHeight = std::max( heights[ row * cols + col ], maxRowHeight ); } mMaxRowHeightMap.insert( row, maxRowHeight ); } @@ -1161,7 +1161,7 @@ bool QgsComposerTableV2::textRequiresWrapping( const QString &text, double colum double currentTextWidth = 0; Q_FOREACH ( const QString &line, multiLineSplit ) { - currentTextWidth = qMax( currentTextWidth, QgsComposerUtils::textWidthMM( font, line ) ); + currentTextWidth = std::max( currentTextWidth, QgsComposerUtils::textWidthMM( font, line ) ); } return ( currentTextWidth > columnWidth ); diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index 92c0c8c9b8c..2e03e15d440 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -1117,8 +1117,8 @@ QPointF QgsComposition::minPointFromXml( const QDomElement &elem ) const { continue; } - minX = qMin( minX, x ); - minY = qMin( minY, y ); + minX = std::min( minX, x ); + minY = std::min( minY, y ); } if ( minX < std::numeric_limits::max() ) { @@ -3238,13 +3238,13 @@ void QgsComposition::refreshPageSize( const QgsExpressionContext *context ) double heightD, widthD; if ( orientation == QgsComposition::Portrait ) { - heightD = qMax( pageHeight, pageWidth ); - widthD = qMin( pageHeight, pageWidth ); + heightD = std::max( pageHeight, pageWidth ); + widthD = std::min( pageHeight, pageWidth ); } else { - heightD = qMin( pageHeight, pageWidth ); - widthD = qMax( pageHeight, pageWidth ); + heightD = std::min( pageHeight, pageWidth ); + widthD = std::max( pageHeight, pageWidth ); } pageWidth = widthD; pageHeight = heightD; diff --git a/src/core/diagram/qgshistogramdiagram.cpp b/src/core/diagram/qgshistogramdiagram.cpp index 64e7b6d4fba..df964e1d716 100644 --- a/src/core/diagram/qgshistogramdiagram.cpp +++ b/src/core/diagram/qgshistogramdiagram.cpp @@ -52,7 +52,7 @@ QSizeF QgsHistogramDiagram::diagramSize( const QgsFeature &feature, const QgsRen Q_FOREACH ( const QString &cat, s.categoryAttributes ) { QgsExpression *expression = getExpression( cat, expressionContext ); - maxValue = qMax( expression->evaluate( &expressionContext ).toDouble(), maxValue ); + maxValue = std::max( expression->evaluate( &expressionContext ).toDouble(), maxValue ); } // Scale, if extension is smaller than the specified minimum @@ -108,7 +108,7 @@ QSizeF QgsHistogramDiagram::diagramSize( const QgsAttributes &attributes, const for ( int i = 0; i < attributes.count(); ++i ) { - maxValue = qMax( attributes.at( i ).toDouble(), maxValue ); + maxValue = std::max( attributes.at( i ).toDouble(), maxValue ); } switch ( s.diagramOrientation ) @@ -151,7 +151,7 @@ void QgsHistogramDiagram::renderDiagram( const QgsFeature &feature, QgsRenderCon QgsExpression *expression = getExpression( cat, expressionContext ); double currentVal = expression->evaluate( &expressionContext ).toDouble(); values.push_back( currentVal ); - maxValue = qMax( currentVal, maxValue ); + maxValue = std::max( currentVal, maxValue ); } double scaledMaxVal = sizePainterUnits( maxValue * mScaleFactor, s, c ); diff --git a/src/core/diagram/qgspiediagram.cpp b/src/core/diagram/qgspiediagram.cpp index 4c9910543a1..20bb11991a0 100644 --- a/src/core/diagram/qgspiediagram.cpp +++ b/src/core/diagram/qgspiediagram.cpp @@ -64,7 +64,7 @@ QSizeF QgsPieDiagram::diagramSize( const QgsFeature &feature, const QgsRenderCon double QgsPieDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const { QSizeF size = sizeForValue( value, s, is ); - return qMax( size.width(), size.height() ); + return std::max( size.width(), size.height() ); } QSizeF QgsPieDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) diff --git a/src/core/diagram/qgstextdiagram.cpp b/src/core/diagram/qgstextdiagram.cpp index 25881df4fe5..6ae1f9119cb 100644 --- a/src/core/diagram/qgstextdiagram.cpp +++ b/src/core/diagram/qgstextdiagram.cpp @@ -63,7 +63,7 @@ QSizeF QgsTextDiagram::diagramSize( const QgsFeature &feature, const QgsRenderCo double QgsTextDiagram::legendSize( double value, const QgsDiagramSettings &s, const QgsDiagramInterpolationSettings &is ) const { QSizeF size = sizeForValue( value, s, is ); - return qMax( size.width(), size.height() ); + return std::max( size.width(), size.height() ); } QSizeF QgsTextDiagram::diagramSize( const QgsAttributes &attributes, const QgsRenderContext &c, const QgsDiagramSettings &s ) diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index fe7d98f7be0..2300a6402a1 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -220,10 +220,10 @@ void QgsImageOperation::grayscaleLightnessOp( QRgb &rgb ) int green = qGreen( rgb ); int blue = qBlue( rgb ); - int min = qMin( qMin( red, green ), blue ); - int max = qMax( qMax( red, green ), blue ); + int min = std::min( std::min( red, green ), blue ); + int max = std::max( std::max( red, green ), blue ); - int lightness = qMin( ( min + max ) / 2, 255 ); + int lightness = std::min( ( min + max ) / 2, 255 ); rgb = qRgba( lightness, lightness, lightness, qAlpha( rgb ) ); } @@ -283,13 +283,13 @@ void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb, cons if ( mSaturation < 1.0 ) { // Lowering the saturation. Use a simple linear relationship - s = qMin( static_cast< int >( s * mSaturation ), 255 ); + s = std::min( static_cast< int >( s * mSaturation ), 255 ); } else if ( mSaturation > 1.0 ) { // Raising the saturation. Use a saturation curve to prevent // clipping at maximum saturation with ugly results. - s = qMin( static_cast< int >( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturation, 2 ) ) ) ), 255 ); + s = std::min( static_cast< int >( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturation, 2 ) ) ) ), 255 ); } if ( mColorize ) @@ -478,7 +478,7 @@ double QgsImageOperation::maxValueInDistanceTransformArray( const double *array, /* distance transform of 2d function using squared distance */ void QgsImageOperation::distanceTransform2d( double *im, int width, int height ) { - int maxDimension = qMax( width, height ); + int maxDimension = std::max( width, height ); double *f = new double[ maxDimension ]; int *v = new int[ maxDimension ]; @@ -836,8 +836,8 @@ QRect QgsImageOperation::nonTransparentImageRect( const QImage &image, QSize min if ( qAlpha( imgScanline[x] ) ) { ymax = y; - xmin = qMin( xmin, x ); - xmax = qMax( xmax, x ); + xmin = std::min( xmin, x ); + xmax = std::max( xmax, x ); found = true; break; } @@ -876,24 +876,24 @@ QRect QgsImageOperation::nonTransparentImageRect( const QImage &image, QSize min { if ( xmax - xmin < minSize.width() ) // centers image on x { - xmin = qMax( ( xmax + xmin ) / 2 - minSize.width() / 2, 0 ); + xmin = std::max( ( xmax + xmin ) / 2 - minSize.width() / 2, 0 ); xmax = xmin + minSize.width(); } if ( ymax - ymin < minSize.height() ) // centers image on y { - ymin = qMax( ( ymax + ymin ) / 2 - minSize.height() / 2, 0 ); + ymin = std::max( ( ymax + ymin ) / 2 - minSize.height() / 2, 0 ); ymax = ymin + minSize.height(); } } if ( center ) { // recompute min and max to center image - const int dx = qMax( std::abs( xmax - width / 2 ), std::abs( xmin - width / 2 ) ); - const int dy = qMax( std::abs( ymax - height / 2 ), std::abs( ymin - height / 2 ) ); - xmin = qMax( 0, width / 2 - dx ); - xmax = qMin( width, width / 2 + dx ); - ymin = qMax( 0, height / 2 - dy ); - ymax = qMin( height, height / 2 + dy ); + const int dx = std::max( std::abs( xmax - width / 2 ), std::abs( xmin - width / 2 ) ); + const int dy = std::max( std::abs( ymax - height / 2 ), std::abs( ymin - height / 2 ) ); + xmin = std::max( 0, width / 2 - dx ); + xmax = std::min( width, width / 2 + dx ); + ymin = std::max( 0, height / 2 - dy ); + ymax = std::min( height, height / 2 + dy ); } return QRect( xmin, ymin, xmax - xmin, ymax - ymin ); diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 3175353e361..0a9930b849b 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -1875,7 +1875,7 @@ static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContex if ( geom.isNull() ) return QVariant(); - int iterations = qMin( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 ); + int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 ); double offset = qBound( 0.0, QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.5 ); double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ); double maxAngle = qBound( 0.0, QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 180.0 ); diff --git a/src/core/geometry/qgsbox3d.cpp b/src/core/geometry/qgsbox3d.cpp index 0446cc23c4b..057049a8a75 100644 --- a/src/core/geometry/qgsbox3d.cpp +++ b/src/core/geometry/qgsbox3d.cpp @@ -26,8 +26,8 @@ QgsBox3d::QgsBox3d( double xmin, double ymin, double zmin, double xmax, double y QgsBox3d::QgsBox3d( const QgsPoint &p1, const QgsPoint &p2 ) : mBounds2d( p1.x(), p1.y(), p2.x(), p2.y() ) - , mZmin( qMin( p1.z(), p2.z() ) ) - , mZmax( qMax( p1.z(), p2.z() ) ) + , mZmin( std::min( p1.z(), p2.z() ) ) + , mZmax( std::max( p1.z(), p2.z() ) ) { mBounds2d.normalize(); } @@ -65,8 +65,8 @@ void QgsBox3d::setZMaximum( double z ) void QgsBox3d::normalize() { mBounds2d.normalize(); - double z1 = qMin( mZmin, mZmax ); - double z2 = qMax( mZmin, mZmax ); + double z1 = std::min( mZmin, mZmax ); + double z2 = std::max( mZmin, mZmax ); mZmin = z1; mZmax = z2; } @@ -74,8 +74,8 @@ void QgsBox3d::normalize() QgsBox3d QgsBox3d::intersect( const QgsBox3d &other ) const { QgsRectangle intersect2d = mBounds2d.intersect( &( other.mBounds2d ) ); - double zMin = qMax( mZmin, other.mZmin ); - double zMax = qMin( mZmax, other.mZmax ); + double zMin = std::max( mZmin, other.mZmin ); + double zMax = std::min( mZmax, other.mZmax ); return QgsBox3d( intersect2d.xMinimum(), intersect2d.yMinimum(), zMin, intersect2d.xMaximum(), intersect2d.yMaximum(), zMax ); } diff --git a/src/core/geometry/qgscircularstring.cpp b/src/core/geometry/qgscircularstring.cpp index 07ceada9178..ff23e35e20f 100644 --- a/src/core/geometry/qgscircularstring.cpp +++ b/src/core/geometry/qgscircularstring.cpp @@ -364,12 +364,12 @@ QgsLineString *QgsCircularString::curveToLine( double tolerance, SegmentationTol int QgsCircularString::numPoints() const { - return qMin( mX.size(), mY.size() ); + return std::min( mX.size(), mY.size() ); } QgsPoint QgsCircularString::pointN( int i ) const { - if ( qMin( mX.size(), mY.size() ) <= i ) + if ( std::min( mX.size(), mY.size() ) <= i ) { return QgsPoint(); } diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index 14ca2d20cf2..b7f754d2d22 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -315,7 +315,7 @@ QgsAbstractGeometry *QgsGeos::subdivide( int maxNodes, QString *errorMsg ) const } // minimum allowed max is 8 - maxNodes = qMax( maxNodes, 8 ); + maxNodes = std::max( maxNodes, 8 ); std::unique_ptr< QgsGeometryCollection > parts = QgsGeometryFactory::createCollectionOfType( mGeometry->wkbType() ); try diff --git a/src/core/geometry/qgsinternalgeometryengine.cpp b/src/core/geometry/qgsinternalgeometryengine.cpp index 9af2f8cbd5b..b6f74a28b34 100644 --- a/src/core/geometry/qgsinternalgeometryengine.cpp +++ b/src/core/geometry/qgsinternalgeometryengine.cpp @@ -169,7 +169,7 @@ QgsPoint surfacePoleOfInaccessibility( const QgsSurface *surface, double precisi QgsRectangle bounds = polygon->boundingBox(); // initial parameters - double cellSize = qMin( bounds.width(), bounds.height() ); + double cellSize = std::min( bounds.width(), bounds.height() ); if ( qgsDoubleNear( cellSize, 0.0 ) ) return QgsPoint( bounds.xMinimum(), bounds.yMinimum() ); @@ -341,7 +341,7 @@ double squareness( QgsLineString *ring, double lowerThreshold, double upperThres if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) ) continue; - sum += 2.0 * qMin( std::fabs( dotProduct - 1.0 ), qMin( std::fabs( dotProduct ), std::fabs( dotProduct + 1 ) ) ); + sum += 2.0 * std::min( std::fabs( dotProduct - 1.0 ), std::min( std::fabs( dotProduct ), std::fabs( dotProduct + 1 ) ) ); } a = b; b = c; @@ -360,7 +360,7 @@ QgsVector calcMotion( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c, return QgsVector( 0, 0 ); // 2.0 is a magic number from the original JOSM source code - double scale = 2.0 * qMin( p.length(), q.length() ); + double scale = 2.0 * std::min( p.length(), q.length() ); p = p.normalized(); q = q.normalized(); diff --git a/src/core/geometry/qgslinestring.cpp b/src/core/geometry/qgslinestring.cpp index 7e8f6cb7226..90f7959860e 100644 --- a/src/core/geometry/qgslinestring.cpp +++ b/src/core/geometry/qgslinestring.cpp @@ -79,7 +79,7 @@ QgsLineString::QgsLineString( const QVector &points ) QgsLineString::QgsLineString( const QVector &x, const QVector &y, const QVector &z, const QVector &m ) { mWkbType = QgsWkbTypes::LineString; - int pointCount = qMin( x.size(), y.size() ); + int pointCount = std::min( x.size(), y.size() ); if ( x.size() == pointCount ) { mX = x; diff --git a/src/core/geometry/qgspolygon.cpp b/src/core/geometry/qgspolygon.cpp index e449a5f6c11..8f6c9082ec0 100644 --- a/src/core/geometry/qgspolygon.cpp +++ b/src/core/geometry/qgspolygon.cpp @@ -288,7 +288,7 @@ double QgsPolygonV2::pointDistanceToBoundary( double x, double y ) const ( x < ( bX - aX ) * ( y - aY ) / ( bY - aY ) + aX ) ) inside = !inside; - minimumDistance = qMin( minimumDistance, QgsGeometryUtils::sqrDistToLine( x, y, aX, aY, bX, bY, minDistX, minDistY, 4 * DBL_EPSILON ) ); + minimumDistance = std::min( minimumDistance, QgsGeometryUtils::sqrDistToLine( x, y, aX, aY, bX, bY, minDistX, minDistY, 4 * DBL_EPSILON ) ); } } diff --git a/src/core/geometry/qgsrectangle.cpp b/src/core/geometry/qgsrectangle.cpp index 1c35879d65f..f553fe076a2 100644 --- a/src/core/geometry/qgsrectangle.cpp +++ b/src/core/geometry/qgsrectangle.cpp @@ -304,7 +304,7 @@ QString QgsRectangle::toString( int precision ) const precision = 0; if ( ( width() < 10 || height() < 10 ) && ( width() > 0 && height() > 0 ) ) { - precision = static_cast( std::ceil( -1.0 * std::log10( qMin( width(), height() ) ) ) ) + 1; + precision = static_cast( std::ceil( -1.0 * std::log10( std::min( width(), height() ) ) ) ) + 1; // sanity check if ( precision > 20 ) precision = 20; diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index 0fe0b054b15..70d385ed819 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -1541,7 +1541,7 @@ void QgsLayerTreeModel::invalidateLegendMapBasedData() { const QSize sz( n->minimumIconSize( context.get() ) ); const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() ); - widthMax[parentKey] = qMax( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 ); + widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 ); n->setIconSize( sz ); symbolNodes.append( n ); } diff --git a/src/core/layertree/qgslayertreemodellegendnode.cpp b/src/core/layertree/qgslayertreemodellegendnode.cpp index fc7528809f4..427d4544424 100644 --- a/src/core/layertree/qgslayertreemodellegendnode.cpp +++ b/src/core/layertree/qgslayertreemodellegendnode.cpp @@ -62,7 +62,7 @@ QgsLayerTreeModelLegendNode::ItemMetrics QgsLayerTreeModelLegendNode::draw( cons // itemHeight here is not really item height, it is only for symbol // vertical alignment purpose, i.e. OK take single line height // if there are more lines, thos run under the symbol - double itemHeight = qMax( static_cast< double >( settings.symbolSize().height() ), textHeight ); + double itemHeight = std::max( static_cast< double >( settings.symbolSize().height() ), textHeight ); ItemMetrics im; im.symbolSize = drawSymbol( settings, ctx, itemHeight ); @@ -101,7 +101,7 @@ QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &set { ctx->painter->setPen( settings.fontColor() ); - labelX = ctx->point.x() + qMax( static_cast< double >( symbolSize.width() ), ctx->labelXOffset ); + labelX = ctx->point.x() + std::max( static_cast< double >( symbolSize.width() ), ctx->labelXOffset ); labelY = ctx->point.y(); // Vertical alignment of label with symbol @@ -113,7 +113,7 @@ QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &set for ( QStringList::Iterator itemPart = lines.begin(); itemPart != lines.end(); ++itemPart ) { - labelSize.rwidth() = qMax( settings.textWidthMillimeters( symbolLabelFont, *itemPart ), double( labelSize.width() ) ); + labelSize.rwidth() = std::max( settings.textWidthMillimeters( symbolLabelFont, *itemPart ), double( labelSize.width() ) ); if ( ctx ) { @@ -420,8 +420,8 @@ QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemC p->restore(); } - return QSizeF( qMax( width + 2 * widthOffset, static_cast< double >( settings.symbolSize().width() ) ), - qMax( height + 2 * heightOffset, static_cast< double >( settings.symbolSize().height() ) ) ); + return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( settings.symbolSize().width() ) ), + std::max( height + 2 * heightOffset, static_cast< double >( settings.symbolSize().height() ) ) ); } diff --git a/src/core/layout/qgslayoutitempage.cpp b/src/core/layout/qgslayoutitempage.cpp index 0b4aafc4512..e9d37c834ea 100644 --- a/src/core/layout/qgslayoutitempage.cpp +++ b/src/core/layout/qgslayoutitempage.cpp @@ -153,8 +153,8 @@ void QgsLayoutItemPage::draw( QgsRenderContext &context, const QStyleOptionGraph //shadow painter->setBrush( QBrush( QColor( 150, 150, 150 ) ) ); painter->setPen( Qt::NoPen ); - painter->drawRect( pageRect.translated( qMin( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ), - qMin( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ) ) ); + painter->drawRect( pageRect.translated( std::min( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ), + std::min( scale * mLayout->pageCollection()->pageShadowWidth(), mMaximumShadowWidth ) ) ); //page area painter->setBrush( QColor( 215, 215, 215 ) ); diff --git a/src/core/layout/qgslayoutpagecollection.cpp b/src/core/layout/qgslayoutpagecollection.cpp index 00e73b3d9c8..3bdbc1cb560 100644 --- a/src/core/layout/qgslayoutpagecollection.cpp +++ b/src/core/layout/qgslayoutpagecollection.cpp @@ -60,7 +60,7 @@ double QgsLayoutPageCollection::maximumPageWidth() const double maxWidth = 0; Q_FOREACH ( QgsLayoutItemPage *page, mPages ) { - maxWidth = qMax( maxWidth, mLayout->convertToLayoutUnits( page->pageSize() ).width() ); + maxWidth = std::max( maxWidth, mLayout->convertToLayoutUnits( page->pageSize() ).width() ); } return maxWidth; } diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 1c2a5889c34..00cb25561c3 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -671,7 +671,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QListnbPoints - 1] = totalLineLength; straightSegmentLengths << currentStraightSegmentLength; straightSegmentAngles << QgsGeometryUtils::normalizedAngle( std::atan2( y[numberNodes - 1] - segmentStartY, x[numberNodes - 1] - segmentStartX ) ); - longestSegmentLength = qMax( longestSegmentLength, currentStraightSegmentLength ); + longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength ); double middleOfLine = totalLineLength / 2.0; if ( totalLineLength < labelWidth ) @@ -693,7 +693,7 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QListlayer()->pal->line_p ); + lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / mLF->layer()->pal->line_p ); double distanceToEndOfSegment = 0.0; int lastNodeInSegment = 0; @@ -861,7 +861,7 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList & if ( totalLineLength > labelWidth ) { - lineStepDistance = qMin( qMin( labelHeight, labelWidth ), lineStepDistance / mLF->layer()->pal->line_p ); + lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / mLF->layer()->pal->line_p ); } else // line length < label width => centering label position { @@ -1175,7 +1175,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos } QLinkedList positions; - double delta = qMax( li->label_height, total_distance / mLF->layer()->pal->line_p ); + double delta = std::max( li->label_height, total_distance / mLF->layer()->pal->line_p ); unsigned long flags = mLF->layer()->arrangementFlags(); if ( flags == 0 ) @@ -1225,7 +1225,7 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition * > &lPos { diff = std::fabs( tmp->getAlpha() - angle_last ); if ( diff > 2 * M_PI ) diff -= 2 * M_PI; - diff = qMin( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg... + diff = std::min( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg... angle_diff += diff; } @@ -1627,7 +1627,7 @@ void FeaturePart::addSizePenalty( int nbp, QList< LabelPosition * > &lPos, doubl QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) ); return; } - double bbox_length = qMax( bbx[2] - bbx[0], bby[2] - bby[0] ); + double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] ); if ( length >= bbox_length / 4 ) return; // the line is longer than quarter of height or width - don't penalize it diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index 270f3026bdb..268a875d76a 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -136,10 +136,10 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, for ( int i = 0; i < nbPoints; ++i ) { - xmin = qMin( xmin, x[i] ); - xmax = qMax( xmax, x[i] ); - ymin = qMin( ymin, y[i] ); - ymax = qMax( ymax, y[i] ); + xmin = std::min( xmin, x[i] ); + xmax = std::max( xmax, x[i] ); + ymin = std::min( ymin, y[i] ); + ymax = std::max( ymax, y[i] ); } } @@ -456,7 +456,7 @@ double LabelPosition::getDistanceToPoint( double xp, double yp ) const : std::sqrt( minDistanceToPoint( xp, yp ) ) ); if ( nextPart && distance > 0 ) - return qMin( distance, nextPart->getDistanceToPoint( xp, yp ) ); + return std::min( distance, nextPart->getDistanceToPoint( xp, yp ) ); return distance; } diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index 872bcf05a23..dc2ab605b06 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -508,7 +508,7 @@ void PointSet::splitPolygons( QLinkedList &shapes_toProcess, // we will cut the shapeu in two new shapes, one from [retainedPoint] to [newPoint] and one form [newPoint] to [retainedPoint] int imin = retainedPt; - int imax = ( ( ( fps < retainedPt && fpe < retainedPt ) || ( fps > retainedPt && fpe > retainedPt ) ) ? qMin( fps, fpe ) : qMax( fps, fpe ) ); + int imax = ( ( ( fps < retainedPt && fpe < retainedPt ) || ( fps > retainedPt && fpe > retainedPt ) ) ? std::min( fps, fpe ) : std::max( fps, fpe ) ); int nbPtSh1, nbPtSh2; // how many points in new shapes ? if ( imax > imin ) diff --git a/src/core/pal/rtree.hpp b/src/core/pal/rtree.hpp index b50b4a0a90e..4e29f062fbe 100644 --- a/src/core/pal/rtree.hpp +++ b/src/core/pal/rtree.hpp @@ -1093,8 +1093,8 @@ namespace pal for ( int index = 0; index < NUMDIMS; ++index ) { - newRect.m_min[index] = qMin( a_rectA->m_min[index], a_rectB->m_min[index] ); - newRect.m_max[index] = qMax( a_rectA->m_max[index], a_rectB->m_max[index] ); + newRect.m_min[index] = std::min( a_rectA->m_min[index], a_rectB->m_min[index] ); + newRect.m_max[index] = std::max( a_rectA->m_max[index], a_rectB->m_max[index] ); } return newRect; diff --git a/src/core/qgis.cpp b/src/core/qgis.cpp index e5e7350c94a..33e3d1fdbec 100644 --- a/src/core/qgis.cpp +++ b/src/core/qgis.cpp @@ -177,7 +177,7 @@ bool qgsVariantLessThan( const QVariant &lhs, const QVariant &rhs ) const QList &lhsl = lhs.toList(); const QList &rhsl = rhs.toList(); - int i, n = qMin( lhsl.size(), rhsl.size() ); + int i, n = std::min( lhsl.size(), rhsl.size() ); for ( i = 0; i < n && lhsl[i].type() == rhsl[i].type() && lhsl[i].isNull() == rhsl[i].isNull() && lhsl[i] == rhsl[i]; i++ ) ; @@ -192,7 +192,7 @@ bool qgsVariantLessThan( const QVariant &lhs, const QVariant &rhs ) const QStringList &lhsl = lhs.toStringList(); const QStringList &rhsl = rhs.toStringList(); - int i, n = qMin( lhsl.size(), rhsl.size() ); + int i, n = std::min( lhsl.size(), rhsl.size() ); for ( i = 0; i < n && lhsl[i] == rhsl[i]; i++ ) ; diff --git a/src/core/qgscolorramp.cpp b/src/core/qgscolorramp.cpp index b0920dac184..9cd24c55249 100644 --- a/src/core/qgscolorramp.cpp +++ b/src/core/qgscolorramp.cpp @@ -344,7 +344,7 @@ QColor QgsLimitedRandomColorRamp::color( double value ) const return QColor(); int colorCnt = mColors.count(); - int colorIdx = qMin( static_cast< int >( value * colorCnt ), colorCnt - 1 ); + int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 ); if ( colorIdx >= 0 && colorIdx < colorCnt ) return mColors.at( colorIdx ); @@ -378,12 +378,12 @@ QList QgsLimitedRandomColorRamp::randomColors( int count, QList colors; //normalize values - int safeHueMax = qMax( hueMin, hueMax ); - int safeHueMin = qMin( hueMin, hueMax ); - int safeSatMax = qMax( satMin, satMax ); - int safeSatMin = qMin( satMin, satMax ); - int safeValMax = qMax( valMin, valMax ); - int safeValMin = qMin( valMin, valMax ); + int safeHueMax = std::max( hueMin, hueMax ); + int safeHueMin = std::min( hueMin, hueMax ); + int safeSatMax = std::max( satMin, satMax ); + int safeSatMin = std::min( satMin, satMax ); + int safeValMax = std::max( valMin, valMax ); + int safeValMin = std::min( valMin, valMax ); //start hue at random angle double currentHueAngle = 360.0 * static_cast< double >( qrand() ) / RAND_MAX; @@ -876,7 +876,7 @@ QColor QgsPresetSchemeColorRamp::color( double value ) const return QColor(); int colorCnt = mColors.count(); - int colorIdx = qMin( static_cast< int >( value * colorCnt ), colorCnt - 1 ); + int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 ); if ( colorIdx >= 0 && colorIdx < colorCnt ) return mColors.at( colorIdx ).first; diff --git a/src/core/qgsdatadefinedsizelegend.cpp b/src/core/qgsdatadefinedsizelegend.cpp index a56cb8ca5a3..8b221572a52 100644 --- a/src/core/qgsdatadefinedsizelegend.cpp +++ b/src/core/qgsdatadefinedsizelegend.cpp @@ -222,7 +222,7 @@ void QgsDataDefinedSizeLegend::drawCollapsedLegend( QgsRenderContext &context, Q int totalTextHeight = textBottomY - textTopY; int fullWidth = outputLargestSize + hLengthLine + hSpaceLineText + maxTextWidth; - int fullHeight = qMax( static_cast< int >( std::round( outputLargestSize ) ) - textTopY, totalTextHeight ); + int fullHeight = std::max( static_cast< int >( std::round( outputLargestSize ) ) - textTopY, totalTextHeight ); if ( outputSize ) *outputSize = QSize( fullWidth, fullHeight ); diff --git a/src/core/qgsdatetimestatisticalsummary.cpp b/src/core/qgsdatetimestatisticalsummary.cpp index a1caa9c0a3a..549f68f1518 100644 --- a/src/core/qgsdatetimestatisticalsummary.cpp +++ b/src/core/qgsdatetimestatisticalsummary.cpp @@ -103,7 +103,7 @@ void QgsDateTimeStatisticalSummary::testDateTime( const QDateTime &dateTime ) { if ( mMin.isValid() && dateTime.isValid() ) { - mMin = qMin( mMin, dateTime ); + mMin = std::min( mMin, dateTime ); } else if ( !mMin.isValid() && dateTime.isValid() ) { @@ -114,7 +114,7 @@ void QgsDateTimeStatisticalSummary::testDateTime( const QDateTime &dateTime ) { if ( mMax.isValid() && dateTime.isValid() ) { - mMax = qMax( mMax, dateTime ); + mMax = std::max( mMax, dateTime ); } else if ( !mMax.isValid() && dateTime.isValid() ) { diff --git a/src/core/qgsdiagramrenderer.cpp b/src/core/qgsdiagramrenderer.cpp index 9ca2279ec94..89083dd8bcb 100644 --- a/src/core/qgsdiagramrenderer.cpp +++ b/src/core/qgsdiagramrenderer.cpp @@ -381,7 +381,7 @@ void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc categoryElem.setAttribute( QStringLiteral( "minimumSize" ), QString::number( minimumSize ) ); categoryElem.setAttribute( QStringLiteral( "rotationOffset" ), QString::number( rotationOffset ) ); - int nCats = qMin( categoryColors.size(), categoryAttributes.size() ); + int nCats = std::min( categoryColors.size(), categoryAttributes.size() ); for ( int i = 0; i < nCats; ++i ) { QDomElement attributeElem = doc.createElement( QStringLiteral( "attribute" ) ); diff --git a/src/core/qgslegendrenderer.cpp b/src/core/qgslegendrenderer.cpp index 94ca381b7a7..2bb96560574 100644 --- a/src/core/qgslegendrenderer.cpp +++ b/src/core/qgslegendrenderer.cpp @@ -59,7 +59,7 @@ QSizeF QgsLegendRenderer::paintAndDetermineSize( QPainter *painter ) { Q_FOREACH ( const Atom &atom, atomList ) { - maxColumnWidth = qMax( atom.size.width(), maxColumnWidth ); + maxColumnWidth = std::max( atom.size.width(), maxColumnWidth ); } } @@ -98,10 +98,10 @@ QSizeF QgsLegendRenderer::paintAndDetermineSize( QPainter *painter ) } QSizeF atomSize = drawAtom( atom, painter, point ); - columnWidth = qMax( atomSize.width(), columnWidth ); + columnWidth = std::max( atomSize.width(), columnWidth ); point.ry() += atom.size.height(); - columnMaxHeight = qMax( point.y() - columnTop, columnMaxHeight ); + columnMaxHeight = std::max( point.y() - columnTop, columnMaxHeight ); firstInColumn = false; } @@ -111,14 +111,14 @@ QSizeF QgsLegendRenderer::paintAndDetermineSize( QPainter *painter ) size.rwidth() = point.x(); if ( !mSettings.title().isEmpty() ) { - size.rwidth() = qMax( titleSize.width(), size.width() ); + size.rwidth() = std::max( titleSize.width(), size.width() ); } // override the size if it was set by the user if ( mLegendSize.isValid() ) { - qreal w = qMax( size.width(), mLegendSize.width() ); - qreal h = qMax( size.height(), mLegendSize.height() ); + qreal w = std::max( size.width(), mLegendSize.width() ); + qreal h = std::max( size.height(), mLegendSize.height() ); size = QSizeF( w, h ); } @@ -174,7 +174,7 @@ QList QgsLegendRenderer::createAtomList( QgsLayerTreeGr // Prepend this group title to the first atom groupAtoms[0].nucleons.prepend( nucleon ); groupAtoms[0].size.rheight() += nucleon.size.height(); - groupAtoms[0].size.rwidth() = qMax( nucleon.size.width(), groupAtoms[0].size.width() ); + groupAtoms[0].size.rwidth() = std::max( nucleon.size.width(), groupAtoms[0].size.width() ); } else { @@ -183,7 +183,7 @@ QList QgsLegendRenderer::createAtomList( QgsLayerTreeGr atom.nucleons.append( nucleon ); atom.size.rwidth() += nucleon.size.width(); atom.size.rheight() += nucleon.size.height(); - atom.size.rwidth() = qMax( nucleon.size.width(), atom.size.width() ); + atom.size.rwidth() = std::max( nucleon.size.width(), atom.size.width() ); groupAtoms.append( atom ); } } @@ -230,7 +230,7 @@ QList QgsLegendRenderer::createAtomList( QgsLayerTreeGr { // append to layer atom // the width is not correct at this moment, we must align all symbol labels - atom.size.rwidth() = qMax( symbolNucleon.size.width(), atom.size.width() ); + atom.size.rwidth() = std::max( symbolNucleon.size.width(), atom.size.width() ); // Add symbol space only if there is already title or another item above if ( !atom.nucleons.isEmpty() ) { @@ -269,7 +269,7 @@ void QgsLegendRenderer::setColumns( QList &atomList ) { totalHeight += spaceAboveAtom( atom ); totalHeight += atom.size.height(); - maxAtomHeight = qMax( atom.size.height(), maxAtomHeight ); + maxAtomHeight = std::max( atom.size.height(), maxAtomHeight ); } // We know height of each atom and we have to split them into columns @@ -320,7 +320,7 @@ void QgsLegendRenderer::setColumns( QList &atomList ) } atomList[i].column = currentColumn; currentColumnAtomCount++; - maxColumnHeight = qMax( currentColumnHeight, maxColumnHeight ); + maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight ); } // Align labels of symbols for each layr/column to the same labelXOffset @@ -333,7 +333,7 @@ void QgsLegendRenderer::setColumns( QList &atomList ) if ( QgsLayerTreeModelLegendNode *legendNode = qobject_cast( atom.nucleons.at( j ).item ) ) { QString key = QStringLiteral( "%1-%2" ).arg( reinterpret_cast< qulonglong >( legendNode->layerNode() ) ).arg( atom.column ); - maxSymbolWidth[key] = qMax( atom.nucleons.at( j ).symbolSize.width(), maxSymbolWidth[key] ); + maxSymbolWidth[key] = std::max( atom.nucleons.at( j ).symbolSize.width(), maxSymbolWidth[key] ); } } } @@ -377,7 +377,7 @@ QSizeF QgsLegendRenderer::drawTitle( QPainter *painter, QPointF point, Qt::Align switch ( halignment ) { case Qt::AlignHCenter: - textBoxWidth = ( qMin( static_cast< double >( point.x() ), legendWidth - point.x() ) - mSettings.boxSpace() ) * 2.0; + textBoxWidth = ( std::min( static_cast< double >( point.x() ), legendWidth - point.x() ) - mSettings.boxSpace() ) * 2.0; textBoxLeft = point.x() - textBoxWidth / 2.; break; case Qt::AlignRight: @@ -408,7 +408,7 @@ QSizeF QgsLegendRenderer::drawTitle( QPainter *painter, QPointF point, Qt::Align } //update max width of title - size.rwidth() = qMax( width, size.rwidth() ); + size.rwidth() = std::max( width, size.rwidth() ); y += height; if ( titlePart != ( lines.end() - 1 ) ) @@ -486,7 +486,7 @@ QSizeF QgsLegendRenderer::drawAtom( const Atom &atom, QPainter *painter, QPointF Nucleon symbolNucleon = drawSymbolItem( legendNode, painter, point, nucleon.labelXOffset ); // expand width, it may be wider because of labelXOffset - size.rwidth() = qMax( symbolNucleon.size.width(), size.width() ); + size.rwidth() = std::max( symbolNucleon.size.width(), size.width() ); } point.ry() += nucleon.size.height(); first = false; @@ -509,8 +509,8 @@ QgsLegendRenderer::Nucleon QgsLegendRenderer::drawSymbolItem( QgsLayerTreeModelL nucleon.symbolSize = im.symbolSize; nucleon.labelSize = im.labelSize; //QgsDebugMsg( QString( "symbol height = %1 label height = %2").arg( symbolSize.height()).arg( labelSize.height() )); - double width = qMax( static_cast< double >( im.symbolSize.width() ), labelXOffset ) + im.labelSize.width(); - double height = qMax( im.symbolSize.height(), im.labelSize.height() ); + double width = std::max( static_cast< double >( im.symbolSize.width() ), labelXOffset ) + im.labelSize.width(); + double height = std::max( im.symbolSize.height(), im.labelSize.height() ); nucleon.size = QSizeF( width, height ); return nucleon; } @@ -536,7 +536,7 @@ QSizeF QgsLegendRenderer::drawLayerTitle( QgsLayerTreeLayer *nodeLayer, QPainter y += mSettings.fontAscentMillimeters( layerFont ); if ( painter ) mSettings.drawText( painter, point.x(), y, *layerItemPart, layerFont ); qreal width = mSettings.textWidthMillimeters( layerFont, *layerItemPart ); - size.rwidth() = qMax( width, size.width() ); + size.rwidth() = std::max( width, size.width() ); if ( layerItemPart != ( lines.end() - 1 ) ) { y += mSettings.lineSpacing(); @@ -565,7 +565,7 @@ QSizeF QgsLegendRenderer::drawGroupTitle( QgsLayerTreeGroup *nodeGroup, QPainter y += mSettings.fontAscentMillimeters( groupFont ); if ( painter ) mSettings.drawText( painter, point.x(), y, *groupPart, groupFont ); qreal width = mSettings.textWidthMillimeters( groupFont, *groupPart ); - size.rwidth() = qMax( width, size.width() ); + size.rwidth() = std::max( width, size.width() ); if ( groupPart != ( lines.end() - 1 ) ) { y += mSettings.lineSpacing(); diff --git a/src/core/qgsmaptopixelgeometrysimplifier.cpp b/src/core/qgsmaptopixelgeometrysimplifier.cpp index 43c1bfd8190..6ea505a442c 100644 --- a/src/core/qgsmaptopixelgeometrysimplifier.cpp +++ b/src/core/qgsmaptopixelgeometrysimplifier.cpp @@ -329,7 +329,7 @@ QgsGeometry QgsMapToPixelSimplifier::simplify( const QgsGeometry &geometry ) con } const QgsRectangle envelope = geometry.boundingBox(); - if ( qMax( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 ) + if ( std::max( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 ) { //points are in average too far apart to lead to any significant simplification return geometry; diff --git a/src/core/qgsmapunitscale.cpp b/src/core/qgsmapunitscale.cpp index 4e071711b9d..76a323b5bb2 100644 --- a/src/core/qgsmapunitscale.cpp +++ b/src/core/qgsmapunitscale.cpp @@ -23,11 +23,11 @@ double QgsMapUnitScale::computeMapUnitsPerPixel( const QgsRenderContext &c ) con double renderScale = c.rendererScale(); if ( !qgsDoubleNear( minScale, 0 ) ) { - mup = qMin( mup / ( renderScale / minScale ), mup ); + mup = std::min( mup / ( renderScale / minScale ), mup ); } if ( !qgsDoubleNear( maxScale, 0 ) ) { - mup = qMax( mup / ( renderScale / maxScale ), mup ); + mup = std::max( mup / ( renderScale / maxScale ), mup ); } return mup; } diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 2edde7d50fb..55e39934fd5 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -1876,7 +1876,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent //this makes labels align to the font's baseline or highest character - double topMargin = qMax( 0.25 * labelFontMetrics->ascent(), 0.0 ); + double topMargin = std::max( 0.25 * labelFontMetrics->ascent(), 0.0 ); double bottomMargin = 1.0 + labelFontMetrics->descent(); QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin ); vm *= xform->mapUnitsPerPixel(); @@ -1921,7 +1921,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature &f, QgsRenderContext &cont // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours if ( placement == QgsPalLayerSettings::Line || placement == QgsPalLayerSettings::Curved || placement == QgsPalLayerSettings::PerimeterCurved ) { - distance = qMax( distance, 1.0 ); + distance = std::max( distance, 1.0 ); } if ( !qgsDoubleNear( distance, 0.0 ) ) diff --git a/src/core/qgspointxy.cpp b/src/core/qgspointxy.cpp index f98798337c3..d416e400ef5 100644 --- a/src/core/qgspointxy.cpp +++ b/src/core/qgspointxy.cpp @@ -97,7 +97,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix //make sure rounding to specified precision doesn't create seconds >= 60 if ( std::round( mySecondsX * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { - mySecondsX = qMax( mySecondsX - 60, 0.0 ); + mySecondsX = std::max( mySecondsX - 60, 0.0 ); myIntMinutesX++; if ( myIntMinutesX >= 60 ) { @@ -107,7 +107,7 @@ QString QgsPointXY::toDegreesMinutesSeconds( int precision, const bool useSuffix } if ( std::round( mySecondsY * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { - mySecondsY = qMax( mySecondsY - 60, 0.0 ); + mySecondsY = std::max( mySecondsY - 60, 0.0 ); myIntMinutesY++; if ( myIntMinutesY >= 60 ) { @@ -195,12 +195,12 @@ QString QgsPointXY::toDegreesMinutes( int precision, const bool useSuffix, const //make sure rounding to specified precision doesn't create minutes >= 60 if ( std::round( myFloatMinutesX * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { - myFloatMinutesX = qMax( myFloatMinutesX - 60, 0.0 ); + myFloatMinutesX = std::max( myFloatMinutesX - 60, 0.0 ); myDegreesX++; } if ( std::round( myFloatMinutesY * std::pow( 10.0, precision ) ) >= 60 * std::pow( 10.0, precision ) ) { - myFloatMinutesY = qMax( myFloatMinutesY - 60, 0.0 ); + myFloatMinutesY = std::max( myFloatMinutesY - 60, 0.0 ); myDegreesY++; } diff --git a/src/core/qgsrenderchecker.cpp b/src/core/qgsrenderchecker.cpp index 66f4c29b55f..1f65e9084f3 100644 --- a/src/core/qgsrenderchecker.cpp +++ b/src/core/qgsrenderchecker.cpp @@ -338,7 +338,7 @@ bool QgsRenderChecker::compareImages( const QString &testName, int imgHeight = 280; if ( ! myExpectedImage.isNull() ) { - imgWidth = qMin( myExpectedImage.width(), imgWidth ); + imgWidth = std::min( myExpectedImage.width(), imgWidth ); imgHeight = myExpectedImage.height() * imgWidth / myExpectedImage.width(); } @@ -404,8 +404,8 @@ bool QgsRenderChecker::compareImages( const QString &testName, // dissimilar pixel values there are // - int maxHeight = qMin( myExpectedImage.height(), myResultImage.height() ); - int maxWidth = qMin( myExpectedImage.width(), myResultImage.width() ); + int maxHeight = std::min( myExpectedImage.height(), myResultImage.height() ); + int maxWidth = std::min( myExpectedImage.width(), myResultImage.width() ); mMismatchCount = 0; int colorTolerance = static_cast< int >( mColorTolerance ); @@ -419,7 +419,7 @@ bool QgsRenderChecker::compareImages( const QString &testName, for ( int x = 0; x < maxWidth; ++x ) { int maskTolerance = hasMask ? qRed( maskScanline[ x ] ) : 0; - int pixelTolerance = qMax( colorTolerance, maskTolerance ); + int pixelTolerance = std::max( colorTolerance, maskTolerance ); if ( pixelTolerance == 255 ) { //skip pixel diff --git a/src/core/qgsrendercontext.cpp b/src/core/qgsrendercontext.cpp index 2476c05adff..4ae8d78e481 100644 --- a/src/core/qgsrendercontext.cpp +++ b/src/core/qgsrendercontext.cpp @@ -274,9 +274,9 @@ double QgsRenderContext::convertToPainterUnits( double size, QgsUnitTypes::Rende { //check max/min size if ( scale.minSizeMMEnabled ) - convertedSize = qMax( convertedSize, scale.minSizeMM * mScaleFactor ); + convertedSize = std::max( convertedSize, scale.minSizeMM * mScaleFactor ); if ( scale.maxSizeMMEnabled ) - convertedSize = qMin( convertedSize, scale.maxSizeMM * mScaleFactor ); + convertedSize = std::min( convertedSize, scale.maxSizeMM * mScaleFactor ); } return convertedSize; @@ -304,9 +304,9 @@ double QgsRenderContext::convertToMapUnits( double size, QgsUnitTypes::RenderUni } if ( !qgsDoubleNear( scale.minScale, 0.0 ) ) { - minSizeMU = qMax( minSizeMU, size * ( mRendererScale / scale.minScale ) ); + minSizeMU = std::max( minSizeMU, size * ( mRendererScale / scale.minScale ) ); } - size = qMax( size, minSizeMU ); + size = std::max( size, minSizeMU ); double maxSizeMU = DBL_MAX; if ( scale.maxSizeMMEnabled ) @@ -315,9 +315,9 @@ double QgsRenderContext::convertToMapUnits( double size, QgsUnitTypes::RenderUni } if ( !qgsDoubleNear( scale.maxScale, 0.0 ) ) { - maxSizeMU = qMin( maxSizeMU, size * ( mRendererScale / scale.maxScale ) ); + maxSizeMU = std::min( maxSizeMU, size * ( mRendererScale / scale.maxScale ) ); } - size = qMin( size, maxSizeMU ); + size = std::min( size, maxSizeMU ); return size; } diff --git a/src/core/qgssnappingutils.cpp b/src/core/qgssnappingutils.cpp index 61d59535f44..91db8d4582e 100644 --- a/src/core/qgssnappingutils.cpp +++ b/src/core/qgssnappingutils.cpp @@ -265,7 +265,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPointXY &pointMap, if ( mSnappingConfig.intersectionSnapping() ) { edges << loc->edgesInRect( pointMap, tolerance ); - maxSnapIntTolerance = qMax( maxSnapIntTolerance, tolerance ); + maxSnapIntTolerance = std::max( maxSnapIntTolerance, tolerance ); } } } diff --git a/src/core/qgsstatisticalsummary.cpp b/src/core/qgsstatisticalsummary.cpp index c0b19ddcaa4..3ebb77f5c37 100644 --- a/src/core/qgsstatisticalsummary.cpp +++ b/src/core/qgsstatisticalsummary.cpp @@ -71,8 +71,8 @@ void QgsStatisticalSummary::addValue( double value ) { mCount++; mSum += value; - mMin = qMin( mMin, value ); - mMax = qMax( mMax, value ); + mMin = std::min( mMin, value ); + mMax = std::max( mMax, value ); if ( mStatistics & QgsStatisticalSummary::Majority || mStatistics & QgsStatisticalSummary::Minority || mStatistics & QgsStatisticalSummary::Variety ) mValueCount.insert( value, mValueCount.value( value, 0 ) + 1 ); diff --git a/src/core/qgsstringstatisticalsummary.cpp b/src/core/qgsstringstatisticalsummary.cpp index 2b544db3b4a..a44de78d10f 100644 --- a/src/core/qgsstringstatisticalsummary.cpp +++ b/src/core/qgsstringstatisticalsummary.cpp @@ -104,7 +104,7 @@ void QgsStringStatisticalSummary::testString( const QString &string ) { if ( !mMin.isEmpty() && !string.isEmpty() ) { - mMin = qMin( mMin, string ); + mMin = std::min( mMin, string ); } else if ( mMin.isEmpty() && !string.isEmpty() ) { @@ -115,7 +115,7 @@ void QgsStringStatisticalSummary::testString( const QString &string ) { if ( !mMax.isEmpty() && !string.isEmpty() ) { - mMax = qMax( mMax, string ); + mMax = std::max( mMax, string ); } else if ( mMax.isEmpty() && !string.isEmpty() ) { @@ -124,8 +124,8 @@ void QgsStringStatisticalSummary::testString( const QString &string ) } if ( mStatistics & MeanLength ) mSumLengths += string.length(); - mMinLength = qMin( mMinLength, string.length() ); - mMaxLength = qMax( mMaxLength, string.length() ); + mMinLength = std::min( mMinLength, string.length() ); + mMaxLength = std::max( mMaxLength, string.length() ); } QVariant QgsStringStatisticalSummary::statistic( QgsStringStatisticalSummary::Statistic stat ) const diff --git a/src/core/qgsstringutils.cpp b/src/core/qgsstringutils.cpp index 83a7ae944d3..d35d5ee63ac 100644 --- a/src/core/qgsstringutils.cpp +++ b/src/core/qgsstringutils.cpp @@ -135,7 +135,7 @@ int QgsStringUtils::levenshteinDistance( const QString &string1, const QString & s2Char = s2start; for ( int j = 0; j < length2; ++j ) { - col[j + 1] = qMin( qMin( 1 + col[j], 1 + prevCol[1 + j] ), prevCol[j] + ( ( *s1Char == *s2Char ) ? 0 : 1 ) ); + col[j + 1] = std::min( std::min( 1 + col[j], 1 + prevCol[1 + j] ), prevCol[j] + ( ( *s1Char == *s2Char ) ? 0 : 1 ) ); s2Char++; } col.swap( prevCol ); diff --git a/src/core/qgstextrenderer.cpp b/src/core/qgstextrenderer.cpp index 10ae5252f1e..a92a5e528f3 100644 --- a/src/core/qgstextrenderer.cpp +++ b/src/core/qgstextrenderer.cpp @@ -1911,7 +1911,7 @@ double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTex double maxWidth = 0; Q_FOREACH ( const QString &line, textLines ) { - maxWidth = qMax( maxWidth, fm->width( line ) ); + maxWidth = std::max( maxWidth, fm->width( line ) ); } return maxWidth; } @@ -2051,7 +2051,7 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer } else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer ) { - sizeOut = qMax( component.size.width(), component.size.height() ); + sizeOut = std::max( component.size.width(), component.size.height() ); double bufferSize = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() ); // add buffer diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 7afd65dfea4..47a904a036f 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -2472,7 +2472,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer *layer, { QVariant min = layer->minimumValue( i ); QVariant max = layer->maximumValue( i ); - if ( qMax( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < INT_MAX ) + if ( std::max( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < INT_MAX ) { fields[i].setType( QVariant::Int ); } diff --git a/src/core/qgsvectorlayereditbuffer.cpp b/src/core/qgsvectorlayereditbuffer.cpp index c811aec2d19..d1d65692519 100644 --- a/src/core/qgsvectorlayereditbuffer.cpp +++ b/src/core/qgsvectorlayereditbuffer.cpp @@ -456,7 +456,7 @@ bool QgsVectorLayerEditBuffer::commitChanges( QStringList &commitErrors ) attributeChangesOk = false; // don't try attribute updates - they'll fail. } - for ( int i = 0; i < qMin( oldFields.count(), newFields.count() ); ++i ) + for ( int i = 0; i < std::min( oldFields.count(), newFields.count() ); ++i ) { QgsField oldField = oldFields.at( i ); QgsField newField = newFields.at( i ); diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp index 9d16d1089e5..c6e1b74c34c 100644 --- a/src/core/qgsvectorlayerlabeling.cpp +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -196,7 +196,7 @@ std::unique_ptr backgroundToMarkerLayer( const QgsTextBack layer->setEnabled( true ); // a marker does not have a size x and y, just a size (and it should be at least one) QSizeF size = settings.size(); - layer->setSize( qMax( 1., qMax( size.width(), size.height() ) ) ); + layer->setSize( std::max( 1., std::max( size.width(), size.height() ) ) ); layer->setSizeUnit( settings.sizeUnit() ); // fill and stroke QColor fillColor = settings.fillColor(); @@ -465,7 +465,7 @@ void QgsVectorLayerSimpleLabeling::toSld( QDomNode &parent, const QgsStringMap & if ( mSettings->maxCurvedCharAngleIn > 0 || mSettings->maxCurvedCharAngleOut > 0 ) { // SLD has no notion for this, the GeoTools ecosystem can only do a single angle - double angle = qMin( std::fabs( mSettings->maxCurvedCharAngleIn ), std::fabs( mSettings->maxCurvedCharAngleOut ) ); + double angle = std::min( std::fabs( mSettings->maxCurvedCharAngleIn ), std::fabs( mSettings->maxCurvedCharAngleOut ) ); QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "maxAngleDelta" ), qgsDoubleToString( angle ) ); textSymbolizerElement.appendChild( vo ); } diff --git a/src/core/raster/qgscolorrampshader.cpp b/src/core/raster/qgscolorrampshader.cpp index aaebc1f2be3..76cf8c91ae0 100644 --- a/src/core/raster/qgscolorrampshader.cpp +++ b/src/core/raster/qgscolorrampshader.cpp @@ -285,8 +285,8 @@ void QgsColorRampShader::classifyColorRamp( const int classes, const int band, c QVector::const_iterator color_it = entryColors.begin(); // calculate a reasonable number of decimals to display - double maxabs = std::log10( qMax( std::fabs( max ), std::fabs( min ) ) ); - int nDecimals = std::round( qMax( 3.0 + maxabs - std::log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); + double maxabs = std::log10( std::max( std::fabs( max ), std::fabs( min ) ) ); + int nDecimals = std::round( std::max( 3.0 + maxabs - std::log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); QList colorRampItems; for ( ; value_it != entryValues.end(); ++value_it, ++color_it ) diff --git a/src/core/raster/qgshillshaderenderer.cpp b/src/core/raster/qgshillshaderenderer.cpp index 4fd0cfa0adb..6243ef844eb 100644 --- a/src/core/raster/qgshillshaderenderer.cpp +++ b/src/core/raster/qgshillshaderenderer.cpp @@ -124,7 +124,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext double cellXSize = extent.width() / double( width ); double cellYSize = extent.height() / double( height ); - double zenithRad = qMax( 0.0, 90 - mLightAngle ) * M_PI / 180.0; + double zenithRad = std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0; double azimuthRad = -1 * mLightAzimuth * M_PI / 180.0; double cosZenithRad = std::cos( zenithRad ); double sinZenithRad = std::sin( zenithRad ); diff --git a/src/core/raster/qgshuesaturationfilter.cpp b/src/core/raster/qgshuesaturationfilter.cpp index 0f3dcd92c75..e5bda9d538f 100644 --- a/src/core/raster/qgshuesaturationfilter.cpp +++ b/src/core/raster/qgshuesaturationfilter.cpp @@ -301,13 +301,13 @@ void QgsHueSaturationFilter::processSaturation( int &r, int &g, int &b, int &h, if ( mSaturationScale < 1 ) { // Lowering the saturation. Use a simple linear relationship - s = qMin( ( int )( s * mSaturationScale ), 255 ); + s = std::min( ( int )( s * mSaturationScale ), 255 ); } else { // Raising the saturation. Use a saturation curve to prevent // clipping at maximum saturation with ugly results. - s = qMin( ( int )( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturationScale, 2 ) ) ) ), 255 ); + s = std::min( ( int )( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturationScale, 2 ) ) ) ), 255 ); } // Saturation changed, so update rgb values diff --git a/src/core/raster/qgsrasterblock.cpp b/src/core/raster/qgsrasterblock.cpp index c12ae7d918b..e10d7691761 100644 --- a/src/core/raster/qgsrasterblock.cpp +++ b/src/core/raster/qgsrasterblock.cpp @@ -462,10 +462,10 @@ bool QgsRasterBlock::setIsNoDataExcept( QRect exceptRect ) int bottom = exceptRect.bottom(); int left = exceptRect.left(); int right = exceptRect.right(); - top = qMin( qMax( top, 0 ), mHeight - 1 ); - left = qMin( qMax( left, 0 ), mWidth - 1 ); - bottom = qMax( 0, qMin( bottom, mHeight - 1 ) ); - right = qMax( 0, qMin( right, mWidth - 1 ) ); + top = std::min( std::max( top, 0 ), mHeight - 1 ); + left = std::min( std::max( left, 0 ), mWidth - 1 ); + bottom = std::max( 0, std::min( bottom, mHeight - 1 ) ); + right = std::max( 0, std::min( right, mWidth - 1 ) ); QgsDebugMsgLevel( "Entered", 4 ); if ( typeIsNumeric( mDataType ) ) @@ -660,12 +660,12 @@ void QgsRasterBlock::setData( const QByteArray &data, int offset ) if ( mData ) { - int len = qMin( data.size(), typeSize( mDataType ) * mWidth * mHeight - offset ); + int len = std::min( data.size(), typeSize( mDataType ) * mWidth * mHeight - offset ); ::memcpy( static_cast( mData ) + offset, data.constData(), len ); } else if ( mImage && mImage->constBits() ) { - int len = qMin( data.size(), mImage->byteCount() - offset ); + int len = std::min( data.size(), mImage->byteCount() - offset ); ::memcpy( mImage->bits() + offset, data.constData(), len ); } } diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index a402b8781ec..e0b3e91e3c9 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -77,8 +77,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); - tmpXRes = qMax( providerXRes, xRes ); - tmpYRes = qMax( providerYRes, yRes ); + tmpXRes = std::max( providerXRes, xRes ); + tmpYRes = std::max( providerYRes, yRes ); if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index 15f9b3abcb1..0a17c6903ca 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -171,8 +171,8 @@ QgsRasterBandStats QgsRasterInterface::bandStatistics( int bandNo, return myRasterBandStats; QgsDebugMsgLevel( QString( "myYBlock = %1 myXBlock = %2" ).arg( myYBlock ).arg( myXBlock ), 4 ); - int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize ); - int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize ); + int myBlockWidth = std::min( myXBlockSize, myWidth - myXBlock * myXBlockSize ); + int myBlockHeight = std::min( myYBlockSize, myHeight - myYBlock * myYBlockSize ); double xmin = myExtent.xMinimum() + myXBlock * myXBlockSize * myXRes; double xmax = xmin + myBlockWidth * myXRes; @@ -456,8 +456,8 @@ QgsRasterHistogram QgsRasterInterface::histogram( int bandNo, if ( feedback && feedback->isCanceled() ) return myHistogram; - int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize ); - int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize ); + int myBlockWidth = std::min( myXBlockSize, myWidth - myXBlock * myXBlockSize ); + int myBlockHeight = std::min( myYBlockSize, myHeight - myYBlock * myYBlockSize ); double xmin = myExtent.xMinimum() + myXBlock * myXBlockSize * myXRes; double xmax = xmin + myBlockWidth * myXRes; @@ -498,7 +498,7 @@ QgsRasterHistogram QgsRasterInterface::histogram( int bandNo, #ifdef QGISDEBUG QString hist; - for ( int i = 0; i < qMin( myHistogram.histogramVector.size(), 500 ); i++ ) + for ( int i = 0; i < std::min( myHistogram.histogramVector.size(), 500 ); i++ ) { hist += QString::number( myHistogram.histogramVector.value( i ) ) + ' '; } diff --git a/src/core/raster/qgsrasteriterator.cpp b/src/core/raster/qgsrasteriterator.cpp index 137f6f16be3..6c8635e9f09 100644 --- a/src/core/raster/qgsrasteriterator.cpp +++ b/src/core/raster/qgsrasteriterator.cpp @@ -92,8 +92,8 @@ bool QgsRasterIterator::readNextRasterPart( int bandNumber, } //read data block - nCols = qMin( mMaximumTileWidth, pInfo.nCols - pInfo.currentCol ); - nRows = qMin( mMaximumTileHeight, pInfo.nRows - pInfo.currentRow ); + nCols = std::min( mMaximumTileWidth, pInfo.nCols - pInfo.currentCol ); + nRows = std::min( mMaximumTileHeight, pInfo.nRows - pInfo.currentRow ); QgsDebugMsgLevel( QString( "nCols = %1 nRows = %2" ).arg( nCols ).arg( nRows ), 4 ); //get subrectangle diff --git a/src/core/raster/qgsrasterpipe.cpp b/src/core/raster/qgsrasterpipe.cpp index 16b0b7824c1..7dbbe1ddb16 100644 --- a/src/core/raster/qgsrasterpipe.cpp +++ b/src/core/raster/qgsrasterpipe.cpp @@ -204,19 +204,19 @@ bool QgsRasterPipe::set( QgsRasterInterface *interface ) } else if ( role == BrightnessRole ) { - idx = qMax( providerIdx, rendererIdx ) + 1; + idx = std::max( providerIdx, rendererIdx ) + 1; } else if ( role == HueSaturationRole ) { - idx = qMax( qMax( providerIdx, rendererIdx ), brightnessIdx ) + 1; + idx = std::max( std::max( providerIdx, rendererIdx ), brightnessIdx ) + 1; } else if ( role == ResamplerRole ) { - idx = qMax( qMax( qMax( providerIdx, rendererIdx ), brightnessIdx ), hueSaturationIdx ) + 1; + idx = std::max( std::max( std::max( providerIdx, rendererIdx ), brightnessIdx ), hueSaturationIdx ) + 1; } else if ( role == ProjectorRole ) { - idx = qMax( qMax( qMax( qMax( providerIdx, rendererIdx ), brightnessIdx ), hueSaturationIdx ), resamplerIdx ) + 1; + idx = std::max( std::max( std::max( std::max( providerIdx, rendererIdx ), brightnessIdx ), hueSaturationIdx ), resamplerIdx ) + 1; } return insert( idx, interface ); // insert may still fail and return false diff --git a/src/core/simplify/effectivearea.h b/src/core/simplify/effectivearea.h index 3e7f5fb48e6..ce6830f9dd1 100644 --- a/src/core/simplify/effectivearea.h +++ b/src/core/simplify/effectivearea.h @@ -34,7 +34,7 @@ #define LWDEBUG // #define LWDEBUGF // -#define FP_MAX qMax +#define FP_MAX std::max #define FLAGS_GET_Z( flags ) ( ( flags ) & 0x01 ) #define LW_MSG_MAXLEN 256 #define lwalloc qgsMalloc diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index 33692f47e08..1a8c39f29a7 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -395,7 +395,7 @@ QgsSymbolLayer *QgsSimpleFillSymbolLayer::createFromSld( QDomElement &element ) double QgsSimpleFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { double penBleed = context.convertToPainterUnits( mStrokeStyle == Qt::NoPen ? 0 : ( mStrokeWidth / 2.0 ), mStrokeWidthUnit, mStrokeWidthMapUnitScale ); - double offsetBleed = context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return penBleed + offsetBleed; } @@ -916,7 +916,7 @@ QgsGradientFillSymbolLayer *QgsGradientFillSymbolLayer::clone() const double QgsGradientFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - double offsetBleed = context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return offsetBleed; } @@ -1320,7 +1320,7 @@ void QgsShapeburstFillSymbolLayer::distanceTransform1d( double *f, int n, int *v /* distance transform of 2d function using squared distance */ void QgsShapeburstFillSymbolLayer::distanceTransform2d( double *im, int width, int height ) { - int maxDimension = qMax( width, height ); + int maxDimension = std::max( width, height ); double *f = new double[ maxDimension ]; int *v = new int[ maxDimension ]; double *z = new double[ maxDimension + 1 ]; @@ -1444,7 +1444,7 @@ void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, Q //scale result to fit in the range [0, 1] if ( maxDistanceValue > 0 ) { - pixVal = squaredVal > 0 ? qMin( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0; + pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0; } else { @@ -1512,7 +1512,7 @@ QgsShapeburstFillSymbolLayer *QgsShapeburstFillSymbolLayer::clone() const double QgsShapeburstFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - double offsetBleed = context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); return offsetBleed; } @@ -2541,7 +2541,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & { QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i ); double outputPixelLayerBleed = layer->estimateMaxBleed( context.renderContext() ); - outputPixelBleed = qMax( outputPixelBleed, outputPixelLayerBleed ); + outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed ); QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast( layer ); if ( markerLineLayer ) @@ -2553,7 +2553,7 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & // big (multiplication of intervals in the worst case). // Because patterns without small common interval would look strange, we // believe that the longest interval should usually be sufficient. - outputPixelInterval = qMax( outputPixelInterval, outputPixelLayerInterval ); + outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval ); } } @@ -2617,11 +2617,11 @@ void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & // Add buffer based on bleed but keep precisely the height/width ratio (angle) // thus we add integer multiplications of width and height covering the bleed - int bufferMulti = qMax( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ); + int bufferMulti = std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ); // Always buffer at least once so that center of line marker in upper right corner // does not fall outside due to representation error - bufferMulti = qMax( bufferMulti, 1 ); + bufferMulti = std::max( bufferMulti, 1 ); int xBuffer = width * bufferMulti; int yBuffer = height * bufferMulti; @@ -3749,7 +3749,7 @@ QgsRasterFillSymbolLayer *QgsRasterFillSymbolLayer::clone() const double QgsRasterFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const { - return context.convertToPainterUnits( qMax( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); + return context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale ); } void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath ) diff --git a/src/core/symbology/qgsgraduatedsymbolrenderer.cpp b/src/core/symbology/qgsgraduatedsymbolrenderer.cpp index 5a60b2deb7b..b536040200b 100644 --- a/src/core/symbology/qgsgraduatedsymbolrenderer.cpp +++ b/src/core/symbology/qgsgraduatedsymbolrenderer.cpp @@ -629,8 +629,8 @@ static QList _calcStdDevBreaks( QList values, int classes, QList for ( int i = 0; i < n; i++ ) { mean += values[i]; - minimum = qMin( values[i], minimum ); // could use precomputed max and min - maximum = qMax( values[i], maximum ); // but have to go through entire list anyway + minimum = std::min( values[i], minimum ); // could use precomputed max and min + maximum = std::max( values[i], maximum ); // but have to go through entire list anyway } mean = mean / static_cast< double >( n ); @@ -688,7 +688,7 @@ static QList _calcJenksBreaks( QList values, int classes, // is larger. This will produce a more representative sample for very large // layers, but could end up being computationally intensive... - sample.resize( qMax( maximumSize, values.size() / 10 ) ); + sample.resize( std::max( maximumSize, values.size() / 10 ) ); QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) ); QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) ); @@ -1251,7 +1251,7 @@ double QgsGraduatedSymbolRenderer::minSymbolSize() const sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size(); else if ( mRanges[i].symbol()->type() == QgsSymbol::Line ) sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width(); - min = qMin( sz, min ); + min = std::min( sz, min ); } return min; } @@ -1266,7 +1266,7 @@ double QgsGraduatedSymbolRenderer::maxSymbolSize() const sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size(); else if ( mRanges[i].symbol()->type() == QgsSymbol::Line ) sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width(); - max = qMax( sz, max ); + max = std::max( sz, max ); } return max; } diff --git a/src/core/symbology/qgsheatmaprenderer.cpp b/src/core/symbology/qgsheatmaprenderer.cpp index 0ced32a6f34..78b01da6ced 100644 --- a/src/core/symbology/qgsheatmaprenderer.cpp +++ b/src/core/symbology/qgsheatmaprenderer.cpp @@ -156,9 +156,9 @@ bool QgsHeatmapRenderer::renderFeature( QgsFeature &feature, QgsRenderContext &c QgsPointXY pixel = context.mapToPixel().transform( *pointIt ); int pointX = pixel.x() / mRenderQuality; int pointY = pixel.y() / mRenderQuality; - for ( int x = qMax( pointX - mRadiusPixels, 0 ); x < qMin( pointX + mRadiusPixels, width ); ++x ) + for ( int x = std::max( pointX - mRadiusPixels, 0 ); x < std::min( pointX + mRadiusPixels, width ); ++x ) { - for ( int y = qMax( pointY - mRadiusPixels, 0 ); y < qMin( pointY + mRadiusPixels, height ); ++y ) + for ( int y = std::max( pointY - mRadiusPixels, 0 ); y < std::min( pointY + mRadiusPixels, height ); ++y ) { int index = y * width + x; if ( index >= mValues.count() ) @@ -250,7 +250,7 @@ void QgsHeatmapRenderer::renderImage( QgsRenderContext &context ) for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex ) { //scale result to fit in the range [0, 1] - pixVal = mValues.at( idx ) > 0 ? qMin( ( mValues.at( idx ) / scaleMax ), 1.0 ) : 0; + pixVal = mValues.at( idx ) > 0 ? std::min( ( mValues.at( idx ) / scaleMax ), 1.0 ) : 0; //convert value to color from ramp pixColor = mGradientRamp->color( pixVal ); diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index ee7db666715..37208659c39 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -1234,7 +1234,7 @@ void QgsMarkerLineSymbolLayer::renderOffsetVertexAlongLine( const QPolygonF &poi int pointIncrement = distance > 0 ? 1 : -1; QPointF previousPoint = points[vertex]; - int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 ); + int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 ); int endPoint = distance > 0 ? points.count() - 1 : 0; double distanceLeft = std::fabs( distance ); diff --git a/src/core/symbology/qgspointdisplacementrenderer.cpp b/src/core/symbology/qgspointdisplacementrenderer.cpp index d929f0c7197..631d01dda2a 100644 --- a/src/core/symbology/qgspointdisplacementrenderer.cpp +++ b/src/core/symbology/qgspointdisplacementrenderer.cpp @@ -72,8 +72,8 @@ void QgsPointDisplacementRenderer::drawGroup( QPointF centerPoint, QgsRenderCont { if ( QgsMarkerSymbol *symbol = feature.symbol() ) { - diagonal = qMax( diagonal, context.convertToPainterUnits( M_SQRT2 * symbol->size(), - symbol->sizeUnit(), symbol->sizeMapUnitScale() ) ); + diagonal = std::max( diagonal, context.convertToPainterUnits( M_SQRT2 * symbol->size(), + symbol->sizeUnit(), symbol->sizeMapUnitScale() ) ); } } @@ -251,7 +251,7 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRe case Ring: { double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI ); - double radius = qMax( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits; + double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits; double fullPerimeter = 2 * M_PI; double angleStep = fullPerimeter / nPosition; @@ -278,9 +278,9 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRe double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0; while ( pointsRemaining > 0 ) { - double radiusCurrentRing = qMax( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 ); - int maxPointsCurrentRing = qMax( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 ); - int actualPointsCurrentRing = qMin( maxPointsCurrentRing, pointsRemaining ); + double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 ); + int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 ); + int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining ); double angleStep = 2 * M_PI / actualPointsCurrentRing; double currentAngle = 0.0; diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 32c13eb1c29..8b420fba078 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -3895,7 +3895,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, else { int U = 1; - cell = qMax( std::fabs( minimum ), std::fabs( maximum ) ); + cell = std::max( std::fabs( minimum ), std::fabs( maximum ) ); if ( adjustBias >= 1.5 * h + 0.5 ) { U = 1 + ( 1.0 / ( 1 + h ) ); @@ -3904,7 +3904,7 @@ QList QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, { U = 1 + ( 1.5 / ( 1 + adjustBias ) ); } - small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 ); + small = dx < ( cell * U * std::max( 1, divisions ) * 1e-07 * 3.0 ); } if ( small ) @@ -4124,7 +4124,7 @@ void QgsSymbolLayerUtils::mergeScaleDependencies( int mScaleMinDenom, int mScale if ( !ok || parentScaleMinDenom <= 0 ) props[ QStringLiteral( "scaleMinDenom" )] = QString::number( mScaleMinDenom ); else - props[ QStringLiteral( "scaleMinDenom" )] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) ); + props[ QStringLiteral( "scaleMinDenom" )] = QString::number( std::max( parentScaleMinDenom, mScaleMinDenom ) ); } if ( mScaleMaxDenom != 0 ) @@ -4134,7 +4134,7 @@ void QgsSymbolLayerUtils::mergeScaleDependencies( int mScaleMinDenom, int mScale if ( !ok || parentScaleMaxDenom <= 0 ) props[ QStringLiteral( "scaleMaxDenom" )] = QString::number( mScaleMaxDenom ); else - props[ QStringLiteral( "scaleMaxDenom" )] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) ); + props[ QStringLiteral( "scaleMaxDenom" )] = QString::number( std::min( parentScaleMaxDenom, mScaleMaxDenom ) ); } } diff --git a/src/gui/attributetable/qgsattributetablefiltermodel.cpp b/src/gui/attributetable/qgsattributetablefiltermodel.cpp index 5fa8affa12f..d6945fabb73 100644 --- a/src/gui/attributetable/qgsattributetablefiltermodel.cpp +++ b/src/gui/attributetable/qgsattributetablefiltermodel.cpp @@ -154,7 +154,7 @@ void QgsAttributeTableFilterModel::setAttributeTableConfig( const QgsAttributeTa int removedColumnCount = 0; // Check if there have a contiguous set of columns have been removed or if we require a full reset - for ( int i = 0; i < qMin( newColumnMapping.size(), mColumnMapping.size() - removedColumnCount ); ++i ) + for ( int i = 0; i < std::min( newColumnMapping.size(), mColumnMapping.size() - removedColumnCount ); ++i ) { if ( newColumnMapping.at( i ) == mColumnMapping.at( i + removedColumnCount ) ) continue; diff --git a/src/gui/attributetable/qgsattributetablemodel.cpp b/src/gui/attributetable/qgsattributetablemodel.cpp index c137a963da5..7df70d02b6e 100644 --- a/src/gui/attributetable/qgsattributetablemodel.cpp +++ b/src/gui/attributetable/qgsattributetablemodel.cpp @@ -546,7 +546,7 @@ int QgsAttributeTableModel::rowCount( const QModelIndex &parent ) const int QgsAttributeTableModel::columnCount( const QModelIndex &parent ) const { Q_UNUSED( parent ); - return qMax( 1, mFieldCount + mExtraColumns ); // if there are zero columns all model indices will be considered invalid + return std::max( 1, mFieldCount + mExtraColumns ); // if there are zero columns all model indices will be considered invalid } QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const diff --git a/src/gui/attributetable/qgsattributetableview.cpp b/src/gui/attributetable/qgsattributetableview.cpp index d0fdaae8b87..8b3400bc564 100644 --- a/src/gui/attributetable/qgsattributetableview.cpp +++ b/src/gui/attributetable/qgsattributetableview.cpp @@ -393,8 +393,8 @@ void QgsAttributeTableView::selectRow( int row, bool anchor ) command |= QItemSelectionModel::Current; } - QModelIndex tl = model()->index( qMin( mRowSectionAnchor, row ), 0 ); - QModelIndex br = model()->index( qMax( mRowSectionAnchor, row ), model()->columnCount() - 1 ); + QModelIndex tl = model()->index( std::min( mRowSectionAnchor, row ), 0 ); + QModelIndex br = model()->index( std::max( mRowSectionAnchor, row ), model()->columnCount() - 1 ); if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() ) setSelection( visualRect( tl ) | visualRect( br ), command ); else diff --git a/src/gui/attributetable/qgsfeaturelistview.cpp b/src/gui/attributetable/qgsfeaturelistview.cpp index abcc7f55265..bea56837b19 100644 --- a/src/gui/attributetable/qgsfeaturelistview.cpp +++ b/src/gui/attributetable/qgsfeaturelistview.cpp @@ -329,8 +329,8 @@ void QgsFeatureListView::selectRow( const QModelIndex &index, bool anchor ) command |= QItemSelectionModel::Current; } - QModelIndex tl = model()->index( qMin( mRowAnchor, row ), 0 ); - QModelIndex br = model()->index( qMax( mRowAnchor, row ), model()->columnCount() - 1 ); + QModelIndex tl = model()->index( std::min( mRowAnchor, row ), 0 ); + QModelIndex br = model()->index( std::max( mRowAnchor, row ), model()->columnCount() - 1 ); mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command ); } diff --git a/src/gui/attributetable/qgsfeaturelistviewdelegate.cpp b/src/gui/attributetable/qgsfeaturelistviewdelegate.cpp index 251e5394b86..ca8a952e428 100644 --- a/src/gui/attributetable/qgsfeaturelistviewdelegate.cpp +++ b/src/gui/attributetable/qgsfeaturelistviewdelegate.cpp @@ -67,7 +67,7 @@ QSize QgsFeatureListViewDelegate::sizeHint( const QStyleOptionViewItem &option, { Q_UNUSED( index ) int height = ICON_SIZE; - return QSize( option.rect.width(), qMax( height, option.fontMetrics.height() ) ); + return QSize( option.rect.width(), std::max( height, option.fontMetrics.height() ) ); } void QgsFeatureListViewDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp index ed65f06385b..7670f0536ce 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.cpp +++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp @@ -45,8 +45,8 @@ QgsDateTimeEdit::QgsDateTimeEdit( QWidget *parent ) setStyleSheet( QStringLiteral( ".QWidget, QLineEdit, QToolButton { padding-right: %1px; }" ).arg( mClearButton->sizeHint().width() + spinButtonWidth() + frameWidth() + 1 ) ); QSize msz = minimumSizeHint(); - setMinimumSize( qMax( msz.width(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ), - qMax( msz.height(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ) ); + setMinimumSize( std::max( msz.width(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ), + std::max( msz.height(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ) ); connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed ); diff --git a/src/gui/editorwidgets/qgsdoublespinbox.cpp b/src/gui/editorwidgets/qgsdoublespinbox.cpp index 66aac453c48..40464757768 100644 --- a/src/gui/editorwidgets/qgsdoublespinbox.cpp +++ b/src/gui/editorwidgets/qgsdoublespinbox.cpp @@ -39,7 +39,7 @@ QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent ) QSize msz = minimumSizeHint(); setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2, - qMax( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) ); + std::max( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) ); connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsDoubleSpinBox::clear ); connect( this, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsDoubleSpinBox::changed ); @@ -71,7 +71,7 @@ void QgsDoubleSpinBox::wheelEvent( QWheelEvent *event ) double newStep = step / 10; // but don't ever use an increment smaller than would be visible in the widget // i.e. if showing 2 decimals, smallest increment will be 0.01 - newStep = qMax( newStep, std::pow( 10.0, 0.0 - decimals() ) ); + newStep = std::max( newStep, std::pow( 10.0, 0.0 - decimals() ) ); setSingleStep( newStep ); diff --git a/src/gui/editorwidgets/qgsspinbox.cpp b/src/gui/editorwidgets/qgsspinbox.cpp index 47910757f22..5b7ba83427c 100644 --- a/src/gui/editorwidgets/qgsspinbox.cpp +++ b/src/gui/editorwidgets/qgsspinbox.cpp @@ -39,7 +39,7 @@ QgsSpinBox::QgsSpinBox( QWidget *parent ) QSize msz = minimumSizeHint(); setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2, - qMax( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) ); + std::max( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) ); connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsSpinBox::clear ); connect( this, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsSpinBox::changed ); @@ -76,7 +76,7 @@ void QgsSpinBox::wheelEvent( QWheelEvent *event ) // ctrl modifier results in finer increments - 10% of usual step int newStep = step / 10; // step should be at least 1 - newStep = qMax( newStep, 1 ); + newStep = std::max( newStep, 1 ); setSingleStep( newStep ); diff --git a/src/gui/effects/qgseffectstackpropertieswidget.cpp b/src/gui/effects/qgseffectstackpropertieswidget.cpp index ba60b03b1fc..08c8e524997 100644 --- a/src/gui/effects/qgseffectstackpropertieswidget.cpp +++ b/src/gui/effects/qgseffectstackpropertieswidget.cpp @@ -297,7 +297,7 @@ void QgsEffectStackPropertiesWidget::removeEffect() mModel->invisibleRootItem()->removeRow( row ); - int newSelection = qMin( row, root->rowCount() - 1 ); + int newSelection = std::min( row, root->rowCount() - 1 ); QModelIndex newIdx = root->child( newSelection )->index(); mEffectsList->setCurrentIndex( newIdx ); diff --git a/src/gui/locator/qgslocatorwidget.cpp b/src/gui/locator/qgslocatorwidget.cpp index 29f117ca6bc..5e4969eb764 100644 --- a/src/gui/locator/qgslocatorwidget.cpp +++ b/src/gui/locator/qgslocatorwidget.cpp @@ -41,7 +41,7 @@ QgsLocatorWidget::QgsLocatorWidget( QWidget *parent ) #endif int placeholderMinWidth = mLineEdit->fontMetrics().width( mLineEdit->placeholderText() ); - int minWidth = qMax( 200, ( int )( placeholderMinWidth * 1.6 ) ); + int minWidth = std::max( 200, ( int )( placeholderMinWidth * 1.6 ) ); resize( minWidth, 30 ); QSizePolicy sizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); sizePolicy.setHorizontalStretch( 0 ); @@ -536,7 +536,7 @@ void QgsLocatorResultsView::recalculateSize() int rowSize = 20 * itemDelegate()->sizeHint( viewOptions(), model()->index( 0, 0 ) ).height(); // try to take up a sensible portion of window width (about half) - int width = qMax( 300, window()->size().width() / 2 ); + int width = std::max( 300, window()->size().width() / 2 ); QSize newSize( width, rowSize + frameWidth() * 2 ); // resize the floating widget this is contained within parentWidget()->resize( newSize ); diff --git a/src/gui/qgscolorbutton.cpp b/src/gui/qgscolorbutton.cpp index 9b583f6009b..e5146485b06 100644 --- a/src/gui/qgscolorbutton.cpp +++ b/src/gui/qgscolorbutton.cpp @@ -73,7 +73,7 @@ QgsColorButton::QgsColorButton( QWidget *parent, const QString &cdt, QgsColorSch mMinimumSize = QSize( 120, 28 ); #endif - mMinimumSize.setHeight( qMax( static_cast( fontMetrics().height() * 1.1 ), mMinimumSize.height() ) ); + mMinimumSize.setHeight( std::max( static_cast( fontMetrics().height() * 1.1 ), mMinimumSize.height() ) ); } diff --git a/src/gui/qgscolorswatchgrid.cpp b/src/gui/qgscolorswatchgrid.cpp index ecf8d193c87..e0019180734 100644 --- a/src/gui/qgscolorswatchgrid.cpp +++ b/src/gui/qgscolorswatchgrid.cpp @@ -42,7 +42,7 @@ QgsColorSwatchGrid::QgsColorSwatchGrid( QgsColorScheme *scheme, const QString &c mLabelMargin = Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "." ) ); mSwatchSize = Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "X" ) ) * 1.75; - mSwatchOutlineSize = qMax( fontMetrics().width( QStringLiteral( "." ) ) * 0.4, 1.0 ); + mSwatchOutlineSize = std::max( fontMetrics().width( QStringLiteral( "." ) ) * 0.4, 1.0 ); mSwatchSpacing = mSwatchSize * 0.3; mSwatchMargin = mLabelMargin; @@ -170,11 +170,11 @@ void QgsColorSwatchGrid::keyPressEvent( QKeyEvent *event ) //handle keyboard navigation if ( event->key() == Qt::Key_Right ) { - mCurrentFocusBox = qMin( mCurrentFocusBox + 1, mColors.length() - 1 ); + mCurrentFocusBox = std::min( mCurrentFocusBox + 1, mColors.length() - 1 ); } else if ( event->key() == Qt::Key_Left ) { - mCurrentFocusBox = qMax( mCurrentFocusBox - 1, 0 ); + mCurrentFocusBox = std::max( mCurrentFocusBox - 1, 0 ); } else if ( event->key() == Qt::Key_Up ) { diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index ffa518e0194..29e8caa363e 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -142,7 +142,7 @@ void QgsColorWidget::alterColor( QColor &color, const QgsColorWidget::ColorCompo color.getHsv( &h, &s, &v, &a ); //clip value to sensible range - int clippedValue = qMin( qMax( 0, newValue ), componentRange( component ) ); + int clippedValue = std::min( std::max( 0, newValue ), componentRange( component ) ); switch ( component ) { @@ -262,8 +262,8 @@ void QgsColorWidget::setComponentValue( const int value ) } //clip value to valid range - int valueClipped = qMin( value, componentRange() ); - valueClipped = qMax( valueClipped, 0 ); + int valueClipped = std::min( value, componentRange() ); + valueClipped = std::max( valueClipped, 0 ); int r, g, b, a; mCurrentColor.getRgb( &r, &g, &b, &a ); @@ -493,7 +493,7 @@ void QgsColorWheel::setColor( const QColor &color, const bool emitSignals ) void QgsColorWheel::createImages( const QSizeF size ) { - double wheelSize = qMin( size.width(), size.height() ) - mMargin * 2.0; + double wheelSize = std::min( size.width(), size.height() ) - mMargin * 2.0; mWheelThickness = wheelSize / 15.0; //recreate cache images at correct size @@ -553,8 +553,8 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) { double dx = std::tan( rad1 ) * r; double rad2 = std::atan( dx / maxR ); - rad2 = qMin( rad2, M_PI / 3.0 ); - rad2 = qMax( rad2, -M_PI / 3.0 ); + rad2 = std::min( rad2, M_PI / 3.0 ); + rad2 = std::max( rad2, -M_PI / 3.0 ); eventAngleRadians += rad2 - rad1; rad0 = std::fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI ); rad1 = std::fmod( rad0, ( ( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 ); @@ -566,8 +566,8 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) double newL = ( ( -std::sin( rad0 ) * r ) / triangleSideLength ) + 0.5; double widthShare = 1.0 - ( std::fabs( newL - 0.5 ) * 2.0 ); double newS = ( ( ( std::cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare; - s = qMin( static_cast< int >( std::round( qMax( 0.0, newS ) * 255.0 ) ), 255 ); - l = qMin( static_cast< int >( std::round( qMax( 0.0, newL ) * 255.0 ) ), 255 ); + s = std::min( static_cast< int >( std::round( std::max( 0.0, newS ) * 255.0 ) ), 255 ); + l = std::min( static_cast< int >( std::round( std::max( 0.0, newL ) * 255.0 ) ), 255 ); newColor = QColor::fromHsl( h, s, l ); //explicitly set the hue again, so that it's exact newColor.setHsv( h, newColor.hsvSaturation(), newColor.value(), alpha ); @@ -637,7 +637,7 @@ void QgsColorWheel::createWheel() return; } - int maxSize = qMin( mWheelImage->width(), mWheelImage->height() ); + int maxSize = std::min( mWheelImage->width(), mWheelImage->height() ); double wheelRadius = maxSize / 2.0; mWheelImage->fill( Qt::transparent ); @@ -943,10 +943,10 @@ int QgsColorBox::xComponentValue() const void QgsColorBox::setColorFromPoint( QPoint point ) { int valX = valueRangeX() * ( point.x() - mMargin ) / ( width() - 2 * mMargin - 1 ); - valX = qMin( qMax( valX, 0 ), valueRangeX() ); + valX = std::min( std::max( valX, 0 ), valueRangeX() ); int valY = valueRangeY() - valueRangeY() * ( point.y() - mMargin ) / ( height() - 2 * mMargin - 1 ); - valY = qMin( qMax( valY, 0 ), valueRangeY() ); + valY = std::min( std::max( valY, 0 ), valueRangeY() ); QColor color = QColor( mCurrentColor ); alterColor( color, xComponent(), valX ); @@ -1254,7 +1254,7 @@ void QgsColorRampWidget::setColorFromPoint( QPointF point ) { val = componentRange() - componentRange() * ( point.y() - mMargin ) / ( height() - 2 * mMargin ); } - val = qMax( 0, qMin( val, componentRange() ) ); + val = std::max( 0, std::min( val, componentRange() ) ); setComponentValue( val ); if ( componentValue() != oldValue ) diff --git a/src/gui/qgscomposerview.cpp b/src/gui/qgscomposerview.cpp index 93933fbf9e9..f6011c3c50a 100644 --- a/src/gui/qgscomposerview.cpp +++ b/src/gui/qgscomposerview.cpp @@ -1049,8 +1049,8 @@ void QgsComposerView::mouseReleaseEvent( QMouseEvent *e ) newLabelItem->adjustSizeToText(); //make sure label size is sufficient to fit text - double labelWidth = qMax( mRubberBandItem->rect().width(), newLabelItem->rect().width() ); - double labelHeight = qMax( mRubberBandItem->rect().height(), newLabelItem->rect().height() ); + double labelWidth = std::max( mRubberBandItem->rect().width(), newLabelItem->rect().width() ); + double labelHeight = std::max( mRubberBandItem->rect().height(), newLabelItem->rect().height() ); newLabelItem->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), labelWidth, labelHeight ) ); composition()->addComposerLabel( newLabelItem ); diff --git a/src/gui/qgscompoundcolorwidget.cpp b/src/gui/qgscompoundcolorwidget.cpp index 3b0d78c2f08..37b532b0ed0 100644 --- a/src/gui/qgscompoundcolorwidget.cpp +++ b/src/gui/qgscompoundcolorwidget.cpp @@ -359,7 +359,7 @@ void QgsCompoundColorWidget::removePalette() //remove scheme from registry QgsApplication::colorSchemeRegistry()->removeColorScheme( userScheme ); refreshSchemeComboBox(); - prevIndex = qMax( qMin( prevIndex, mSchemeComboBox->count() - 1 ), 0 ); + prevIndex = std::max( std::min( prevIndex, mSchemeComboBox->count() - 1 ), 0 ); mSchemeComboBox->setCurrentIndex( prevIndex ); } diff --git a/src/gui/qgscurveeditorwidget.cpp b/src/gui/qgscurveeditorwidget.cpp index 136feeb6870..bc96dd65ec6 100644 --- a/src/gui/qgscurveeditorwidget.cpp +++ b/src/gui/qgscurveeditorwidget.cpp @@ -213,7 +213,7 @@ void QgsCurveEditorWidget::plotMouseMove( QPointF point ) bool removePoint = false; if ( mCurrentPlotMarkerIndex == 0 ) { - point.setX( qMin( point.x(), cp.at( 1 ).x() - 0.01 ) ); + point.setX( std::min( point.x(), cp.at( 1 ).x() - 0.01 ) ); } else { @@ -221,7 +221,7 @@ void QgsCurveEditorWidget::plotMouseMove( QPointF point ) } if ( mCurrentPlotMarkerIndex == cp.count() - 1 ) { - point.setX( qMax( point.x(), cp.at( mCurrentPlotMarkerIndex - 1 ).x() + 0.01 ) ); + point.setX( std::max( point.x(), cp.at( mCurrentPlotMarkerIndex - 1 ).x() + 0.01 ) ); removePoint = false; } else diff --git a/src/gui/qgsfontbutton.cpp b/src/gui/qgsfontbutton.cpp index 06d6202805e..0214dd1b2cf 100644 --- a/src/gui/qgsfontbutton.cpp +++ b/src/gui/qgsfontbutton.cpp @@ -50,7 +50,7 @@ QgsFontButton::QgsFontButton( QWidget *parent, const QString &dialogTitle ) //make sure height of button looks good under different platforms QSize size = QToolButton::minimumSizeHint(); int fontHeight = fontMetrics().height() * 1.4; - mSizeHint = QSize( size.width(), qMax( size.height(), fontHeight ) ); + mSizeHint = QSize( size.width(), std::max( size.height(), fontHeight ) ); } QSize QgsFontButton::minimumSizeHint() const @@ -349,7 +349,7 @@ void QgsFontButton::wheelEvent( QWheelEvent *event ) { size -= increment; } - size = qMax( size, 1.0 ); + size = std::max( size, 1.0 ); switch ( mMode ) { @@ -439,13 +439,13 @@ QPixmap QgsFontButton::createDragIcon( QSize size, const QgsTextFormat *tempForm if ( tempFormat->buffer().enabled() ) xtrans = context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ); if ( tempFormat->background().enabled() && tempFormat->background().sizeType() != QgsTextBackgroundSettings::SizeFixed ) - xtrans = qMax( xtrans, context.convertToPainterUnits( tempFormat->background().size().width(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) ); + xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat->background().size().width(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) ); double ytrans = 0.0; if ( tempFormat->buffer().enabled() ) - ytrans = qMax( ytrans, context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ) ); + ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ) ); if ( tempFormat->background().enabled() ) - ytrans = qMax( ytrans, context.convertToPainterUnits( tempFormat->background().size().height(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) ); + ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->background().size().height(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) ); QRectF textRect = rect; textRect.setLeft( xtrans ); @@ -818,13 +818,13 @@ void QgsFontButton::updatePreview( const QColor &color, QgsTextFormat *format, Q if ( tempFormat.buffer().enabled() ) xtrans = context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ); if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed ) - xtrans = qMax( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) ); + xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) ); double ytrans = 0.0; if ( tempFormat.buffer().enabled() ) - ytrans = qMax( ytrans, context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) ); + ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) ); if ( tempFormat.background().enabled() ) - ytrans = qMax( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) ); + ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) ); QRectF textRect = rect; textRect.setLeft( xtrans ); diff --git a/src/gui/qgshighlight.cpp b/src/gui/qgshighlight.cpp index 26ff9e5e941..17ce2e60778 100644 --- a/src/gui/qgshighlight.cpp +++ b/src/gui/qgshighlight.cpp @@ -187,7 +187,7 @@ double QgsHighlight::getSymbolWidth( const QgsRenderContext &context, double wid { scale = context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMapUnits ); } - width = qMax( width + 2 * mBuffer * scale, mMinWidth * scale ); + width = std::max( width + 2 * mBuffer * scale, mMinWidth * scale ); return width; } diff --git a/src/gui/qgshistogramwidget.cpp b/src/gui/qgshistogramwidget.cpp index 780e77508e4..432a83c82ec 100644 --- a/src/gui/qgshistogramwidget.cpp +++ b/src/gui/qgshistogramwidget.cpp @@ -115,7 +115,7 @@ void QgsHistogramWidget::refreshValues() std::sort( mValues.begin(), mValues.end() ); mHistogram.setValues( mValues ); mBinsSpinBox->blockSignals( true ); - mBinsSpinBox->setValue( qMax( mHistogram.optimalNumberBins(), 30 ) ); + mBinsSpinBox->setValue( std::max( mHistogram.optimalNumberBins(), 30 ) ); mBinsSpinBox->blockSignals( false ); mStats.setStatistics( QgsStatisticalSummary::StDev ); @@ -235,7 +235,7 @@ void QgsHistogramWidget::drawHistogram() dataHisto << QwtIntervalSample( lastValue, mRanges.at( rangeIndex - 1 ).upperValue(), edges.at( bin ) ); } - double upperEdge = !mRanges.isEmpty() ? qMin( edges.at( bin + 1 ), mRanges.at( rangeIndex ).upperValue() ) + double upperEdge = !mRanges.isEmpty() ? std::min( edges.at( bin + 1 ), mRanges.at( rangeIndex ).upperValue() ) : edges.at( bin + 1 ); dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge ); diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index 8ff4e666957..d10a95bac82 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -1251,7 +1251,7 @@ void QgsMapCanvas::endZoomRect( QPoint pos ) const QSize &canvasSize = mSettings.outputSize(); double sfx = ( double )zoomRectSize.width() / canvasSize.width(); double sfy = ( double )zoomRectSize.height() / canvasSize.height(); - double sf = qMax( sfx, sfy ); + double sf = std::max( sfx, sfy ); QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() ); @@ -1745,7 +1745,7 @@ void QgsMapCanvas::updateAutoRefreshTimer() Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() ) { if ( layer->hasAutoRefreshEnabled() && layer->autoRefreshInterval() > 0 ) - minAutoRefreshInterval = minAutoRefreshInterval > 0 ? qMin( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval(); + minAutoRefreshInterval = minAutoRefreshInterval > 0 ? std::min( layer->autoRefreshInterval(), minAutoRefreshInterval ) : layer->autoRefreshInterval(); } if ( minAutoRefreshInterval > 0 ) diff --git a/src/gui/qgsmapcanvasannotationitem.cpp b/src/gui/qgsmapcanvasannotationitem.cpp index ca0ce5f9bd1..56c945fd3b7 100644 --- a/src/gui/qgsmapcanvasannotationitem.cpp +++ b/src/gui/qgsmapcanvasannotationitem.cpp @@ -110,10 +110,10 @@ void QgsMapCanvasAnnotationItem::updateBoundingRect() QSizeF frameSize = mAnnotation ? mAnnotation->frameSize() : QSizeF( 0.0, 0.0 ); - double xMinPos = qMin( -halfSymbolSize, offset.x() - fillSymbolBleed ); - double xMaxPos = qMax( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed ); - double yMinPos = qMin( -halfSymbolSize, offset.y() - fillSymbolBleed ); - double yMaxPos = qMax( halfSymbolSize, offset.y() + frameSize.height() + fillSymbolBleed ); + double xMinPos = std::min( -halfSymbolSize, offset.x() - fillSymbolBleed ); + double xMaxPos = std::max( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed ); + double yMinPos = std::min( -halfSymbolSize, offset.y() - fillSymbolBleed ); + double yMaxPos = std::max( halfSymbolSize, offset.y() + frameSize.height() + fillSymbolBleed ); mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos ); } } diff --git a/src/gui/qgsmaptoolzoom.cpp b/src/gui/qgsmaptoolzoom.cpp index 9996b84ab1d..f23eaba1a3b 100644 --- a/src/gui/qgsmaptoolzoom.cpp +++ b/src/gui/qgsmaptoolzoom.cpp @@ -116,7 +116,7 @@ void QgsMapToolZoom::canvasReleaseEvent( QgsMapMouseEvent *e ) const QSize &canvasSize = mapSettings.outputSize(); double sfx = ( double )zoomRectSize.width() / canvasSize.width(); double sfy = ( double )zoomRectSize.height() / canvasSize.height(); - double sf = qMax( sfx, sfy ); + double sf = std::max( sfx, sfy ); const QgsMapToPixel *m2p = mCanvas->getCoordinateTransform(); QgsPointXY c = m2p->toMapCoordinates( mZoomRect.center() ); diff --git a/src/gui/qgsprevieweffect.cpp b/src/gui/qgsprevieweffect.cpp index 27d61d8fb47..4afb838a0bc 100644 --- a/src/gui/qgsprevieweffect.cpp +++ b/src/gui/qgsprevieweffect.cpp @@ -130,9 +130,9 @@ QRgb QgsPreviewEffect::simulateColorBlindness( QRgb &originalColor, QgsPreviewEf blue = ( -0.000365294 * L ) + ( -0.00412163 * M ) + ( 0.693513 * S ); //restrict values to 0-255 - red = qMax( qMin( 255, red ), 0 ); - green = qMax( qMin( 255, green ), 0 ); - blue = qMax( qMin( 255, blue ), 0 ); + red = std::max( std::min( 255, red ), 0 ); + green = std::max( std::min( 255, green ), 0 ); + blue = std::max( std::min( 255, blue ), 0 ); return qRgb( red, green, blue ); } diff --git a/src/gui/qgspropertyassistantwidget.cpp b/src/gui/qgspropertyassistantwidget.cpp index a101b3631f2..fe4d3e6aa13 100644 --- a/src/gui/qgspropertyassistantwidget.cpp +++ b/src/gui/qgspropertyassistantwidget.cpp @@ -226,7 +226,7 @@ void QgsPropertyAssistantWidget::updatePreview() { const QSize minSize( node->minimumIconSize() ); node->setIconSize( minSize ); - widthMax = qMax( minSize.width(), widthMax ); + widthMax = std::max( minSize.width(), widthMax ); QStandardItem *item = new QStandardItem( node->data( Qt::DecorationRole ).value(), QString::number( breaks[i] ) ); item->setEditable( false ); mPreviewList.appendRow( item ); @@ -287,8 +287,8 @@ bool QgsPropertyAssistantWidget::computeValuesFromExpression( const QString &exp const double value = e.evaluate( &context ).toDouble( &ok ); if ( ok ) { - max = qMax( max, value ); - min = qMin( min, value ); + max = std::max( max, value ); + min = std::min( min, value ); found = true; } } diff --git a/src/gui/qgsrasterlayersaveasdialog.cpp b/src/gui/qgsrasterlayersaveasdialog.cpp index f50e75740bc..69611dffe9c 100644 --- a/src/gui/qgsrasterlayersaveasdialog.cpp +++ b/src/gui/qgsrasterlayersaveasdialog.cpp @@ -682,8 +682,8 @@ void QgsRasterLayerSaveAsDialog::adjustNoDataCellWidth( int row, int column ) QLineEdit *lineEdit = dynamic_cast( mNoDataTableWidget->cellWidget( row, column ) ); if ( !lineEdit ) return; - int width = qMax( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 ); - width = qMax( width, mNoDataTableWidget->columnWidth( column ) ); + int width = std::max( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 ); + width = std::max( width, mNoDataTableWidget->columnWidth( column ) ); lineEdit->setFixedWidth( width ); } diff --git a/src/gui/qgssymbolbutton.cpp b/src/gui/qgssymbolbutton.cpp index e140b2fe573..9d975228254 100644 --- a/src/gui/qgssymbolbutton.cpp +++ b/src/gui/qgssymbolbutton.cpp @@ -46,7 +46,7 @@ QgsSymbolButton::QgsSymbolButton( QWidget *parent, const QString &dialogTitle ) //make sure height of button looks good under different platforms QSize size = QToolButton::minimumSizeHint(); int fontHeight = fontMetrics().height() * 1.4; - mSizeHint = QSize( size.width(), qMax( size.height(), fontHeight ) ); + mSizeHint = QSize( size.width(), std::max( size.height(), fontHeight ) ); } QSize QgsSymbolButton::minimumSizeHint() const diff --git a/src/gui/qgstextpreview.cpp b/src/gui/qgstextpreview.cpp index 98be62de992..8b5b183d688 100644 --- a/src/gui/qgstextpreview.cpp +++ b/src/gui/qgstextpreview.cpp @@ -43,14 +43,14 @@ void QgsTextPreview::paintEvent( QPaintEvent *e ) if ( mFormat.buffer().enabled() ) xtrans = mContext.convertToPainterUnits( mFormat.buffer().size(), mFormat.buffer().sizeUnit(), mFormat.buffer().sizeMapUnitScale() ); if ( mFormat.background().enabled() && mFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed ) - xtrans = qMax( xtrans, mContext.convertToPainterUnits( mFormat.background().size().width(), mFormat.background().sizeUnit(), mFormat.background().sizeMapUnitScale() ) ); + xtrans = std::max( xtrans, mContext.convertToPainterUnits( mFormat.background().size().width(), mFormat.background().sizeUnit(), mFormat.background().sizeMapUnitScale() ) ); xtrans += 4; double ytrans = 0.0; if ( mFormat.buffer().enabled() ) - ytrans = qMax( ytrans, mContext.convertToPainterUnits( mFormat.buffer().size(), mFormat.buffer().sizeUnit(), mFormat.buffer().sizeMapUnitScale() ) ); + ytrans = std::max( ytrans, mContext.convertToPainterUnits( mFormat.buffer().size(), mFormat.buffer().sizeUnit(), mFormat.buffer().sizeMapUnitScale() ) ); if ( mFormat.background().enabled() ) - ytrans = qMax( ytrans, mContext.convertToPainterUnits( mFormat.background().size().height(), mFormat.background().sizeUnit(), mFormat.background().sizeMapUnitScale() ) ); + ytrans = std::max( ytrans, mContext.convertToPainterUnits( mFormat.background().size().height(), mFormat.background().sizeUnit(), mFormat.background().sizeMapUnitScale() ) ); ytrans += 4; QRectF textRect = rect(); diff --git a/src/gui/raster/qgspalettedrendererwidget.cpp b/src/gui/raster/qgspalettedrendererwidget.cpp index 9121619c6fb..174300b85be 100644 --- a/src/gui/raster/qgspalettedrendererwidget.cpp +++ b/src/gui/raster/qgspalettedrendererwidget.cpp @@ -708,9 +708,9 @@ bool QgsPalettedRendererModel::insertRows( int row, int count, const QModelIndex for ( ; cIt != mData.constEnd(); ++cIt ) { int value = cIt->value; - currentMaxValue = qMax( value, currentMaxValue ); + currentMaxValue = std::max( value, currentMaxValue ); } - int nextValue = qMax( 0, currentMaxValue + 1 ); + int nextValue = std::max( 0, currentMaxValue + 1 ); beginInsertRows( QModelIndex(), row, row + count - 1 ); for ( int i = row; i < row + count; ++i, ++nextValue ) diff --git a/src/gui/raster/qgsrastertransparencywidget.cpp b/src/gui/raster/qgsrastertransparencywidget.cpp index 1916821c952..d800a65c186 100644 --- a/src/gui/raster/qgsrastertransparencywidget.cpp +++ b/src/gui/raster/qgsrastertransparencywidget.cpp @@ -618,8 +618,8 @@ void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int colu QLineEdit *lineEdit = dynamic_cast( tableTransparency->cellWidget( row, column ) ); if ( !lineEdit ) return; - int width = qMax( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 ); - width = qMax( width, tableTransparency->columnWidth( column ) ); + int width = std::max( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 ); + width = std::max( width, tableTransparency->columnWidth( column ) ); lineEdit->setFixedWidth( width ); } diff --git a/src/gui/symbology/characterwidget.cpp b/src/gui/symbology/characterwidget.cpp index 967345ba40e..28489c0d085 100644 --- a/src/gui/symbology/characterwidget.cpp +++ b/src/gui/symbology/characterwidget.cpp @@ -62,7 +62,7 @@ CharacterWidget::CharacterWidget( QWidget *parent ) void CharacterWidget::setFont( const QFont &font ) { mDisplayFont.setFamily( font.family() ); - mSquareSize = qMax( 24, QFontMetrics( mDisplayFont ).xHeight() * 3 ); + mSquareSize = std::max( 24, QFontMetrics( mDisplayFont ).xHeight() * 3 ); adjustSize(); update(); } @@ -70,7 +70,7 @@ void CharacterWidget::setFont( const QFont &font ) void CharacterWidget::setFontSize( double fontSize ) { mDisplayFont.setPointSizeF( fontSize ); - mSquareSize = qMax( 24, QFontMetrics( mDisplayFont ).xHeight() * 3 ); + mSquareSize = std::max( 24, QFontMetrics( mDisplayFont ).xHeight() * 3 ); adjustSize(); update(); } @@ -81,7 +81,7 @@ void CharacterWidget::setFontStyle( const QString &fontStyle ) const QFont::StyleStrategy oldStrategy = mDisplayFont.styleStrategy(); mDisplayFont = fontDatabase.font( mDisplayFont.family(), fontStyle, mDisplayFont.pointSize() ); mDisplayFont.setStyleStrategy( oldStrategy ); - mSquareSize = qMax( 24, QFontMetrics( mDisplayFont ).xHeight() * 3 ); + mSquareSize = std::max( 24, QFontMetrics( mDisplayFont ).xHeight() * 3 ); adjustSize(); update(); } diff --git a/src/gui/symbology/qgsellipsesymbollayerwidget.cpp b/src/gui/symbology/qgsellipsesymbollayerwidget.cpp index 0caa367868c..e32cc9bccde 100644 --- a/src/gui/symbology/qgsellipsesymbollayerwidget.cpp +++ b/src/gui/symbology/qgsellipsesymbollayerwidget.cpp @@ -51,7 +51,7 @@ QgsEllipseSymbolLayerWidget::QgsEllipseSymbolLayerWidget( const QgsVectorLayer * names << QStringLiteral( "circle" ) << QStringLiteral( "rectangle" ) << QStringLiteral( "diamond" ) << QStringLiteral( "cross" ) << QStringLiteral( "triangle" ) << QStringLiteral( "right_half_triangle" ) << QStringLiteral( "left_half_triangle" ) << QStringLiteral( "semi_circle" ); int size = mShapeListWidget->iconSize().width(); - size = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ) ); + size = std::max( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ) ); mShapeListWidget->setGridSize( QSize( size * 1.2, size * 1.2 ) ); mShapeListWidget->setIconSize( QSize( size, size ) ); diff --git a/src/gui/symbology/qgspointdisplacementrendererwidget.cpp b/src/gui/symbology/qgspointdisplacementrendererwidget.cpp index 349c3c70ebe..44135b1fa3b 100644 --- a/src/gui/symbology/qgspointdisplacementrendererwidget.cpp +++ b/src/gui/symbology/qgspointdisplacementrendererwidget.cpp @@ -124,7 +124,7 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto mPlacementComboBox->setCurrentIndex( mPlacementComboBox->findData( mRenderer->placement() ) ); //scale dependent labeling - mMinLabelScaleWidget->setScale( qMax( mRenderer->minimumLabelScale(), 0.0 ) ); + mMinLabelScaleWidget->setScale( std::max( mRenderer->minimumLabelScale(), 0.0 ) ); if ( mRenderer->minimumLabelScale() > 0 ) { mScaleDependentLabelsCheckBox->setCheckState( Qt::Checked ); diff --git a/src/gui/symbology/qgsrulebasedrendererwidget.cpp b/src/gui/symbology/qgsrulebasedrendererwidget.cpp index 2da805fd79b..ae2ba7908d7 100644 --- a/src/gui/symbology/qgsrulebasedrendererwidget.cpp +++ b/src/gui/symbology/qgsrulebasedrendererwidget.cpp @@ -619,8 +619,8 @@ QgsRendererRulePropsWidget::QgsRendererRulePropsWidget( QgsRuleBasedRenderer::Ru if ( mRule->dependsOnScale() ) { groupScale->setChecked( true ); - mScaleRangeWidget->setMaximumScale( qMax( rule->maximumScale(), 0.0 ) ); - mScaleRangeWidget->setMinimumScale( qMax( rule->minimumScale(), 0.0 ) ); + mScaleRangeWidget->setMaximumScale( std::max( rule->maximumScale(), 0.0 ) ); + mScaleRangeWidget->setMinimumScale( std::max( rule->minimumScale(), 0.0 ) ); } mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() ); diff --git a/src/gui/symbology/qgssvgselectorwidget.cpp b/src/gui/symbology/qgssvgselectorwidget.cpp index b7368ee50ed..8ced3b98714 100644 --- a/src/gui/symbology/qgssvgselectorwidget.cpp +++ b/src/gui/symbology/qgssvgselectorwidget.cpp @@ -377,7 +377,7 @@ QgsSvgSelectorWidget::QgsSvgSelectorWidget( QWidget *parent ) // TODO: in-code gui setup with option to vertically or horizontally stack SVG groups/images widgets setupUi( this ); - mIconSize = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) ); + mIconSize = std::max( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) ); mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) ); mGroupsTreeView->setHeaderHidden( true ); diff --git a/src/gui/symbology/qgssymbollayerwidget.cpp b/src/gui/symbology/qgssymbollayerwidget.cpp index 8e92550641f..6b95db4c888 100644 --- a/src/gui/symbology/qgssymbollayerwidget.cpp +++ b/src/gui/symbology/qgssymbollayerwidget.cpp @@ -400,7 +400,7 @@ QgsSimpleMarkerSymbolLayerWidget::QgsSimpleMarkerSymbolLayerWidget( const QgsVec mSizeDDBtn->setSymbol( mAssistantPreviewSymbol ); int size = lstNames->iconSize().width(); - size = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ) ); + size = std::max( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXX" ) ) ) ) ); lstNames->setGridSize( QSize( size * 1.2, size * 1.2 ) ); lstNames->setIconSize( QSize( size, size ) ); @@ -1747,7 +1747,7 @@ QgsSvgMarkerSymbolLayerWidget::QgsSvgMarkerSymbolLayerWidget( const QgsVectorLay spinOffsetY->setClearValue( 0.0 ); spinAngle->setClearValue( 0.0 ); - mIconSize = qMax( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) ); + mIconSize = std::max( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) ); viewImages->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) ); populateList(); diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h index bdad2a27971..51a91a6a176 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h @@ -34,7 +34,7 @@ class QgsGeometrySliverPolygonCheck : public QgsGeometryAreaCheck bool checkThreshold( const QgsAbstractGeometry *geom, double &value ) const override { QgsRectangle bb = geom->boundingBox(); - double maxDim = qMax( bb.width(), bb.height() ); + double maxDim = std::max( bb.width(), bb.height() ); double area = geom->area(); value = ( maxDim * maxDim ) / area; if ( mMaxArea > 0. && area > mMaxArea ) diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp index 21fd6aceeec..b73186c4155 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp @@ -64,7 +64,7 @@ QgsGeometryCheckerFixSummaryDialog::QgsGeometryCheckerFixSummaryDialog( QgisInte void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeometryCheckError *error ) { - int prec = 7 - std::floor( qMax( 0., std::log10( qMax( error->location().x(), error->location().y() ) ) ) ); + int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); double layerToMap = mIface->mapCanvas()->mapSettings().layerToMapUnits( mLayer ); QVariant value; diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp index e1f929af3ff..d9d2ad1c353 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp @@ -119,7 +119,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error ) ui.tableWidgetErrors->setSortingEnabled( false ); int row = ui.tableWidgetErrors->rowCount(); - int prec = 7 - std::floor( qMax( 0., std::log10( qMax( error->location().x(), error->location().y() ) ) ) ); + int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); double layerToMap = mIface->mapCanvas()->mapSettings().layerToMapUnits( mFeaturePool->getLayer() ); QVariant value; @@ -167,7 +167,7 @@ void QgsGeometryCheckerResultTab::updateError( QgsGeometryCheckError *error, boo ui.tableWidgetErrors->setSortingEnabled( false ); int row = mErrorMap.value( error ).row(); - int prec = 7 - std::floor( qMax( 0., std::log10( qMax( error->location().x(), error->location().y() ) ) ) ); + int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); double layerToMap = mIface->mapCanvas()->mapSettings().layerToMapUnits( mFeaturePool->getLayer() ); QVariant value; diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp index a59a3dcad58..d77aff41120 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp @@ -100,7 +100,7 @@ void QgsGeometryCheckerSetupTab::updateLayers() } ++idx; } - ui.comboBoxInputLayer->setCurrentIndex( qMax( 0, currIdx ) ); + ui.comboBoxInputLayer->setCurrentIndex( std::max( 0, currIdx ) ); } QgsVectorLayer *QgsGeometryCheckerSetupTab::getSelectedLayer() diff --git a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp index 481697e4a69..d9f5737aa28 100644 --- a/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp +++ b/src/plugins/geometry_checker/utils/qgsgeometrycheckerutils.cpp @@ -104,9 +104,9 @@ namespace QgsGeometryCheckerUtils { std::swap( lambdaq1, lambdaq2 ); } - double lambda1 = qMax( lambdaq1, lambdap1 ); - double lambda2 = qMin( lambdaq2, lambdap2 ); - len += qMax( 0., lambda2 - lambda1 ); + double lambda1 = std::max( lambdaq1, lambdap1 ); + double lambda2 = std::min( lambdaq2, lambdap2 ); + len += std::max( 0., lambda2 - lambda1 ); } } } diff --git a/src/plugins/georeferencer/qgsgeorefplugingui.cpp b/src/plugins/georeferencer/qgsgeorefplugingui.cpp index 17d6e371132..08504af5bc0 100644 --- a/src/plugins/georeferencer/qgsgeorefplugingui.cpp +++ b/src/plugins/georeferencer/qgsgeorefplugingui.cpp @@ -2017,10 +2017,10 @@ QgsRectangle QgsGeorefPluginGui::transformViewportBoundingBox( const QgsRectangl break; } t.transform( src, raster, rasterToWorld ); - minX = qMin( raster.x(), minX ); - maxX = qMax( raster.x(), maxX ); - minY = qMin( raster.y(), minY ); - maxY = qMax( raster.y(), maxY ); + minX = std::min( raster.x(), minX ); + maxX = std::max( raster.x(), maxX ); + minY = std::min( raster.y(), minY ); + maxY = std::max( raster.y(), maxY ); } } return QgsRectangle( minX, minY, maxX, maxY ); diff --git a/src/plugins/georeferencer/qgsgeoreftransform.cpp b/src/plugins/georeferencer/qgsgeoreftransform.cpp index 81ff4eb4a91..83d39ca7b7d 100644 --- a/src/plugins/georeferencer/qgsgeoreftransform.cpp +++ b/src/plugins/georeferencer/qgsgeoreftransform.cpp @@ -486,7 +486,7 @@ int QgsHelmertGeorefTransform::helmert_transform( void *pTransformerArg, int bDs } QgsGDALGeorefTransform::QgsGDALGeorefTransform( bool useTPS, unsigned int polynomialOrder ) - : mPolynomialOrder( qMin( 3u, polynomialOrder ) ) + : mPolynomialOrder( std::min( 3u, polynomialOrder ) ) , mIsTPSTransform( useTPS ) { mGDALTransformer = nullptr; diff --git a/src/plugins/georeferencer/qgsimagewarper.cpp b/src/plugins/georeferencer/qgsimagewarper.cpp index 0f8459f90f1..279c3f87cce 100644 --- a/src/plugins/georeferencer/qgsimagewarper.cpp +++ b/src/plugins/georeferencer/qgsimagewarper.cpp @@ -333,7 +333,7 @@ int CPL_STDCALL QgsImageWarper::updateWarpProgress( double dfComplete, const cha { Q_UNUSED( pszMessage ); QProgressDialog *progress = static_cast( pProgressArg ); - progress->setValue( qMin( 100u, ( uint )( dfComplete * 100.0 ) ) ); + progress->setValue( std::min( 100u, ( uint )( dfComplete * 100.0 ) ) ); qApp->processEvents(); // TODO: call QEventLoop manually to make "cancel" button more responsive if ( progress->wasCanceled() ) diff --git a/src/plugins/georeferencer/qgsleastsquares.cpp b/src/plugins/georeferencer/qgsleastsquares.cpp index 476e1d1025c..2fd9c72a687 100644 --- a/src/plugins/georeferencer/qgsleastsquares.cpp +++ b/src/plugins/georeferencer/qgsleastsquares.cpp @@ -255,7 +255,7 @@ void QgsLeastSquares::projective( QVector mapCoords, // GSL does not support a full SVD, so we artificially add a linear dependent row // to the matrix in case the system is underconstrained. - uint m = qMax( 9u, ( uint )mapCoords.size() * 2u ); + uint m = std::max( 9u, ( uint )mapCoords.size() * 2u ); uint n = 9; gsl_matrix *S = gsl_matrix_alloc( m, n ); diff --git a/src/plugins/grass/qtermwidget/ColorScheme.cpp b/src/plugins/grass/qtermwidget/ColorScheme.cpp index 563ce71d3a9..2cc2f3ed425 100644 --- a/src/plugins/grass/qtermwidget/ColorScheme.cpp +++ b/src/plugins/grass/qtermwidget/ColorScheme.cpp @@ -197,8 +197,8 @@ ColorEntry ColorScheme::colorEntry( int index, uint randomSeed ) const QColor &color = entry.color; int newHue = std::abs( ( color.hue() + hueDifference ) % MAX_HUE ); - int newValue = qMin( std::abs( color.value() + valueDifference ), 255 ); - int newSaturation = qMin( std::abs( color.saturation() + saturationDifference ), 255 ); + int newValue = std::min( std::abs( color.value() + valueDifference ), 255 ); + int newSaturation = std::min( std::abs( color.saturation() + saturationDifference ), 255 ); color.setHsv( newHue, newSaturation, newValue ); } diff --git a/src/plugins/grass/qtermwidget/History.cpp b/src/plugins/grass/qtermwidget/History.cpp index 909ebd10b64..f49b00becad 100644 --- a/src/plugins/grass/qtermwidget/History.cpp +++ b/src/plugins/grass/qtermwidget/History.cpp @@ -384,12 +384,12 @@ void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount) HistoryLine* oldBuffer = _historyBuffer; HistoryLine* newBuffer = new HistoryLine[lineCount]; - for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ ) + for ( int i = 0 ; i < std::min(_usedLines,(int)lineCount) ; i++ ) { newBuffer[i] = oldBuffer[bufferIndex(i)]; } - _usedLines = qMin(_usedLines,(int)lineCount); + _usedLines = std::min(_usedLines,(int)lineCount); _maxLineCount = lineCount; _head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1; diff --git a/src/plugins/grass/qtermwidget/HistorySearch.cpp b/src/plugins/grass/qtermwidget/HistorySearch.cpp index 41c514f7bd6..19d73a385b8 100644 --- a/src/plugins/grass/qtermwidget/HistorySearch.cpp +++ b/src/plugins/grass/qtermwidget/HistorySearch.cpp @@ -72,7 +72,7 @@ bool HistorySearch::search(int startColumn, int startLine, int endColumn, int en // We read process history from (and including) startLine to (and including) endLine in // blocks of at most 10K lines so that we do not use unhealthy amounts of memory int blockSize; - while ((blockSize = qMin(10000, linesToRead - linesRead)) > 0) { + while ((blockSize = std::min(10000, linesToRead - linesRead)) > 0) { QString string; QTextStream searchStream(&string); diff --git a/src/plugins/grass/qtermwidget/Screen.cpp b/src/plugins/grass/qtermwidget/Screen.cpp index 9ba0768181d..28e68422100 100644 --- a/src/plugins/grass/qtermwidget/Screen.cpp +++ b/src/plugins/grass/qtermwidget/Screen.cpp @@ -107,8 +107,8 @@ void Screen::cursorUp(int n) { if (n == 0) n = 1; // Default int stop = cuY < _topMargin ? 0 : _topMargin; - cuX = qMin(columns-1,cuX); // nowrap! - cuY = qMax(stop,cuY-n); + cuX = std::min(columns-1,cuX); // nowrap! + cuY = std::max(stop,cuY-n); } void Screen::cursorDown(int n) @@ -116,23 +116,23 @@ void Screen::cursorDown(int n) { if (n == 0) n = 1; // Default int stop = cuY > _bottomMargin ? lines-1 : _bottomMargin; - cuX = qMin(columns-1,cuX); // nowrap! - cuY = qMin(stop,cuY+n); + cuX = std::min(columns-1,cuX); // nowrap! + cuY = std::min(stop,cuY+n); } void Screen::cursorLeft(int n) //=CUB { if (n == 0) n = 1; // Default - cuX = qMin(columns-1,cuX); // nowrap! - cuX = qMax(0,cuX-n); + cuX = std::min(columns-1,cuX); // nowrap! + cuX = std::max(0,cuX-n); } void Screen::cursorRight(int n) //=CUF { if (n == 0) n = 1; // Default - cuX = qMin(columns-1,cuX+n); + cuX = std::min(columns-1,cuX+n); } void Screen::setMargins(int top, int bot) @@ -189,7 +189,7 @@ void Screen::nextLine() void Screen::eraseChars(int n) { if (n == 0) n = 1; // Default - int p = qMax(0,qMin(cuX+n-1,columns-1)); + int p = std::max(0,std::min(cuX+n-1,columns-1)); clearImage(loc(cuX,cuY),loc(p,cuY),' '); } @@ -283,8 +283,8 @@ void Screen::saveCursor() void Screen::restoreCursor() { - cuX = qMin(savedState.cursorColumn,columns-1); - cuY = qMin(savedState.cursorLine,lines-1); + cuX = std::min(savedState.cursorColumn,columns-1); + cuY = std::min(savedState.cursorLine,lines-1); currentRendition = savedState.rendition; currentForeground = savedState.foreground; currentBackground = savedState.background; @@ -307,7 +307,7 @@ void Screen::resizeImage(int new_lines, int new_columns) // create new screen lines and copy from old to new ImageLine* newScreenLines = new ImageLine[new_lines+1]; - for (int i=0; i < qMin(lines,new_lines+1) ;i++) + for (int i=0; i < std::min(lines,new_lines+1) ;i++) newScreenLines[i]=screenLines[i]; for (int i=lines;(i > 0) && (igetLineLen(line)); + const int length = std::min(columns,history->getLineLen(line)); const int destLineOffset = (line-startLine)*columns; history->getCells(line,0,length,dest + destLineOffset); @@ -550,8 +550,8 @@ void Screen::clear() void Screen::backspace() { - cuX = qMin(columns-1,cuX); // nowrap! - cuX = qMax(0,cuX-1); + cuX = std::min(columns-1,cuX); // nowrap! + cuX = std::max(0,cuX-1); if (screenLines[cuY].size() < cuX+1) screenLines[cuY].resize(cuX+1); @@ -768,14 +768,14 @@ void Screen::setCursorX(int x) { if (x == 0) x = 1; // Default x -= 1; // Adjust - cuX = qMax(0,qMin(columns-1, x)); + cuX = std::max(0,std::min(columns-1, x)); } void Screen::setCursorY(int y) { if (y == 0) y = 1; // Default y -= 1; // Adjust - cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? _topMargin : 0) )); + cuY = std::max(0,std::min(lines -1, y + (getMode(MODE_Origin) ? _topMargin : 0) )); } void Screen::home() @@ -1077,8 +1077,8 @@ void Screen::setSelectionEnd( const int x, const int y) int bottomRow = selBottomRight / columns; int bottomColumn = selBottomRight % columns; - selTopLeft = loc(qMin(topColumn,bottomColumn),topRow); - selBottomRight = loc(qMax(topColumn,bottomColumn),bottomRow); + selTopLeft = loc(std::min(topColumn,bottomColumn),topRow); + selBottomRight = loc(std::max(topColumn,bottomColumn),bottomRow); } } @@ -1187,7 +1187,7 @@ int Screen::copyLineToStream(int line , const int lineLength = history->getLineLen(line); // ensure that start position is before end of line - start = qMin(start,qMax(0,lineLength-1)); + start = std::min(start,std::max(0,lineLength-1)); // retrieve line from history buffer. It is assumed // that the history buffer does not store trailing white space @@ -1198,7 +1198,7 @@ int Screen::copyLineToStream(int line , } else { - count = qMin(start+count,lineLength)-start; + count = std::min(start+count,lineLength)-start; } // safety checks @@ -1224,7 +1224,7 @@ int Screen::copyLineToStream(int line , int length = screenLines[screenLine].count(); //retrieve line from screen image - for (int i=start;i < qMin(start+count,length);i++) + for (int i=start;i < std::min(start+count,length);i++) { characterBuffer[i-start] = data[i]; } diff --git a/src/plugins/grass/qtermwidget/ScreenWindow.cpp b/src/plugins/grass/qtermwidget/ScreenWindow.cpp index 640465f67c4..53cd2102aee 100644 --- a/src/plugins/grass/qtermwidget/ScreenWindow.cpp +++ b/src/plugins/grass/qtermwidget/ScreenWindow.cpp @@ -102,7 +102,7 @@ void ScreenWindow::fillUnusedArea() // int ScreenWindow::endWindowLine() const { - return qMin(currentLine() + windowLines() - 1, + return std::min(currentLine() + windowLines() - 1, lineCount() - 1); } QVector ScreenWindow::getLineProperties() @@ -132,7 +132,7 @@ void ScreenWindow::getSelectionEnd( int& column , int& line ) } void ScreenWindow::setSelectionStart( int column , int line , bool columnMode ) { - _screen->setSelectionStart( column , qMin(line + currentLine(),endWindowLine()) , columnMode); + _screen->setSelectionStart( column , std::min(line + currentLine(),endWindowLine()) , columnMode); _bufferNeedsUpdate = true; emit selectionChanged(); @@ -140,7 +140,7 @@ void ScreenWindow::setSelectionStart( int column , int line , bool columnMode ) void ScreenWindow::setSelectionEnd( int column , int line ) { - _screen->setSelectionEnd( column , qMin(line + currentLine(),endWindowLine()) ); + _screen->setSelectionEnd( column , std::min(line + currentLine(),endWindowLine()) ); _bufferNeedsUpdate = true; emit selectionChanged(); @@ -148,7 +148,7 @@ void ScreenWindow::setSelectionEnd( int column , int line ) bool ScreenWindow::isSelected( int column , int line ) { - return _screen->isSelected( column , qMin(line + currentLine(),endWindowLine()) ); + return _screen->isSelected( column , std::min(line + currentLine(),endWindowLine()) ); } void ScreenWindow::clearSelection() @@ -269,7 +269,7 @@ void ScreenWindow::notifyOutputChanged() if ( _trackOutput ) { _scrollCount -= _screen->scrolledLines(); - _currentLine = qMax(0,_screen->getHistLines() - (windowLines()-_screen->getLines())); + _currentLine = std::max(0,_screen->getHistLines() - (windowLines()-_screen->getLines())); } else { @@ -278,12 +278,12 @@ void ScreenWindow::notifyOutputChanged() // lines of output - in this case the screen // window's current line number will need to // be adjusted - otherwise the output will scroll - _currentLine = qMax(0,_currentLine - + _currentLine = std::max(0,_currentLine - _screen->droppedLines()); // ensure that the screen window's current position does // not go beyond the bottom of the screen - _currentLine = qMin( _currentLine , _screen->getHistLines() ); + _currentLine = std::min( _currentLine , _screen->getHistLines() ); } _bufferNeedsUpdate = true; diff --git a/src/plugins/grass/qtermwidget/Session.cpp b/src/plugins/grass/qtermwidget/Session.cpp index e8cf854437b..6617651cf1f 100644 --- a/src/plugins/grass/qtermwidget/Session.cpp +++ b/src/plugins/grass/qtermwidget/Session.cpp @@ -541,8 +541,8 @@ void Session::updateTerminalSize() if ( view->isHidden() == false && view->lines() >= VIEW_LINES_THRESHOLD && view->columns() >= VIEW_COLUMNS_THRESHOLD ) { - minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() ); - minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() ); + minLines = (minLines == -1) ? view->lines() : std::min( minLines , view->lines() ); + minColumns = (minColumns == -1) ? view->columns() : std::min( minColumns , view->columns() ); } } diff --git a/src/plugins/grass/qtermwidget/TerminalCharacterDecoder.cpp b/src/plugins/grass/qtermwidget/TerminalCharacterDecoder.cpp index dd62785d0e4..7698955c28c 100644 --- a/src/plugins/grass/qtermwidget/TerminalCharacterDecoder.cpp +++ b/src/plugins/grass/qtermwidget/TerminalCharacterDecoder.cpp @@ -103,7 +103,7 @@ void PlainTextDecoder::decodeLine(const Character* const characters, int count, for (int i=0;i_lines - 2 ) ); + region.setBottom( std::min( region.bottom(), this->_lines - 2 ) ); // return if there is nothing to do if ( lines == 0 @@ -995,8 +995,8 @@ void TerminalDisplay::updateImage() CharacterColor _clipboard; // undefined int cr = -1; // undefined - const int linesToUpdate = qMin( this->_lines, qMax( 0, lines ) ); - const int columnsToUpdate = qMin( this->_columns, qMax( 0, columns ) ); + const int linesToUpdate = std::min( this->_lines, std::max( 0, lines ) ); + const int columnsToUpdate = std::min( this->_columns, std::max( 0, columns ) ); QChar *disstrU = new QChar[columnsToUpdate]; char *dirtyMask = new char[columnsToUpdate + 2]; @@ -1440,10 +1440,10 @@ void TerminalDisplay::drawContents( QPainter &paint, const QRect &rect ) int tLx = tL.x(); int tLy = tL.y(); - int lux = qMin( _usedColumns - 1, qMax( 0, ( rect.left() - tLx - _leftMargin ) / _fontWidth ) ); - int luy = qMin( _usedLines - 1, qMax( 0, ( rect.top() - tLy - _topMargin ) / _fontHeight ) ); - int rlx = qMin( _usedColumns - 1, qMax( 0, ( rect.right() - tLx - _leftMargin ) / _fontWidth ) ); - int rly = qMin( _usedLines - 1, qMax( 0, ( rect.bottom() - tLy - _topMargin ) / _fontHeight ) ); + int lux = std::min( _usedColumns - 1, std::max( 0, ( rect.left() - tLx - _leftMargin ) / _fontWidth ) ); + int luy = std::min( _usedLines - 1, std::max( 0, ( rect.top() - tLy - _topMargin ) / _fontHeight ) ); + int rlx = std::min( _usedColumns - 1, std::max( 0, ( rect.right() - tLx - _leftMargin ) / _fontWidth ) ); + int rly = std::min( _usedLines - 1, std::max( 0, ( rect.bottom() - tLy - _topMargin ) / _fontHeight ) ); const int bufferSize = _usedColumns; QString unistr; @@ -1488,7 +1488,7 @@ void TerminalDisplay::drawContents( QPainter &paint, const QRect &rect ) } bool lineDraw = isLineChar( c ); - bool doubleWidth = ( _image[ qMin( loc( x, y ) + 1, _imageSize ) ].character == 0 ); + bool doubleWidth = ( _image[ std::min( loc( x, y ) + 1, _imageSize ) ].character == 0 ); CharacterColor currentForeground = _image[loc( x, y )].foregroundColor; CharacterColor currentBackground = _image[loc( x, y )].backgroundColor; quint8 currentRendition = _image[loc( x, y )].rendition; @@ -1497,7 +1497,7 @@ void TerminalDisplay::drawContents( QPainter &paint, const QRect &rect ) _image[loc( x + len, y )].foregroundColor == currentForeground && _image[loc( x + len, y )].backgroundColor == currentBackground && _image[loc( x + len, y )].rendition == currentRendition && - ( _image[ qMin( loc( x + len, y ) + 1, _imageSize ) ].character == 0 ) == doubleWidth && + ( _image[ std::min( loc( x + len, y ) + 1, _imageSize ) ].character == 0 ) == doubleWidth && isLineChar( c = _image[loc( x + len, y )].character ) == lineDraw ) // Assignment! { if ( c ) @@ -1641,8 +1641,8 @@ void TerminalDisplay::updateImageSize() makeImage(); // copy the old image to reduce flicker - int lines = qMin( oldlin, _lines ); - int columns = qMin( oldcol, _columns ); + int lines = std::min( oldlin, _lines ); + int columns = std::min( oldcol, _columns ); if ( oldimg ) { @@ -2896,12 +2896,12 @@ void TerminalDisplay::calcGeometry() if ( !_isFixedSize ) { // ensure that display is always at least one column wide - _columns = qMax( 1, _contentWidth / _fontWidth ); - _usedColumns = qMin( _usedColumns, _columns ); + _columns = std::max( 1, _contentWidth / _fontWidth ); + _usedColumns = std::min( _usedColumns, _columns ); // ensure that display is always at least one line high - _lines = qMax( 1, _contentHeight / _fontHeight ); - _usedLines = qMin( _usedLines, _lines ); + _lines = std::max( 1, _contentHeight / _fontHeight ); + _usedLines = std::min( _usedLines, _lines ); } } @@ -2945,10 +2945,10 @@ void TerminalDisplay::setFixedSize( int cols, int lins ) _isFixedSize = true; //ensure that display is at least one line by one column in size - _columns = qMax( 1, cols ); - _lines = qMax( 1, lins ); - _usedColumns = qMin( _usedColumns, _columns ); - _usedLines = qMin( _usedLines, _lines ); + _columns = std::max( 1, cols ); + _lines = std::max( 1, lins ); + _usedColumns = std::min( _usedColumns, _columns ); + _usedLines = std::min( _usedLines, _lines ); if ( _image ) { diff --git a/src/plugins/grass/qtermwidget/Vt102Emulation.cpp b/src/plugins/grass/qtermwidget/Vt102Emulation.cpp index 822a16a6f19..edc721f077d 100644 --- a/src/plugins/grass/qtermwidget/Vt102Emulation.cpp +++ b/src/plugins/grass/qtermwidget/Vt102Emulation.cpp @@ -192,14 +192,14 @@ void Vt102Emulation::addDigit(int digit) void Vt102Emulation::addArgument() { - argc = qMin(argc+1,MAXARGS-1); + argc = std::min(argc+1,MAXARGS-1); argv[argc] = 0; } void Vt102Emulation::addToCurrentToken(int cc) { tokenBuffer[tokenBufferPos] = cc; - tokenBufferPos = qMin(tokenBufferPos+1,MAX_TOKEN_LENGTH-1); + tokenBufferPos = std::min(tokenBufferPos+1,MAX_TOKEN_LENGTH-1); } // Character Class flags used while decoding diff --git a/src/plugins/grass/qtermwidget/kpty.cpp b/src/plugins/grass/qtermwidget/kpty.cpp index 2ddfec229af..daf3a5c1b33 100644 --- a/src/plugins/grass/qtermwidget/kpty.cpp +++ b/src/plugins/grass/qtermwidget/kpty.cpp @@ -509,7 +509,7 @@ void KPty::login(const char * user, const char * remotehost) if (remotehost) { strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host)); # ifdef HAVE_STRUCT_UTMP_UT_SYSLEN - l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host)); + l_struct.ut_syslen = std::min(strlen(remotehost), sizeof(l_struct.ut_host)); # endif } diff --git a/src/plugins/grass/qtermwidget/kptydevice.cpp b/src/plugins/grass/qtermwidget/kptydevice.cpp index 0445947bf52..0112e5a9a3a 100644 --- a/src/plugins/grass/qtermwidget/kptydevice.cpp +++ b/src/plugins/grass/qtermwidget/kptydevice.cpp @@ -400,14 +400,14 @@ bool KPtyDevice::isSuspended() const qint64 KPtyDevice::readData(char *data, qint64 maxlen) { Q_D(KPtyDevice); - return d->readBuffer.read(data, (int)qMin(maxlen, KMAXINT)); + return d->readBuffer.read(data, (int)std::min(maxlen, KMAXINT)); } // protected qint64 KPtyDevice::readLineData(char *data, qint64 maxlen) { Q_D(KPtyDevice); - return d->readBuffer.readLine(data, (int)qMin(maxlen, KMAXINT)); + return d->readBuffer.readLine(data, (int)std::min(maxlen, KMAXINT)); } // protected diff --git a/src/plugins/grass/qtermwidget/kptydevice.h b/src/plugins/grass/qtermwidget/kptydevice.h index aba476649d3..997c5638c90 100644 --- a/src/plugins/grass/qtermwidget/kptydevice.h +++ b/src/plugins/grass/qtermwidget/kptydevice.h @@ -251,7 +251,7 @@ public: } else { buffers.last().resize(tail); QByteArray tmp; - tmp.resize(qMax(CHUNKSIZE, bytes)); + tmp.resize(std::max(CHUNKSIZE, bytes)); ptr = tmp.data(); buffers << tmp; tail = bytes; @@ -286,7 +286,7 @@ public: return -1; const QByteArray &buf = *it; ++it; - int len = qMin((it == buffers.end() ? tail : buf.size()) - start, + int len = std::min((it == buffers.end() ? tail : buf.size()) - start, maxLength); const char *ptr = buf.data() + start; if (const char *rptr = (const char *)memchr(ptr, c, len)) @@ -309,11 +309,11 @@ public: int read(char *data, int maxLength) { - int bytesToRead = qMin(size(), maxLength); + int bytesToRead = std::min(size(), maxLength); int readSoFar = 0; while (readSoFar < bytesToRead) { const char *ptr = readPointer(); - int bs = qMin(bytesToRead - readSoFar, readSize()); + int bs = std::min(bytesToRead - readSoFar, readSize()); memcpy(data + readSoFar, ptr, bs); readSoFar += bs; free(bs); @@ -323,7 +323,7 @@ public: int readLine(char *data, int maxLength) { - return read(data, lineSize(qMin(maxLength, size()))); + return read(data, lineSize(std::min(maxLength, size()))); } private: diff --git a/src/providers/arcgisrest/qgsafsshareddata.cpp b/src/providers/arcgisrest/qgsafsshareddata.cpp index 3ea7a66324f..6544dded612 100644 --- a/src/providers/arcgisrest/qgsafsshareddata.cpp +++ b/src/providers/arcgisrest/qgsafsshareddata.cpp @@ -52,7 +52,7 @@ bool QgsAfsSharedData::getFeature( QgsFeatureId id, QgsFeature &f, bool fetchGeo // Fetch 100 features at the time int startId = ( id / 100 ) * 100; - int stopId = qMin( startId + 100, mObjectIds.length() ); + int stopId = std::min( startId + 100, mObjectIds.length() ); QList objectIds; objectIds.reserve( stopId ); for ( int i = startId; i < stopId; ++i ) diff --git a/src/providers/arcgisrest/qgsamsprovider.cpp b/src/providers/arcgisrest/qgsamsprovider.cpp index ecae64daab4..c97676e4f2c 100644 --- a/src/providers/arcgisrest/qgsamsprovider.cpp +++ b/src/providers/arcgisrest/qgsamsprovider.cpp @@ -96,11 +96,11 @@ void QgsAmsLegendFetcher::handleFinished() QSize maxImageSize( 0, 0 ); foreach ( const LegendEntry_t &legendEntry, legendEntries ) { - maxImageSize.setWidth( qMax( maxImageSize.width(), legendEntry.second.width() ) ); - maxImageSize.setHeight( qMax( maxImageSize.height(), legendEntry.second.height() ) ); + maxImageSize.setWidth( std::max( maxImageSize.width(), legendEntry.second.width() ) ); + maxImageSize.setHeight( std::max( maxImageSize.height(), legendEntry.second.height() ) ); } double scaleFactor = maxImageSize.width() == 0 || maxImageSize.height() == 0 ? 1.0 : - qMin( 1., qMin( double( imageSize ) / maxImageSize.width(), double( imageSize ) / maxImageSize.height() ) ); + std::min( 1., std::min( double( imageSize ) / maxImageSize.width(), double( imageSize ) / maxImageSize.height() ) ); mLegendImage = QImage( imageSize + padding + textWidth, vpadding + legendEntries.size() * ( imageSize + vpadding ), QImage::Format_ARGB32 ); mLegendImage.fill( Qt::transparent ); diff --git a/src/providers/grass/qgsgrassfeatureiterator.cpp b/src/providers/grass/qgsgrassfeatureiterator.cpp index 6a3b1f3d516..d6afc567e20 100644 --- a/src/providers/grass/qgsgrassfeatureiterator.cpp +++ b/src/providers/grass/qgsgrassfeatureiterator.cpp @@ -65,7 +65,7 @@ QgsGrassFeatureIterator::QgsGrassFeatureIterator( QgsGrassFeatureSource *source, // multiple iterators if features are edited -> lock only critical sections // Create selection - int size = 1 + qMax( Vect_get_num_lines( mSource->map() ), Vect_get_num_areas( mSource->map() ) ); + int size = 1 + std::max( Vect_get_num_lines( mSource->map() ), Vect_get_num_areas( mSource->map() ) ); QgsDebugMsg( QString( "mSelection.resize(%1)" ).arg( size ) ); mSelection.resize( size ); diff --git a/src/providers/grass/qgsgrassvector.cpp b/src/providers/grass/qgsgrassvector.cpp index aaf2a11347c..3c201218275 100644 --- a/src/providers/grass/qgsgrassvector.cpp +++ b/src/providers/grass/qgsgrassvector.cpp @@ -348,7 +348,7 @@ int QgsGrassVector::maxLayerNumber() const int max = 0; Q_FOREACH ( QgsGrassVectorLayer *layer, mLayers ) { - max = qMax( max, layer->number() ); + max = std::max( max, layer->number() ); } return max; } diff --git a/src/providers/grass/qgsgrassvectormaplayer.cpp b/src/providers/grass/qgsgrassvectormaplayer.cpp index 2941380e72a..8a9cb71073b 100644 --- a/src/providers/grass/qgsgrassvectormaplayer.cpp +++ b/src/providers/grass/qgsgrassvectormaplayer.cpp @@ -241,14 +241,14 @@ void QgsGrassVectorMapLayer::load() case DB_C_TYPE_INT: iv = db_get_value_int( value ); variant = QVariant( iv ); - mMinMax[i].first = qMin( mMinMax[i].first, ( double )iv ); - mMinMax[i].second = qMin( mMinMax[i].second, ( double )iv ); + mMinMax[i].first = std::min( mMinMax[i].first, ( double )iv ); + mMinMax[i].second = std::min( mMinMax[i].second, ( double )iv ); break; case DB_C_TYPE_DOUBLE: dv = db_get_value_double( value ); variant = QVariant( dv ); - mMinMax[i].first = qMin( mMinMax[i].first, dv ); - mMinMax[i].second = qMin( mMinMax[i].second, dv ); + mMinMax[i].first = std::min( mMinMax[i].first, dv ); + mMinMax[i].second = std::min( mMinMax[i].second, dv ); break; case DB_C_TYPE_STRING: // Store as byte array so that codec may be used later diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 0126c6535ee..c53ea49ffe4 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -1108,10 +1108,10 @@ QgsRectangle QgsOgrProvider::extent() const OGREnvelope env; OGR_G_GetEnvelope( g, &env ); - mExtent->MinX = qMin( mExtent->MinX, env.MinX ); - mExtent->MinY = qMin( mExtent->MinY, env.MinY ); - mExtent->MaxX = qMax( mExtent->MaxX, env.MaxX ); - mExtent->MaxY = qMax( mExtent->MaxY, env.MaxY ); + mExtent->MinX = std::min( mExtent->MinX, env.MinX ); + mExtent->MinY = std::min( mExtent->MinY, env.MinY ); + mExtent->MaxX = std::max( mExtent->MaxX, env.MaxX ); + mExtent->MaxY = std::max( mExtent->MaxY, env.MaxY ); } OGR_F_Destroy( f ); diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp index 03f55d75a1f..5944575783c 100644 --- a/src/providers/wcs/qgswcsprovider.cpp +++ b/src/providers/wcs/qgswcsprovider.cpp @@ -1218,7 +1218,7 @@ QString QgsWcsProvider::coverageMetadata( const QgsWcsCoverageSummary &coverage // TODO(?): supportedCrs and supportedFormat are not available in 1.0 // until coverage is described - it would be confusing to show it only if available #if 0 - for ( int j = 0; j < qMin( coverage.supportedCrs.size(), 10 ); j++ ) + for ( int j = 0; j < std::min( coverage.supportedCrs.size(), 10 ); j++ ) { metadata += htmlRow( tr( "Available in CRS" ), coverage.supportedCrs.value( j ) ); } @@ -1228,7 +1228,7 @@ QString QgsWcsProvider::coverageMetadata( const QgsWcsCoverageSummary &coverage metadata += htmlRow( tr( "Available in CRS" ), tr( "(and %n more)", "crs", coverage.supportedCrs.size() - 10 ) ); } - for ( int j = 0; j < qMin( coverage.supportedFormat.size(), 10 ); j++ ) + for ( int j = 0; j < std::min( coverage.supportedFormat.size(), 10 ); j++ ) { metadata += htmlRow( tr( "Available in format" ), coverage.supportedFormat.value( j ) ); } diff --git a/src/providers/wfs/qgswfsfeatureiterator.cpp b/src/providers/wfs/qgswfsfeatureiterator.cpp index fcf4c36816c..b3f2614f17d 100644 --- a/src/providers/wfs/qgswfsfeatureiterator.cpp +++ b/src/providers/wfs/qgswfsfeatureiterator.cpp @@ -140,7 +140,7 @@ void QgsWFSProgressDialog::resizeEvent( QResizeEvent *ev ) QRect cancelRect = mCancel->geometry(); QRect hideRect = mHide->geometry(); int mtb = style()->pixelMetric( QStyle::PM_DefaultTopLevelMargin ); - int mlr = qMin( width() / 10, mtb ); + int mlr = std::min( width() / 10, mtb ); if ( rect.width() - cancelRect.x() - cancelRect.width() > mlr ) { // Force right alighnment of cancel button diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index 094c9ac9190..1fb5b6256de 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -89,8 +89,8 @@ struct LessThanTileRequest QPointF p1 = req1.rect.center(); QPointF p2 = req2.rect.center(); // using chessboard distance (loading order more natural than euclidean/manhattan distance) - double d1 = qMax( std::fabs( center.x() - p1.x() ), std::fabs( center.y() - p1.y() ) ); - double d2 = qMax( std::fabs( center.x() - p2.x() ), std::fabs( center.y() - p2.y() ) ); + double d1 = std::max( std::fabs( center.x() - p1.x() ), std::fabs( center.y() - p1.y() ) ); + double d2 = std::max( std::fabs( center.x() - p2.x() ), std::fabs( center.y() - p2.y() ) ); return d1 < d2; } }; @@ -500,7 +500,7 @@ void QgsWmsProvider::setFormatQueryItem( QUrl &url ) static bool _fuzzyContainsRect( const QRectF &r1, const QRectF &r2 ) { - double significantDigits = std::log10( qMax( r1.width(), r1.height() ) ); + double significantDigits = std::log10( std::max( r1.width(), r1.height() ) ); double epsilon = std::pow( 10.0, significantDigits - 5 ); // floats have 6-9 significant digits return r1.contains( r2.adjusted( epsilon, epsilon, -epsilon, -epsilon ) ); } @@ -1760,7 +1760,7 @@ QString QgsWmsProvider::layerMetadata( QgsWmsLayerProperty &layer ) metadata += QLatin1String( "" ); // Layer Coordinate Reference Systems - for ( int j = 0; j < qMin( layer.crs.size(), 10 ); j++ ) + for ( int j = 0; j < std::min( layer.crs.size(), 10 ); j++ ) { metadata += QLatin1String( "" ); metadata += tr( "Available in CRS" ); @@ -4044,7 +4044,7 @@ static QString formatDouble( double x ) { if ( x == 0.0 ) return QStringLiteral( "0" ); - const int numberOfDecimals = qMax( 0, 19 - static_cast( std::ceil( std::log10( std::fabs( x ) ) ) ) ); + const int numberOfDecimals = std::max( 0, 19 - static_cast( std::ceil( std::log10( std::fabs( x ) ) ) ) ); return qgsDoubleToString( x, numberOfDecimals ); } diff --git a/src/server/qgsmslayercache.cpp b/src/server/qgsmslayercache.cpp index fc5ab173861..24e9c8a462c 100644 --- a/src/server/qgsmslayercache.cpp +++ b/src/server/qgsmslayercache.cpp @@ -55,7 +55,7 @@ void QgsMSLayerCache::setMaxCacheLayers( int maxCacheLayers ) void QgsMSLayerCache::insertLayer( const QString &url, const QString &layerName, QgsMapLayer *layer, const QString &configFile, const QList &tempFiles ) { QgsMessageLog::logMessage( "Layer cache: insert Layer '" + layerName + "' configFile: " + configFile, QStringLiteral( "Server" ), QgsMessageLog::INFO ); - if ( mEntries.size() > qMax( mDefaultMaxLayers, mProjectMaxLayers ) ) //force cache layer examination after 10 inserted layers + if ( mEntries.size() > std::max( mDefaultMaxLayers, mProjectMaxLayers ) ) //force cache layer examination after 10 inserted layers { updateEntries(); } @@ -143,7 +143,7 @@ void QgsMSLayerCache::removeProjectFileLayers( const QString &project ) void QgsMSLayerCache::updateEntries() { QgsDebugMsg( "updateEntries" ); - int entriesToDelete = mEntries.size() - qMax( mDefaultMaxLayers, mProjectMaxLayers ); + int entriesToDelete = mEntries.size() - std::max( mDefaultMaxLayers, mProjectMaxLayers ); if ( entriesToDelete < 1 ) { return; diff --git a/src/server/qgsserverprojectutils.cpp b/src/server/qgsserverprojectutils.cpp index e5a71034d29..e4994faf450 100644 --- a/src/server/qgsserverprojectutils.cpp +++ b/src/server/qgsserverprojectutils.cpp @@ -179,7 +179,7 @@ QHash QgsServerProjectUtils::wmsFeatureInfoLayerAliasMap( cons return aliasMap; } - int nMapEntries = qMin( aliasLayerStringList.size(), layerAliasStringList.size() ); + int nMapEntries = std::min( aliasLayerStringList.size(), layerAliasStringList.size() ); for ( int i = 0; i < nMapEntries; ++i ) { aliasMap.insert( aliasLayerStringList.at( i ), layerAliasStringList.at( i ) ); diff --git a/src/server/qgswmsconfigparser.cpp b/src/server/qgswmsconfigparser.cpp index f1632746904..d45f6e76c3e 100644 --- a/src/server/qgswmsconfigparser.cpp +++ b/src/server/qgswmsconfigparser.cpp @@ -314,7 +314,7 @@ QStringList QgsWmsConfigParser::addHighlightLayers( const QMap QString crsString = parameterMap.contains( QStringLiteral( "CRS" ) ) ? parameterMap.value( QStringLiteral( "CRS" ) ) : parameterMap.value( QStringLiteral( "SRS" ) ); - int nHighlights = qMin( geomSplit.size(), symbolSplit.size() ); + int nHighlights = std::min( geomSplit.size(), symbolSplit.size() ); for ( int i = 0; i < nHighlights; ++i ) { //create geometry diff --git a/src/server/qgswmsprojectparser.cpp b/src/server/qgswmsprojectparser.cpp index 01035d0db3c..7f3f4f246ea 100644 --- a/src/server/qgswmsprojectparser.cpp +++ b/src/server/qgswmsprojectparser.cpp @@ -2005,7 +2005,7 @@ QHash QgsWmsProjectParser::featureInfoLayerAliasMap() const layerAliasStringList << layerAliasesValueList.at( i ).toElement().text(); } - int nMapEntries = qMin( aliasLayerStringList.size(), layerAliasStringList.size() ); + int nMapEntries = std::min( aliasLayerStringList.size(), layerAliasStringList.size() ); for ( int i = 0; i < nMapEntries; ++i ) { aliasMap.insert( aliasLayerStringList.at( i ), layerAliasStringList.at( i ) ); diff --git a/src/server/services/wms/qgswmsparameters.cpp b/src/server/services/wms/qgswmsparameters.cpp index b5512f7031e..a6e803b331e 100644 --- a/src/server/services/wms/qgswmsparameters.cpp +++ b/src/server/services/wms/qgswmsparameters.cpp @@ -1372,7 +1372,7 @@ namespace QgsWms QList bufferColors = highlightLabelBufferColorAsColor(); QList bufferSizes = highlightLabelBufferSizeAsFloat(); - int nLayers = qMin( geoms.size(), slds.size() ); + int nLayers = std::min( geoms.size(), slds.size() ); for ( int i = 0; i < nLayers; i++ ) { QgsWmsParametersHighlightLayer param; From 05071434e3ae4592f4c03bd340c12b0889fa259b Mon Sep 17 00:00:00 2001 From: nirvn Date: Fri, 25 Aug 2017 16:31:13 +0700 Subject: [PATCH 136/364] [analysis] fix derivative filter z factor handling --- src/analysis/raster/qgsderivativefilter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analysis/raster/qgsderivativefilter.cpp b/src/analysis/raster/qgsderivativefilter.cpp index ef1e18ac9eb..fbcd9f3e181 100644 --- a/src/analysis/raster/qgsderivativefilter.cpp +++ b/src/analysis/raster/qgsderivativefilter.cpp @@ -87,7 +87,7 @@ float QgsDerivativeFilter::calcFirstDerX( float *x11, float *x21, float *x31, fl return mOutputNodataValue; } - return sum / ( weight * mCellSizeX * mZFactor ); + return sum / ( weight * mCellSizeX ) * mZFactor; } float QgsDerivativeFilter::calcFirstDerY( float *x11, float *x21, float *x31, float *x12, float *x22, float *x32, float *x13, float *x23, float *x33 ) @@ -154,7 +154,7 @@ float QgsDerivativeFilter::calcFirstDerY( float *x11, float *x21, float *x31, fl return mOutputNodataValue; } - return sum / ( weight * mCellSizeY * mZFactor ); + return sum / ( weight * mCellSizeY ) * mZFactor; } From 922ded8d76d0f824f88fa5e360860616121c4fa9 Mon Sep 17 00:00:00 2001 From: nirvn Date: Fri, 25 Aug 2017 16:33:04 +0700 Subject: [PATCH 137/364] [processing] fix z factor minimum value in various raster algorithms --- python/plugins/processing/algs/qgis/Hillshade.py | 2 +- python/plugins/processing/algs/qgis/Relief.py | 2 +- python/plugins/processing/algs/qgis/Ruggedness.py | 2 +- python/plugins/processing/algs/qgis/Slope.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/plugins/processing/algs/qgis/Hillshade.py b/python/plugins/processing/algs/qgis/Hillshade.py index 336b6ae287c..52b6d3153be 100644 --- a/python/plugins/processing/algs/qgis/Hillshade.py +++ b/python/plugins/processing/algs/qgis/Hillshade.py @@ -62,7 +62,7 @@ class Hillshade(QgisAlgorithm): self.tr('Elevation layer'))) self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, self.tr('Z factor'), QgsProcessingParameterNumber.Double, - 1, False, 1, 999999.99)) + 1, False, 0.00, 999999.99)) self.addParameter(QgsProcessingParameterNumber(self.AZIMUTH, self.tr('Azimuth (horizontal angle)'), QgsProcessingParameterNumber.Double, 300, False, 0, 360)) diff --git a/python/plugins/processing/algs/qgis/Relief.py b/python/plugins/processing/algs/qgis/Relief.py index f0e31347c07..bdf48f42388 100644 --- a/python/plugins/processing/algs/qgis/Relief.py +++ b/python/plugins/processing/algs/qgis/Relief.py @@ -106,7 +106,7 @@ class Relief(QgisAlgorithm): self.tr('Elevation layer'))) self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, self.tr('Z factor'), type=QgsProcessingParameterNumber.Double, - minValue=1.0, maxValue=999999.99, defaultValue=1.0)) + minValue=0.00, maxValue=999999.99, defaultValue=1.0)) self.addParameter(QgsProcessingParameterBoolean(self.AUTO_COLORS, self.tr('Generate relief classes automatically'), defaultValue=False)) diff --git a/python/plugins/processing/algs/qgis/Ruggedness.py b/python/plugins/processing/algs/qgis/Ruggedness.py index 5ba1b3b8332..5351d3b6bc2 100644 --- a/python/plugins/processing/algs/qgis/Ruggedness.py +++ b/python/plugins/processing/algs/qgis/Ruggedness.py @@ -61,7 +61,7 @@ class Ruggedness(QgisAlgorithm): self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, self.tr('Z factor'), QgsProcessingParameterNumber.Double, - 1, False, 1, 999999.99)) + 1, False, 0.00, 999999.99)) self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Ruggedness'))) def name(self): diff --git a/python/plugins/processing/algs/qgis/Slope.py b/python/plugins/processing/algs/qgis/Slope.py index fd4281ace0a..6606d219f2c 100644 --- a/python/plugins/processing/algs/qgis/Slope.py +++ b/python/plugins/processing/algs/qgis/Slope.py @@ -61,7 +61,7 @@ class Slope(QgisAlgorithm): self.tr('Elevation layer'))) self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, self.tr('Z factor'), QgsProcessingParameterNumber.Double, - 1, False, 1, 999999.99)) + 1, False, 0.00, 999999.99)) self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Slope'))) def name(self): From c64bc974b56f523b8e7d18f212edf42b88ea55a8 Mon Sep 17 00:00:00 2001 From: nirvn Date: Fri, 25 Aug 2017 17:43:54 +0700 Subject: [PATCH 138/364] [processing] one more z factor minimum value adjustment --- python/plugins/processing/algs/qgis/Aspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/algs/qgis/Aspect.py b/python/plugins/processing/algs/qgis/Aspect.py index c08c94bccbb..613c4e09c9b 100644 --- a/python/plugins/processing/algs/qgis/Aspect.py +++ b/python/plugins/processing/algs/qgis/Aspect.py @@ -60,7 +60,7 @@ class Aspect(QgisAlgorithm): self.tr('Elevation layer'))) self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, self.tr('Z factor'), QgsProcessingParameterNumber.Double, - 1, False, 1, 999999.99)) + 1, False, 0.00, 999999.99)) self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Aspect'))) def name(self): From 7f447374cd2907be837e3f9527096c596508947e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 03:25:02 +1000 Subject: [PATCH 139/364] Remove redundant math constant defines Since we use _USE_MATH_DEFINES for windows builds, these are not required --- python/core/symbology/qgssymbollayer.sip | 1 - src/analysis/raster/qgskde.cpp | 4 ---- src/app/dwg/libdxfrw/drw_base.h | 6 ------ src/app/qgsdecorationnortharrowdialog.cpp | 3 +-- src/app/qgsmaptoolrotatefeature.cpp | 8 +++----- src/app/qgsmaptoolselectradius.cpp | 4 ---- src/core/geometry/qgsinternalgeometryengine.cpp | 2 +- src/core/pal/feature.cpp | 4 ---- src/core/pal/labelposition.cpp | 4 ---- src/core/pal/util.cpp | 12 ------------ src/core/qgsdistancearea.cpp | 5 ----- src/core/symbology/qgspointclusterrenderer.cpp | 4 ---- src/core/symbology/qgspointdisplacementrenderer.cpp | 4 ---- src/core/symbology/qgspointdistancerenderer.cpp | 4 ---- src/core/symbology/qgssymbollayer.h | 5 ----- 15 files changed, 5 insertions(+), 65 deletions(-) diff --git a/python/core/symbology/qgssymbollayer.sip b/python/core/symbology/qgssymbollayer.sip index 512ae02e5f6..cb32aa589a5 100644 --- a/python/core/symbology/qgssymbollayer.sip +++ b/python/core/symbology/qgssymbollayer.sip @@ -11,7 +11,6 @@ - class QgsSymbolLayer { diff --git a/src/analysis/raster/qgskde.cpp b/src/analysis/raster/qgskde.cpp index e63e8477a15..a3ff4341ff0 100644 --- a/src/analysis/raster/qgskde.cpp +++ b/src/analysis/raster/qgskde.cpp @@ -20,10 +20,6 @@ #define NO_DATA -9999 -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - QgsKernelDensityEstimation::QgsKernelDensityEstimation( const QgsKernelDensityEstimation::Parameters ¶meters, const QString &outputFile, const QString &outputFormat ) : mSource( parameters.source ) , mOutputFile( outputFile ) diff --git a/src/app/dwg/libdxfrw/drw_base.h b/src/app/dwg/libdxfrw/drw_base.h index 15691cf399e..a92d59ebfa2 100644 --- a/src/app/dwg/libdxfrw/drw_base.h +++ b/src/app/dwg/libdxfrw/drw_base.h @@ -38,12 +38,6 @@ # define DRW_POSIX #endif -#ifndef M_PI -#define M_PI 3.141592653589793238462643 -#endif -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 -#endif #define M_PIx2 6.283185307179586 // 2*PI #define ARAD 57.29577951308232 diff --git a/src/app/qgsdecorationnortharrowdialog.cpp b/src/app/qgsdecorationnortharrowdialog.cpp index e8378752d84..51fcaf49014 100644 --- a/src/app/qgsdecorationnortharrowdialog.cpp +++ b/src/app/qgsdecorationnortharrowdialog.cpp @@ -152,8 +152,7 @@ void QgsDecorationNorthArrowDialog::drawNorthArrow() myQPainter.rotate( rotation ); //work out how to shift the image so that it appears in the center of the canvas //(x cos a + y sin a - x, -x sin a + y cos a - y) - const double PI = 3.14159265358979323846; - double myRadiansDouble = ( PI / 180 ) * rotation; + double myRadiansDouble = ( M_PI / 180 ) * rotation; int xShift = static_cast( ( ( centerXDouble * std::cos( myRadiansDouble ) ) + ( centerYDouble * std::sin( myRadiansDouble ) ) diff --git a/src/app/qgsmaptoolrotatefeature.cpp b/src/app/qgsmaptoolrotatefeature.cpp index 6c38c3ec4dd..9e71686b193 100644 --- a/src/app/qgsmaptoolrotatefeature.cpp +++ b/src/app/qgsmaptoolrotatefeature.cpp @@ -36,8 +36,6 @@ #include #include -#define PI 3.14159265 - QgsAngleMagnetWidget::QgsAngleMagnetWidget( const QString &label, QWidget *parent ) : QWidget( parent ) { @@ -152,7 +150,7 @@ void QgsMapToolRotateFeature::canvasMoveEvent( QgsMapMouseEvent *e ) { const double XDistance = e->pos().x() - mStPoint.x(); const double YDistance = e->pos().y() - mStPoint.y(); - double rotation = std::atan2( YDistance, XDistance ) * ( 180 / PI ); + double rotation = std::atan2( YDistance, XDistance ) * ( 180 / M_PI ); if ( mRotationWidget ) { @@ -297,7 +295,7 @@ void QgsMapToolRotateFeature::canvasReleaseEvent( QgsMapMouseEvent *e ) double XDistance = mInitialPos.x() - mAnchorPoint->x(); double YDistance = mInitialPos.y() - mAnchorPoint->y() ; - mRotationOffset = std::atan2( YDistance, XDistance ) * ( 180 / PI ); + mRotationOffset = std::atan2( YDistance, XDistance ) * ( 180 / M_PI ); createRotationWidget(); if ( e->modifiers() & Qt::ShiftModifier ) @@ -349,7 +347,7 @@ void QgsMapToolRotateFeature::applyRotation( double rotation ) } //calculations for affine transformation - double angle = -1 * mRotation * ( PI / 180 ); + double angle = -1 * mRotation * ( M_PI / 180 ); QgsPointXY anchorPoint = toLayerCoordinates( vlayer, mStartPointMapCoords ); double a = std::cos( angle ); double b = -1 * std::sin( angle ); diff --git a/src/app/qgsmaptoolselectradius.cpp b/src/app/qgsmaptoolselectradius.cpp index ae57e783c15..efbd52218f6 100644 --- a/src/app/qgsmaptoolselectradius.cpp +++ b/src/app/qgsmaptoolselectradius.cpp @@ -24,10 +24,6 @@ email : jpalmer at linz dot govt dot nz #include #include -#ifndef M_PI -#define M_PI 3.1415926535897931159979634685 -#endif - const int RADIUS_SEGMENTS = 40; QgsMapToolSelectRadius::QgsMapToolSelectRadius( QgsMapCanvas *canvas ) diff --git a/src/core/geometry/qgsinternalgeometryengine.cpp b/src/core/geometry/qgsinternalgeometryengine.cpp index b6f74a28b34..4dd219e0c47 100644 --- a/src/core/geometry/qgsinternalgeometryengine.cpp +++ b/src/core/geometry/qgsinternalgeometryengine.cpp @@ -373,7 +373,7 @@ QgsVector calcMotion( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c, // wonderful nasty hack which has survived through JOSM -> id -> QGIS // to deal with almost-straight segments (angle is closer to 180 than to 90/270). - if ( dotProduct < -0.707106781186547 ) + if ( dotProduct < -M_SQRT1_2 ) dotProduct += 1.0; QgsVector new_v = p + q; diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 00cb25561c3..b8d8997b102 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -44,10 +44,6 @@ #include #include -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - using namespace pal; FeaturePart::FeaturePart( QgsLabelFeature *feat, const GEOSGeometry *geom ) diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index 268a875d76a..329ab81d9f2 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -38,10 +38,6 @@ #include #include -#ifndef M_PI -#define M_PI 3.1415926535897931159979634685 -#endif - using namespace pal; LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed, Quadrant quadrant ) diff --git a/src/core/pal/util.cpp b/src/core/pal/util.cpp index 435e0c866b9..e6693f02f61 100644 --- a/src/core/pal/util.cpp +++ b/src/core/pal/util.cpp @@ -37,18 +37,6 @@ #include "qgslogger.h" #include -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 -#endif - -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 -#endif - void pal::Util::sort( void **items, int N, bool ( *greater )( void *l, void *r ) ) { diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 85a966f3a49..69c40d6ebab 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -34,11 +34,6 @@ #include "qgsunittypes.h" #include "qgsexception.h" -// MSVC compiler doesn't have defined M_PI in math.h -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - #define DEG2RAD(x) ((x)*M_PI/180) #define RAD2DEG(r) (180.0 * (r) / M_PI) #define POW2(x) ((x)*(x)) diff --git a/src/core/symbology/qgspointclusterrenderer.cpp b/src/core/symbology/qgspointclusterrenderer.cpp index 22ca8ac09da..1e2891fc2de 100644 --- a/src/core/symbology/qgspointclusterrenderer.cpp +++ b/src/core/symbology/qgspointclusterrenderer.cpp @@ -24,10 +24,6 @@ #include "qgsproperty.h" #include -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 -#endif - QgsPointClusterRenderer::QgsPointClusterRenderer() : QgsPointDistanceRenderer( QStringLiteral( "pointCluster" ) ) { diff --git a/src/core/symbology/qgspointdisplacementrenderer.cpp b/src/core/symbology/qgspointdisplacementrenderer.cpp index 631d01dda2a..56ba78b2f2c 100644 --- a/src/core/symbology/qgspointdisplacementrenderer.cpp +++ b/src/core/symbology/qgspointdisplacementrenderer.cpp @@ -25,10 +25,6 @@ #include #include -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 -#endif - QgsPointDisplacementRenderer::QgsPointDisplacementRenderer( const QString &labelAttributeName ) : QgsPointDistanceRenderer( QStringLiteral( "pointDisplacement" ), labelAttributeName ) , mPlacement( Ring ) diff --git a/src/core/symbology/qgspointdistancerenderer.cpp b/src/core/symbology/qgspointdistancerenderer.cpp index c9dab04f4de..0cd6bc4cf43 100644 --- a/src/core/symbology/qgspointdistancerenderer.cpp +++ b/src/core/symbology/qgspointdistancerenderer.cpp @@ -27,10 +27,6 @@ #include -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 -#endif - QgsPointDistanceRenderer::QgsPointDistanceRenderer( const QString &rendererName, const QString &labelAttributeName ) : QgsFeatureRenderer( rendererName ) , mLabelAttributeName( labelAttributeName ) diff --git a/src/core/symbology/qgssymbollayer.h b/src/core/symbology/qgssymbollayer.h index 2a370b23d36..c5d756fae18 100644 --- a/src/core/symbology/qgssymbollayer.h +++ b/src/core/symbology/qgssymbollayer.h @@ -15,11 +15,6 @@ #ifndef QGSSYMBOLLAYER_H #define QGSSYMBOLLAYER_H -// MSVC compiler doesn't have defined M_PI in math.h -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - #define DEG2RAD(x) ((x)*M_PI/180) #define DEFAULT_SCALE_METHOD QgsSymbol::ScaleDiameter From 725301ad584967b1c8705f7bc3cf8fc6d358d09a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 03:34:12 +1000 Subject: [PATCH 140/364] Make more use of math constants --- python/core/qgsdistancearea.sip | 4 +-- src/core/composer/qgscomposerarrow.cpp | 6 ++--- src/core/effects/qgsshadoweffect.cpp | 4 +-- src/core/expression/qgsexpressionfunction.cpp | 10 +++---- src/core/geometry/qgscircularstring.cpp | 2 +- src/core/geometry/qgsgeometryutils.cpp | 4 +-- src/core/geometry/qgstriangle.cpp | 8 +++--- src/core/pal/feature.cpp | 26 +++++++++---------- src/core/pal/geomfunction.cpp | 2 +- src/core/pal/labelposition.cpp | 4 +-- src/core/pal/pointset.cpp | 2 +- src/core/qgsdistancearea.cpp | 2 +- src/core/qgsdistancearea.h | 4 +-- src/core/qgstextrenderer.cpp | 8 +++--- src/core/symbology/qgslinesymbollayer.cpp | 2 +- src/plugins/georeferencer/qgsleastsquares.cpp | 2 +- src/server/qgswmsprojectparser.cpp | 2 +- .../services/wms/qgswmsgetcapabilities.cpp | 2 +- 18 files changed, 47 insertions(+), 47 deletions(-) diff --git a/python/core/qgsdistancearea.sip b/python/core/qgsdistancearea.sip index db2c578955a..58757c6942d 100644 --- a/python/core/qgsdistancearea.sip +++ b/python/core/qgsdistancearea.sip @@ -179,7 +179,7 @@ Constructor :rtype: float %End - double measureLineProjected( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI / 2, QgsPointXY *projectedPoint /Out/ = 0 ) const; + double measureLineProjected( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI_2, QgsPointXY *projectedPoint /Out/ = 0 ) const; %Docstring Calculates the distance from one point with distance in meters and azimuth (direction) When the sourceCrs() is geographic, computeSpheroidProject() will be called @@ -279,7 +279,7 @@ Constructor :rtype: float %End - QgsPointXY computeSpheroidProject( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI / 2 ) const; + QgsPointXY computeSpheroidProject( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI_2 ) const; %Docstring Given a location, an azimuth and a distance, computes the location of the projected point. Based on Vincenty's formula diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp index d8320e3293c..c48e26e9e88 100644 --- a/src/core/composer/qgscomposerarrow.cpp +++ b/src/core/composer/qgscomposerarrow.cpp @@ -388,18 +388,18 @@ double QgsComposerArrow::computeMarkerMargin() const { if ( mMarkerMode == DefaultMarker ) { - margin = mPen.widthF() / std::sqrt( 2.0 ) + mArrowHeadWidth / 2.0; + margin = mPen.widthF() * M_SQRT1_2 + mArrowHeadWidth / 2.0; } else if ( mMarkerMode == NoMarker ) { - margin = mPen.widthF() / std::sqrt( 2.0 ); + margin = mPen.widthF() * M_SQRT1_2; } else if ( mMarkerMode == SVGMarker ) { double startMarkerMargin = std::sqrt( 0.25 * ( mStartArrowHeadHeight * mStartArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) ); double stopMarkerMargin = std::sqrt( 0.25 * ( mStopArrowHeadHeight * mStopArrowHeadHeight + mArrowHeadWidth * mArrowHeadWidth ) ); double markerMargin = std::max( startMarkerMargin, stopMarkerMargin ); - margin = std::max( mPen.widthF() / std::sqrt( 2.0 ), markerMargin ); + margin = std::max( mPen.widthF() * M_SQRT1_2, markerMargin ); } } return margin; diff --git a/src/core/effects/qgsshadoweffect.cpp b/src/core/effects/qgsshadoweffect.cpp index d9052ef2dfb..40c2ec0978b 100644 --- a/src/core/effects/qgsshadoweffect.cpp +++ b/src/core/effects/qgsshadoweffect.cpp @@ -56,8 +56,8 @@ void QgsShadowEffect::draw( QgsRenderContext &context ) double offsetDist = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale ); double angleRad = mOffsetAngle * M_PI / 180; // to radians - QPointF transPt( -offsetDist * std::cos( angleRad + M_PI / 2 ), - -offsetDist * std::sin( angleRad + M_PI / 2 ) ); + QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ), + -offsetDist * std::sin( angleRad + M_PI_2 ) ); //transparency, scale QgsImageOperation::multiplyOpacity( colorisedIm, mOpacity ); diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 0a9930b849b..4138956cf30 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -2645,9 +2645,9 @@ static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionConte if ( pt1->y() == pt2->y() ) { if ( pt1->x() < pt2->x() ) - return M_PI / 2; + return M_PI_2; else if ( pt1->x() > pt2->x() ) - return M_PI + ( M_PI / 2 ); + return M_PI + ( M_PI_2 ); else return 0; } @@ -2661,7 +2661,7 @@ static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionConte else /* ( pt1->y() > pt2->y() ) - equality case handled above */ { return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) - + ( M_PI / 2 ); + + ( M_PI_2 ); } } @@ -2675,7 +2675,7 @@ static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionConte else /* ( pt1->y() < pt2->y() ) - equality case handled above */ { return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) - + ( M_PI + ( M_PI / 2 ) ); + + ( M_PI + ( M_PI_2 ) ); } } } @@ -3791,7 +3791,7 @@ const QList &QgsExpression::Functions() << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI / 2 ), fcnProject, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI_2 ), fcnProject, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) ) diff --git a/src/core/geometry/qgscircularstring.cpp b/src/core/geometry/qgscircularstring.cpp index ff23e35e20f..7d50942fd50 100644 --- a/src/core/geometry/qgscircularstring.cpp +++ b/src/core/geometry/qgscircularstring.cpp @@ -772,7 +772,7 @@ void QgsCircularString::sumUpArea( double &sum ) const bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0; bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0; - double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - 1 / M_PI * std::asin( d / radius ); + double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius ); double circleChordArea = 0; if ( circlePointLeftOfLine == centerPointLeftOfLine ) { diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 8724af6500e..8286f313afa 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -1093,7 +1093,7 @@ QgsLineString QgsGeometryUtils::perpendicularSegment( const QgsPoint &p, const Q double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 ) { double at = std::atan2( y2 - y1, x2 - x1 ); - double a = -at + M_PI / 2.0; + double a = -at + M_PI_2; return normalizedAngle( a ); } @@ -1107,7 +1107,7 @@ double QgsGeometryUtils::angleBetweenThreePoints( double x1, double y1, double x double QgsGeometryUtils::linePerpendicularAngle( double x1, double y1, double x2, double y2 ) { double a = lineAngle( x1, y1, x2, y2 ); - a += ( M_PI / 2.0 ); + a += M_PI_2; return normalizedAngle( a ); } diff --git a/src/core/geometry/qgstriangle.cpp b/src/core/geometry/qgstriangle.cpp index e9bff06c876..33970c90f32 100644 --- a/src/core/geometry/qgstriangle.cpp +++ b/src/core/geometry/qgstriangle.cpp @@ -386,9 +386,9 @@ QVector QgsTriangle::angles() const double a2 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( ax, ay, bx, by, cx, cy ), M_PI ); double a3 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( bx, by, cx, cy, ax, ay ), M_PI ); - angles.append( ( a1 > M_PI / 2 ? a1 - M_PI / 2 : a1 ) ); - angles.append( ( a2 > M_PI / 2 ? a2 - M_PI / 2 : a2 ) ); - angles.append( ( a3 > M_PI / 2 ? a3 - M_PI / 2 : a3 ) ); + angles.append( ( a1 > M_PI_2 ? a1 - M_PI_2 : a1 ) ); + angles.append( ( a2 > M_PI_2 ? a2 - M_PI_2 : a2 ) ); + angles.append( ( a3 > M_PI_2 ? a3 - M_PI_2 : a3 ) ); return angles; } @@ -419,7 +419,7 @@ bool QgsTriangle::isRight( double angleTolerance ) const QVector::iterator ita = a.begin(); while ( ita != a.end() ) { - if ( qgsDoubleNear( *ita, M_PI / 2.0, angleTolerance ) ) + if ( qgsDoubleNear( *ita, M_PI_2, angleTolerance ) ) return true; ita++; } diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index b8d8997b102..6a39b7cf1d6 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -443,7 +443,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo double candidateAngleIncrement = 2 * M_PI / numberCandidates; /* angle bw 2 pos */ /* various angles */ - double a90 = M_PI / 2; + double a90 = M_PI_2; double a180 = M_PI; double a270 = a180 + a90; double a360 = 2 * M_PI; @@ -470,7 +470,7 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo int i; double angleToCandidate; - for ( i = 0, angleToCandidate = M_PI / 4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement ) + for ( i = 0, angleToCandidate = M_PI_4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement ) { double labelX = x; double labelY = y; @@ -763,12 +763,12 @@ int FeaturePart::createCandidatesAlongLineNearStraightSegments( QListlayer()->arrangement() == QgsPalLayerSettings::Line ) { // find out whether the line direction for this candidate is from right to left - bool isRightToLeft = ( angle > M_PI / 2 || angle <= -M_PI / 2 ); + bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 ); // meaning of above/below may be reversed if using map orientation and the line has right-to-left direction bool reversed = ( ( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false ); bool aboveLine = ( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ); @@ -908,12 +908,12 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList & else angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX ); - beta = angle + M_PI / 2; + beta = angle + M_PI_2; if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Line ) { // find out whether the line direction for this candidate is from right to left - bool isRightToLeft = ( angle > M_PI / 2 || angle <= -M_PI / 2 ); + bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 ); // meaning of above/below may be reversed if using map orientation and the line has right-to-left direction bool reversed = ( ( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false ); bool aboveLine = ( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ); @@ -1125,7 +1125,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI; while ( render_angle < 0 ) render_angle += 2 * M_PI; - if ( render_angle > M_PI / 2 && render_angle < 1.5 * M_PI ) + if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI ) slp->incrementUpsideDownCharCount(); } // END FOR @@ -1136,7 +1136,7 @@ LabelPosition *FeaturePart::curvedPlacementAtOffset( PointSet *path_positions, d static LabelPosition *_createCurvedCandidate( LabelPosition *lp, double angle, double dist ) { LabelPosition *newLp = new LabelPosition( *lp ); - newLp->offsetPosition( dist * std::cos( angle + M_PI / 2 ), dist * std::sin( angle + M_PI / 2 ) ); + newLp->offsetPosition( dist * std::cos( angle + M_PI_2 ), dist * std::sin( angle + M_PI_2 ) ); return newLp; } @@ -1424,18 +1424,18 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin } else if ( box->length > 1.5 * labelWidth && box->width > 1.5 * labelWidth ) { - if ( box->alpha <= M_PI / 4 ) + if ( box->alpha <= M_PI_4 ) { alpha = box->alpha; } else { - alpha = box->alpha - M_PI / 2; + alpha = box->alpha - M_PI_2; } } else if ( box->length > box->width ) { - alpha = box->alpha - M_PI / 2; + alpha = box->alpha - M_PI_2; } else { @@ -1464,8 +1464,8 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition *> &lPos, Poin for ( py = py0; py <= box->length; py += dy ) { - rx = std::cos( box->alpha ) * px + std::cos( box->alpha - M_PI / 2 ) * py; - ry = std::sin( box->alpha ) * px + std::sin( box->alpha - M_PI / 2 ) * py; + rx = std::cos( box->alpha ) * px + std::cos( box->alpha - M_PI_2 ) * py; + ry = std::sin( box->alpha ) * px + std::sin( box->alpha - M_PI_2 ) * py; rx += box->x[0]; ry += box->y[0]; diff --git a/src/core/pal/geomfunction.cpp b/src/core/pal/geomfunction.cpp index d6a24a54c4f..dd2642c9543 100644 --- a/src/core/pal/geomfunction.cpp +++ b/src/core/pal/geomfunction.cpp @@ -329,7 +329,7 @@ bool GeomFunction::containsCandidate( const GEOSPreparedGeometry *geom, double x GEOSCoordSeq_setY_r( geosctxt, coord, 0, y ); if ( !qgsDoubleNear( alpha, 0.0 ) ) { - double beta = alpha + ( M_PI / 2 ); + double beta = alpha + M_PI_2; double dx1 = std::cos( alpha ) * width; double dy1 = std::sin( alpha ) * width; double dx2 = std::cos( beta ) * height; diff --git a/src/core/pal/labelposition.cpp b/src/core/pal/labelposition.cpp index 329ab81d9f2..efa468a49b4 100644 --- a/src/core/pal/labelposition.cpp +++ b/src/core/pal/labelposition.cpp @@ -70,7 +70,7 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, while ( this->alpha < 0 ) this->alpha += 2 * M_PI; - double beta = this->alpha + ( M_PI / 2 ); + double beta = this->alpha + M_PI_2; double dx1, dx2, dy1, dy2; @@ -94,7 +94,7 @@ LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, // upside down ? (curved labels are always correct) if ( !feature->layer()->isCurved() && - this->alpha > M_PI / 2 && this->alpha <= 3 * M_PI / 2 ) + this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 ) { if ( feature->showUprightLabels() ) { diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index dc2ab605b06..bd6b1bb98c5 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -656,7 +656,7 @@ CHullBox *PointSet::compute_chull_bbox() for ( i = 0; i < 16; i += 4 ) { - alpha_seg = ( ( i / 4 > 0 ? ( i / 4 ) - 1 : 3 ) ) * M_PI / 2 + alpha; + alpha_seg = ( ( i / 4 > 0 ? ( i / 4 ) - 1 : 3 ) ) * M_PI_2 + alpha; best_cp = DBL_MAX; for ( j = 0; j < nbPoints; j++ ) diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 69c40d6ebab..d17b3d678c3 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -662,7 +662,7 @@ void QgsDistanceArea::computeAreaInit() m_QbarC = - ( 3.0 / 25.0 ) * e4 - ( 12.0 / 35.0 ) * e6; m_QbarD = ( 4.0 / 49.0 ) * e6; - m_Qp = getQ( M_PI / 2 ); + m_Qp = getQ( M_PI_2 ); m_E = 4 * M_PI * m_Qp * m_AE; if ( m_E < 0.0 ) m_E = -m_E; diff --git a/src/core/qgsdistancearea.h b/src/core/qgsdistancearea.h index df7ff88b081..6cfdb973b4c 100644 --- a/src/core/qgsdistancearea.h +++ b/src/core/qgsdistancearea.h @@ -193,7 +193,7 @@ class CORE_EXPORT QgsDistanceArea * \see sourceCrs() * \see computeSpheroidProject() */ - double measureLineProjected( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI / 2, QgsPointXY *projectedPoint SIP_OUT = nullptr ) const; + double measureLineProjected( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI_2, QgsPointXY *projectedPoint SIP_OUT = nullptr ) const; /** * Returns the units of distance for length calculations made by this object. @@ -286,7 +286,7 @@ class CORE_EXPORT QgsDistanceArea * \param azimuth - azimuth in radians, clockwise from North * \return p2 - location of projected point as longitude/latitude. */ - QgsPointXY computeSpheroidProject( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI / 2 ) const; + QgsPointXY computeSpheroidProject( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI_2 ) const; private: diff --git a/src/core/qgstextrenderer.cpp b/src/core/qgstextrenderer.cpp index a92a5e528f3..8a12c027034 100644 --- a/src/core/qgstextrenderer.cpp +++ b/src/core/qgstextrenderer.cpp @@ -2198,8 +2198,8 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse ) { // start with label bound by ellipse - h = h / std::sqrt( 2.0 ) * 2; - w = w / std::sqrt( 2.0 ) * 2; + h = h * M_SQRT1_2 * 2; + w = w * M_SQRT1_2 * 2; } double bufferWidth = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), @@ -2388,8 +2388,8 @@ void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRender angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 ); } - QPointF transPt( -offsetDist * std::cos( angleRad + M_PI / 2 ), - -offsetDist * std::sin( angleRad + M_PI / 2 ) ); + QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ), + -offsetDist * std::sin( angleRad + M_PI_2 ) ); p->save(); p->setRenderHint( QPainter::SmoothPixmapTransform ); diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 37208659c39..db2837fb316 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -672,7 +672,7 @@ class MyLine // return angle in radians double angle() { - double a = ( mVertical ? M_PI / 2 : std::atan( mT ) ); + double a = ( mVertical ? M_PI_2 : std::atan( mT ) ); if ( !mIncreasing ) a += M_PI; diff --git a/src/plugins/georeferencer/qgsleastsquares.cpp b/src/plugins/georeferencer/qgsleastsquares.cpp index 2fd9c72a687..db938c59a8f 100644 --- a/src/plugins/georeferencer/qgsleastsquares.cpp +++ b/src/plugins/georeferencer/qgsleastsquares.cpp @@ -201,7 +201,7 @@ void normalizeCoordinates( const QVector &coords, QVectormaximumScale() * SCALE_TO_SCALEHINT ) ); diff --git a/src/server/services/wms/qgswmsgetcapabilities.cpp b/src/server/services/wms/qgswmsgetcapabilities.cpp index 456483d9533..3b54cb9a8cb 100644 --- a/src/server/services/wms/qgswmsgetcapabilities.cpp +++ b/src/server/services/wms/qgswmsgetcapabilities.cpp @@ -1001,7 +1001,7 @@ namespace QgsWms if ( version == QLatin1String( "1.1.1" ) ) { double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis - double SCALE_TO_SCALEHINT = OGC_PX_M * std::sqrt( 2.0 ); + double SCALE_TO_SCALEHINT = OGC_PX_M * M_SQRT2; QDomElement scaleHintElem = doc.createElement( QStringLiteral( "ScaleHint" ) ); scaleHintElem.setAttribute( QStringLiteral( "min" ), QString::number( l->maximumScale() * SCALE_TO_SCALEHINT ) ); From 43200ea8e98e96150490311a6e40fbc27e8a4612 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 03:18:31 +1000 Subject: [PATCH 141/364] Remove some redundant and unused MathUtils methods --- src/analysis/interpolation/MathUtils.cc | 70 +------------------------ src/analysis/interpolation/MathUtils.h | 6 --- 2 files changed, 1 insertion(+), 75 deletions(-) diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cc index ce659b66f14..cf8f5a93c89 100644 --- a/src/analysis/interpolation/MathUtils.cc +++ b/src/analysis/interpolation/MathUtils.cc @@ -220,22 +220,7 @@ double MathUtils::distPointFromLine( QgsPoint *thepoint, QgsPoint *p1, QgsPoint int MathUtils::faculty( int n ) { - if ( n < 0 )//Is faculty also defined for negative integers? - { - QgsDebugMsg( "Error, faculty of a negative integer requested!" ); - return 0; - } - int i; - int result = n; - - if ( n == 0 || n == 1 ) - {return 1;}//faculty of 0 is 1! - - for ( i = n - 1; i >= 2; i-- ) - { - result *= i; - } - return result; + return std::tgamma( n + 1 ); } bool MathUtils::inCircle( QgsPoint *testp, QgsPoint *p1, QgsPoint *p2, QgsPoint *p3 ) @@ -431,59 +416,6 @@ int MathUtils::lower( int n, int i ) } } -double MathUtils::max( double x, double y ) -{ - if ( x > y ) - { - return x; - } - else if ( y > x ) - { - return y; - } - else//if both are equal - { - return x; - } -} -double MathUtils::min( double x, double y ) -{ - if ( x < y ) - { - return x; - } - else if ( y < x ) - { - return y; - } - else//if both are equal - { - return x; - } -} - -double MathUtils::power( double a, int b ) -{ - if ( b == 0 ) - { - return 1; - } - double tmp = a; - for ( int i = 2; i <= std::abs( b ); i++ ) - { - - a *= tmp; - } - if ( b > 0 ) - { - return a; - } - else - { - return ( 1.0 / a ); - } -} - double MathUtils::triArea( QgsPoint *pa, QgsPoint *pb, QgsPoint *pc ) { if ( pa && pb && pc ) diff --git a/src/analysis/interpolation/MathUtils.h b/src/analysis/interpolation/MathUtils.h index 790e691f666..14baab7cb12 100644 --- a/src/analysis/interpolation/MathUtils.h +++ b/src/analysis/interpolation/MathUtils.h @@ -54,12 +54,6 @@ namespace MathUtils bool ANALYSIS_EXPORT lineIntersection( QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4, QgsPoint *intersection_point ); //! Lower function int ANALYSIS_EXPORT lower( int n, int i ); - //! Returns the maximum of two doubles or the first argument if both are equal - double ANALYSIS_EXPORT max( double x, double y ); - //! Returns the minimum of two doubles or the first argument if both are equal - double ANALYSIS_EXPORT min( double x, double y ); - //! Power function for integer coefficients - double ANALYSIS_EXPORT power( double a, int b );//calculates a power b //! Returns the area of a triangle. If the points are ordered counterclockwise, the value will be positiv. If they are ordered clockwise, the value will be negativ double ANALYSIS_EXPORT triArea( QgsPoint *pa, QgsPoint *pb, QgsPoint *pc ); //! Calculates the z-component of a vector with coordinates 'x' and 'y'which is in the same tangent plane as the tangent vectors 'v1' and 'v2'. The result is assigned to 'result' From 48c953991e15b56e9e9300ec8007d44ce1c64966 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 21 Jul 2017 17:44:42 +0200 Subject: [PATCH 142/364] Refactor curveToLine to emit equidistant segments and fix some issues Fixes #16717 Fixes #16722 Include tests --- src/core/geometry/qgscircularstring.cpp | 2 + src/core/geometry/qgsgeometryutils.cpp | 137 ++++++++++++++++++++---- tests/src/core/CMakeLists.txt | 1 + tests/src/core/testqgscurve.cpp | 119 ++++++++++++++++++++ tests/src/core/testqgsgeometry.cpp | 1 + 5 files changed, 240 insertions(+), 20 deletions(-) create mode 100644 tests/src/core/testqgscurve.cpp diff --git a/src/core/geometry/qgscircularstring.cpp b/src/core/geometry/qgscircularstring.cpp index ff23e35e20f..f69a693f70b 100644 --- a/src/core/geometry/qgscircularstring.cpp +++ b/src/core/geometry/qgscircularstring.cpp @@ -23,6 +23,7 @@ #include "qgsmaptopixel.h" #include "qgspoint.h" #include "qgswkbptr.h" +#include "qgslogger.h" #include #include @@ -353,6 +354,7 @@ QgsLineString *QgsCircularString::curveToLine( double tolerance, SegmentationTol QgsPointSequence points; int nPoints = numPoints(); + QgsDebugMsg( QString( "curveToLine input: %1" ) .arg( asWkt( 2 ) ) ); for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 ) { QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() ); diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 8724af6500e..b2ca7524798 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com #include "qgsgeometrycollection.h" #include "qgslinestring.h" #include "qgswkbptr.h" +#include "qgslogger.h" #include #include @@ -631,6 +632,7 @@ double QgsGeometryUtils::circleTangentDirection( const QgsPoint &tangentPoint, c void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance, QgsAbstractGeometry::SegmentationToleranceType toleranceType, bool hasZ, bool hasM ) { + bool reversed = false; bool clockwise = false; int segSide = segmentSide( p1, p3, p2 ); if ( segSide == -1 ) @@ -638,9 +640,23 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co clockwise = true; } - QgsPoint circlePoint1 = clockwise ? p3 : p1; - QgsPoint circlePoint2 = p2; - QgsPoint circlePoint3 = clockwise ? p1 : p3 ; + QgsPoint circlePoint1; + const QgsPoint circlePoint2 = p2; + QgsPoint circlePoint3; + + if ( clockwise ) + { + // Reverse ! + circlePoint1 = p3; + circlePoint3 = p1; + clockwise = false; + reversed = true; + } + else + { + circlePoint1 = p1; + circlePoint3 = p3; + } //adapted code from PostGIS double radius = 0; @@ -648,8 +664,12 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co double centerY = 0; circleCenterRadius( circlePoint1, circlePoint2, circlePoint3, radius, centerX, centerY ); + QgsDebugMsg( QString( "Center: POINT(%1 %2) - Radius: %3 - Clockwise: %4" ) + .arg( centerX ) .arg( centerY ) .arg( radius ) .arg( clockwise ) ); + if ( circlePoint1 != circlePoint3 && ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) ) //points are colinear { + QgsDebugMsg( QString( "Collinear curve" ) ); points.append( p1 ); points.append( p2 ); points.append( p3 ); @@ -662,24 +682,65 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co double halfAngle = std::acos( -tolerance / radius + 1 ); increment = 2 * halfAngle; } + QgsDebugMsg( QString( "Increment: %1" ).arg( increment ) ); //angles of pt1, pt2, pt3 double a1 = std::atan2( circlePoint1.y() - centerY, circlePoint1.x() - centerX ); double a2 = std::atan2( circlePoint2.y() - centerY, circlePoint2.x() - centerX ); double a3 = std::atan2( circlePoint3.y() - centerY, circlePoint3.x() - centerX ); - /* Adjust a3 up so we can increment from a1 to a3 cleanly */ - if ( a3 <= a1 ) - a3 += 2.0 * M_PI; - if ( a2 < a1 ) - a2 += 2.0 * M_PI; + QgsDebugMsg( QString( "a1:%1 (%4) a2:%2 (%5) a3:%3 (%6)" ) + .arg( a1 ).arg( a2 ).arg( a3 ) + .arg( a1 * 180 / M_PI ).arg( a2 * 180 / M_PI ).arg( a3 * 180 / M_PI ) + ); + + // Make segmentation symmetric + const bool symmetric = true; + if ( symmetric ) + { + double angle = clockwise ? a1 - a3 : a3 - a1; + if ( angle < 0 ) angle += M_PI * 2; + QgsDebugMsg( QString( "total angle: %1 (%2)" ) + .arg( angle ) .arg( angle * 180 / M_PI ) + ); + + /* Number of segments in output */ + int segs = ceil( angle / increment ); + /* Tweak increment to be regular for all the arc */ + increment = angle / segs; + + QgsDebugMsg( QString( "symmetric adjusted increment:%1" ) .arg( increment ) ); + } + + if ( clockwise ) + { + increment *= -1; + /* Adjust a3 down so we can increment from a1 to a3 cleanly */ + if ( a3 > a1 ) + a3 -= 2.0 * M_PI; + if ( a2 > a1 ) + a2 -= 2.0 * M_PI; + } + else + { + /* Adjust a3 up so we can increment from a1 to a3 cleanly */ + if ( a3 < a1 ) + a3 += 2.0 * M_PI; + if ( a2 < a1 ) + a2 += 2.0 * M_PI; + } + + QgsDebugMsg( QString( "ADJUSTED - a1:%1 (%4) a2:%2 (%5) a3:%3 (%6)" ) + .arg( a1 ).arg( a2 ).arg( a3 ) + .arg( a1 * 180 / M_PI ).arg( a2 * 180 / M_PI ).arg( a3 * 180 / M_PI ) + ); double x, y; double z = 0; double m = 0; QList stringPoints; - stringPoints.insert( clockwise ? 0 : stringPoints.size(), circlePoint1 ); + stringPoints.insert( 0, circlePoint1 ); if ( circlePoint2 != circlePoint3 && circlePoint1 != circlePoint2 ) //draw straight line segment if two points have the same position { QgsWkbTypes::Type pointWkbType = QgsWkbTypes::Point; @@ -688,30 +749,58 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co if ( hasM ) pointWkbType = QgsWkbTypes::addM( pointWkbType ); + QgsDebugMsg( QString( "a1:%1 (%2), a3:%3 (%4), inc:%5, shi:?, cw:%6" ) + . arg( a1 ). arg( a1 * 180 / M_PI ) + . arg( a3 ). arg( a3 * 180 / M_PI ) + . arg( increment ). arg( clockwise ) + ); + //make sure the curve point p2 is part of the segmentized vertices. But only if p1 != p3 + // TODO: make this a parameter bool addP2 = true; if ( qgsDoubleNear( circlePoint1.x(), circlePoint3.x() ) && qgsDoubleNear( circlePoint1.y(), circlePoint3.y() ) ) { addP2 = false; } + addP2 = false; - for ( double angle = a1 + increment; angle < a3; angle += increment ) + // As we're adding the last point in any case, we'll avoid + // including a point which is at less than 1% increment distance + // from it (may happen to find them due to numbers approximation). + // NOTE that this effectively allows in output some segments which + // are more distant than requested. This is at most 1% off + // from requested MaxAngle and less for MaxError. + double tolError = increment / 100; + double stopAngle = clockwise ? a3 - tolError : a3 - tolError; + QgsDebugMsg( QString( "stopAngle: %1 (%2)" ) . arg( stopAngle ) .arg( stopAngle * 180 / M_PI ) ); + for ( double angle = a1 + increment; clockwise ? angle > stopAngle : angle < stopAngle; angle += increment ) { - if ( ( addP2 && angle > a2 ) ) + if ( addP2 && angle > a2 ) { - stringPoints.insert( clockwise ? 0 : stringPoints.size(), circlePoint2 ); + if ( clockwise ) + { + if ( *stringPoints.begin() != circlePoint2 ) + { + QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); + stringPoints.insert( 0, circlePoint2 ); + } + } + else + { + if ( *stringPoints.rbegin() != circlePoint2 ) + { + QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); + stringPoints.insert( stringPoints.size(), circlePoint2 ); + } + } addP2 = false; } + QgsDebugMsg( QString( "SA - %1 (%2)" ) . arg( angle ) .arg( angle * 180 / M_PI ) ); + x = centerX + radius * std::cos( angle ); y = centerY + radius * std::sin( angle ); - if ( !hasZ && !hasM ) - { - stringPoints.insert( clockwise ? 0 : stringPoints.size(), QgsPoint( x, y ) ); - continue; - } - if ( hasZ ) { z = interpolateArcValue( angle, a1, a2, a3, circlePoint1.z(), circlePoint2.z(), circlePoint3.z() ); @@ -721,10 +810,18 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co m = interpolateArcValue( angle, a1, a2, a3, circlePoint1.m(), circlePoint2.m(), circlePoint3.m() ); } - stringPoints.insert( clockwise ? 0 : stringPoints.size(), QgsPoint( pointWkbType, x, y, z, m ) ); + QgsDebugMsg( QString( " -> POINT(%1 %2)" ) . arg( x ) .arg( y ) ); + stringPoints.insert( stringPoints.size(), QgsPoint( pointWkbType, x, y, z, m ) ); } } - stringPoints.insert( clockwise ? 0 : stringPoints.size(), circlePoint3 ); + QgsDebugMsg( QString( " appending last point -> POINT(%1 %2)" ) . arg( circlePoint3.x() ) .arg( circlePoint3.y() ) ); + stringPoints.insert( stringPoints.size(), circlePoint3 ); + + // TODO: check if or implement QgsPointSequence directly taking an iterator to append + if ( reversed ) + { + std::reverse( stringPoints.begin(), stringPoints.end() ); + } points.append( stringPoints ); } diff --git a/tests/src/core/CMakeLists.txt b/tests/src/core/CMakeLists.txt index de87bf719f2..588128d10c3 100755 --- a/tests/src/core/CMakeLists.txt +++ b/tests/src/core/CMakeLists.txt @@ -100,6 +100,7 @@ SET(TESTS testcontrastenhancements.cpp testqgscoordinatereferencesystem.cpp testqgscoordinatetransform.cpp + testqgscurve.cpp testqgsdatadefinedsizelegend.cpp testqgsdataitem.cpp testqgsdatasourceuri.cpp diff --git a/tests/src/core/testqgscurve.cpp b/tests/src/core/testqgscurve.cpp new file mode 100644 index 00000000000..74c910393af --- /dev/null +++ b/tests/src/core/testqgscurve.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + testqgscurve.cpp + -------------------------------------- + Date : 21 July 2017 + Copyright : (C) 2017 by Sandro Santilli + Email : strk @ kbt.io + *************************************************************************** + * * + * 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 +#include +#include // for unique_ptr + +//qgis includes... +#include "qgsabstractgeometry.h" +#include "qgscircularstring.h" +#include "qgsgeometry.h" +#include "qgsgeometryfactory.h" +#include "qgslinestring.h" +#include "qgspoint.h" +#include "qgstest.h" +#include "qgstestutils.h" + +/** \ingroup UnitTests + * This is a unit test for the operations on curve geometries + */ +class TestQgsCurve : public QObject +{ + Q_OBJECT + + public: + TestQgsCurve() {}; + + 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. + + void curveToLine(); +}; + + +#define TEST_C2L(circularString, tol, toltype, exp, prec) { \ + std::unique_ptr< QgsLineString > lineString( \ + circularString->curveToLine(tol, toltype) \ + ); \ + QVERIFY( lineString.get() ); \ + QString wkt_out = lineString->asWkt(prec); \ + QCOMPARE( wkt_out, QString( exp ) ); \ + /* Test reverse */ \ + std::unique_ptr< QgsCircularString > reversed( \ + circularString->reversed() \ + ); \ + lineString.reset( \ + reversed->curveToLine(tol, toltype) \ + ); \ + wkt_out = lineString->asWkt(prec); \ + lineString.reset( \ + reversed->curveToLine(tol, toltype) \ + ); \ + std::unique_ptr< QgsLineString > expgeom( \ + dynamic_cast( \ + QgsGeometryFactory::geomFromWkt( exp ) \ + ) \ + ); \ + expgeom.reset( expgeom->reversed() ); \ + QString exp_reversed = expgeom->asWkt(prec); \ + QCOMPARE( wkt_out, exp_reversed ); \ + } + +void TestQgsCurve::curveToLine() +{ + std::unique_ptr< QgsCircularString > circularString; + + /* input: 2 quadrants arc (180 degrees, PI radians) */ + circularString.reset( dynamic_cast( + QgsGeometryFactory::geomFromWkt( QString( + "CIRCULARSTRING(0 0,100 100,200 0)" + ) ) + ) ); + QVERIFY( circularString.get() ); + + /* op: Maximum of 10 units of difference, symmetric */ + TEST_C2L( circularString, 10, QgsAbstractGeometry::MaximumDifference, + "LineString (0 0, 29.29 70.71, 100 100, 170.71 70.71, 200 0)", 2 ); + + /* op: Maximum of M_PI / 8 degrees of angle, (a)symmetric */ + /* See https://issues.qgis.org/issues/16717 */ + TEST_C2L( circularString, M_PI / 8, QgsAbstractGeometry::MaximumAngle, + "LineString (0 0, 7.61 38.27, 29.29 70.71, 61.73 92.39, 100 100, 138.27 92.39, 170.71 70.71, 192.39 38.27, 200 0)", 2 ); + + /* op: Maximum of 70 degrees of angle, symmetric */ + /* See https://issues.qgis.org/issues/16722 */ + TEST_C2L( circularString, 70 * M_PI / 180, QgsAbstractGeometry::MaximumAngle, + "LineString (0 0, 50 86.6, 150 86.6, 200 0)", 2 ); + + /* input: 2 arcs of 2 quadrants each (180 degrees + 180 degrees other direction) */ + circularString.reset( dynamic_cast( + QgsGeometryFactory::geomFromWkt( QString( + "CIRCULARSTRING(0 0,100 100,200 0,300 -100,400 0)" + ) ) + ) ); + QVERIFY( circularString.get() ); + + /* op: Maximum of M_PI / 3 degrees of angle */ + TEST_C2L( circularString, M_PI / 3, QgsAbstractGeometry::MaximumAngle, + "LineString (0 0, 50 86.6, 150 86.6, 200 0, 200 0, 250 -86.6, 350 -86.6, 400 0)", 2 ); +} + + +QGSTEST_MAIN( TestQgsCurve ) +#include "testqgscurve.moc" diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index c4a738d31a1..18e384d2174 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -5319,6 +5319,7 @@ void TestQgsGeometry::directionNeutralSegmentation() QgsLineString *CCWLineString = CCWCircularString->curveToLine(); QgsLineString *reversedCCWLineString = CCWLineString->reversed(); + QCOMPARE( CWLineString->asWkt(), reversedCCWLineString->asWkt() ); bool equal = ( *CWLineString == *reversedCCWLineString ); delete CWCircularString; From 320c305d929fbabd79120dea360fdb50f266d82c Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Tue, 22 Aug 2017 19:14:37 +0200 Subject: [PATCH 143/364] Fix build and multi-arc duplicated points --- src/core/geometry/qgsgeometryutils.cpp | 5 +++-- tests/src/core/testqgscurve.cpp | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index b2ca7524798..2ce680b36f7 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -779,7 +779,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co { if ( clockwise ) { - if ( *stringPoints.begin() != circlePoint2 ) + if ( stringPoints.empty() || stringPoints.front() != circlePoint2 ) { QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); stringPoints.insert( 0, circlePoint2 ); @@ -787,7 +787,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co } else { - if ( *stringPoints.rbegin() != circlePoint2 ) + if ( stringPoints.empty() || stringPoints.back() != circlePoint2 ) { QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); stringPoints.insert( stringPoints.size(), circlePoint2 ); @@ -822,6 +822,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co { std::reverse( stringPoints.begin(), stringPoints.end() ); } + if ( ! points.empty() && stringPoints.front() == points.back() ) stringPoints.pop_front(); points.append( stringPoints ); } diff --git a/tests/src/core/testqgscurve.cpp b/tests/src/core/testqgscurve.cpp index 74c910393af..03741a2dcfd 100644 --- a/tests/src/core/testqgscurve.cpp +++ b/tests/src/core/testqgscurve.cpp @@ -67,7 +67,7 @@ class TestQgsCurve : public QObject ); \ std::unique_ptr< QgsLineString > expgeom( \ dynamic_cast( \ - QgsGeometryFactory::geomFromWkt( exp ) \ + QgsGeometryFactory::geomFromWkt( exp ).release() \ ) \ ); \ expgeom.reset( expgeom->reversed() ); \ @@ -80,10 +80,11 @@ void TestQgsCurve::curveToLine() std::unique_ptr< QgsCircularString > circularString; /* input: 2 quadrants arc (180 degrees, PI radians) */ - circularString.reset( dynamic_cast( + circularString.reset( dynamic_cast< QgsCircularString *>( QgsGeometryFactory::geomFromWkt( QString( "CIRCULARSTRING(0 0,100 100,200 0)" - ) ) + ) + ).release() ) ); QVERIFY( circularString.get() ); @@ -105,13 +106,13 @@ void TestQgsCurve::curveToLine() circularString.reset( dynamic_cast( QgsGeometryFactory::geomFromWkt( QString( "CIRCULARSTRING(0 0,100 100,200 0,300 -100,400 0)" - ) ) + ) ).release() ) ); QVERIFY( circularString.get() ); /* op: Maximum of M_PI / 3 degrees of angle */ TEST_C2L( circularString, M_PI / 3, QgsAbstractGeometry::MaximumAngle, - "LineString (0 0, 50 86.6, 150 86.6, 200 0, 200 0, 250 -86.6, 350 -86.6, 400 0)", 2 ); + "LineString (0 0, 50 86.6, 150 86.6, 200 0, 250 -86.6, 350 -86.6, 400 0)", 2 ); } From d3558a19aafd0ceae33cc0fedbbfa3c33fee9e50 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 15:26:57 +0200 Subject: [PATCH 144/364] Remove test off-variable --- src/core/geometry/qgsgeometryutils.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 2ce680b36f7..1cbc15d122c 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -762,7 +762,6 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co { addP2 = false; } - addP2 = false; // As we're adding the last point in any case, we'll avoid // including a point which is at less than 1% increment distance From cf5a439feba735ddc51fac254a4a087c78d4ebdf Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 16:19:19 +0200 Subject: [PATCH 145/364] Update expected results now that we're forcing control point in output --- tests/src/core/testqgscurve.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/core/testqgscurve.cpp b/tests/src/core/testqgscurve.cpp index 03741a2dcfd..dcd9bad2eb0 100644 --- a/tests/src/core/testqgscurve.cpp +++ b/tests/src/core/testqgscurve.cpp @@ -100,7 +100,7 @@ void TestQgsCurve::curveToLine() /* op: Maximum of 70 degrees of angle, symmetric */ /* See https://issues.qgis.org/issues/16722 */ TEST_C2L( circularString, 70 * M_PI / 180, QgsAbstractGeometry::MaximumAngle, - "LineString (0 0, 50 86.6, 150 86.6, 200 0)", 2 ); + "LineString (0 0, 50 86.6, 100 100, 150 86.6, 200 0)", 2 ); /* input: 2 arcs of 2 quadrants each (180 degrees + 180 degrees other direction) */ circularString.reset( dynamic_cast( @@ -112,7 +112,7 @@ void TestQgsCurve::curveToLine() /* op: Maximum of M_PI / 3 degrees of angle */ TEST_C2L( circularString, M_PI / 3, QgsAbstractGeometry::MaximumAngle, - "LineString (0 0, 50 86.6, 150 86.6, 200 0, 250 -86.6, 350 -86.6, 400 0)", 2 ); + "LineString (0 0, 50 86.6, 100 100, 150 86.6, 200 0, 250 -86.6, 300 -100, 350 -86.6, 400 0)", 2 ); } From 8363d2f3d0005d5cabf6952cd96fa019362eef88 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 18:07:23 +0200 Subject: [PATCH 146/364] Update expected centroid, interpolated angle and located point for reference curves These values change because they are computed on the *linearization* of those curves, and refactoring linearization codes results in slighly different values. NOTE: adding or not adding the control point would also affect these results --- tests/src/python/test_qgsgeometry.py | 4 ++-- tests/testdata/geom_data.csv | 2 +- tests/testdata/polys_overlapping_with_id.dbf | Bin 1328 -> 1328 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/python/test_qgsgeometry.py b/tests/src/python/test_qgsgeometry.py index 8fb43724b46..8a2a214c711 100644 --- a/tests/src/python/test_qgsgeometry.py +++ b/tests/src/python/test_qgsgeometry.py @@ -3498,7 +3498,7 @@ class TestQgsGeometry(unittest.TestCase): # circular string geom = QgsGeometry.fromWkt('CircularString (1 5, 6 2, 7 3)') point = QgsGeometry.fromWkt('Point(9 -2)') - self.assertAlmostEqual(geom.lineLocatePoint(point), 7.372, places=3) + self.assertAlmostEqual(geom.lineLocatePoint(point), 7.377, places=3) def testInterpolateAngle(self): """ test QgsGeometry.interpolateAngle() """ @@ -3542,7 +3542,7 @@ class TestQgsGeometry(unittest.TestCase): # circular string geom = QgsGeometry.fromWkt('CircularString (1 5, 6 2, 7 3)') - self.assertAlmostEqual(geom.interpolateAngle(5), 1.69120, places=3) + self.assertAlmostEqual(geom.interpolateAngle(5), 1.6919, places=3) def testInterpolate(self): """ test QgsGeometry.interpolate() """ diff --git a/tests/testdata/geom_data.csv b/tests/testdata/geom_data.csv index 3620380daca..52ea9731f79 100644 --- a/tests/testdata/geom_data.csv +++ b/tests/testdata/geom_data.csv @@ -121,4 +121,4 @@ Polygon,,,,,,,,,,,,,,Malformed WKT "PolygonM ((0 0 1, 10 0 1, 10 10 1, 0 10 1, 0 0 1),(5 5 1, 7 5 1, 7 7 1, 5 7 1, 5 5 1),(1 1 1,2 1 1, 2 2 1, 1 2 1, 1 1 1))","POLYGON M ((0 0 1,10 0 1,10 10 1,0 10 1,0 0 1),(5 5 1,7 5 1,7 7 1,5 7 1,5 5 1),(1 1 1,2 1 1,2 2 1,1 2 1,1 1 1))",15,0,95,52,1,2,0,POINT(4.99473684210526 4.99473684210526),0,0,10,10, "PolygonZ ((0 0 1 , 10 0 1, 10 10 1, 0 10 1, 0 0 1))","POLYGON Z ((0 0 1,10 0 1,10 10 1,0 10 1,0 0 1))",5,0,100,40,1,0,0,POINT(5 5),0,0,10,10, "PolygonZ ((0 0 1, 10 0 1, 10 10 1, 0 10 1, 0 0 1),(5 5 1, 7 5 1, 7 7 1 , 5 7 1, 5 5 1))","POLYGON Z ((0 0 1,10 0 1,10 10 1,0 10 1,0 0 1),(5 5 1,7 5 1,7 7 1,5 7 1,5 5 1))",10,0,96,48,1,1,0,POINT(4.95833333333333 4.95833333333333),0,0,10,10, -"CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))","CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))",34,0,32087.8047511,770.542777805,1,1,0,Point (2678143.67776390677317977 1225948.36456208629533648),2678032.40842,1225804.43286112,2678251.06846707,1226083.94340025, +"CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))","CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))",34,0,32087.8047511,770.542777805,1,1,0,Point (2678143.67774726310744882 1225948.36458417493849993),2678032.40842,1225804.43286112,2678251.06846707,1226083.94340025, diff --git a/tests/testdata/polys_overlapping_with_id.dbf b/tests/testdata/polys_overlapping_with_id.dbf index e7162898c5e0ae43130ef63b095186904d7c2b7e..66f5f3c2ad322dfcb05bb4286088f3a22763b8b0 100644 GIT binary patch delta 12 TcmdnMwSkM7xs+ogvo0$D7Bd3g delta 12 TcmdnMwSkM7xs+`qvo0$D7AXSU From 8c8426fc6af7536f0b38cb0e7ff6f5dd108ae29b Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 20:12:03 +0200 Subject: [PATCH 147/364] Do not force inclusion of control point This reverts commit dae9d025293c217628def8eb8d196238d2218ddc. --- src/core/geometry/qgsgeometryutils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 1cbc15d122c..2ce680b36f7 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -762,6 +762,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co { addP2 = false; } + addP2 = false; // As we're adding the last point in any case, we'll avoid // including a point which is at less than 1% increment distance From c51db212eee139028d41fcc10a6e9a01675575ac Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 21:39:47 +0200 Subject: [PATCH 148/364] Do not expect forced control point in output --- tests/src/core/testqgscurve.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/core/testqgscurve.cpp b/tests/src/core/testqgscurve.cpp index dcd9bad2eb0..03741a2dcfd 100644 --- a/tests/src/core/testqgscurve.cpp +++ b/tests/src/core/testqgscurve.cpp @@ -100,7 +100,7 @@ void TestQgsCurve::curveToLine() /* op: Maximum of 70 degrees of angle, symmetric */ /* See https://issues.qgis.org/issues/16722 */ TEST_C2L( circularString, 70 * M_PI / 180, QgsAbstractGeometry::MaximumAngle, - "LineString (0 0, 50 86.6, 100 100, 150 86.6, 200 0)", 2 ); + "LineString (0 0, 50 86.6, 150 86.6, 200 0)", 2 ); /* input: 2 arcs of 2 quadrants each (180 degrees + 180 degrees other direction) */ circularString.reset( dynamic_cast( @@ -112,7 +112,7 @@ void TestQgsCurve::curveToLine() /* op: Maximum of M_PI / 3 degrees of angle */ TEST_C2L( circularString, M_PI / 3, QgsAbstractGeometry::MaximumAngle, - "LineString (0 0, 50 86.6, 100 100, 150 86.6, 200 0, 250 -86.6, 300 -100, 350 -86.6, 400 0)", 2 ); + "LineString (0 0, 50 86.6, 150 86.6, 200 0, 250 -86.6, 350 -86.6, 400 0)", 2 ); } From 5186c385c42630b00e3e1b048f2f6ec72b1dfabe Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 21:14:53 +0200 Subject: [PATCH 149/364] Expect centroid obtained by not forcing inclusion of control point --- tests/testdata/geom_data.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testdata/geom_data.csv b/tests/testdata/geom_data.csv index 52ea9731f79..3164f12cef1 100644 --- a/tests/testdata/geom_data.csv +++ b/tests/testdata/geom_data.csv @@ -121,4 +121,4 @@ Polygon,,,,,,,,,,,,,,Malformed WKT "PolygonM ((0 0 1, 10 0 1, 10 10 1, 0 10 1, 0 0 1),(5 5 1, 7 5 1, 7 7 1, 5 7 1, 5 5 1),(1 1 1,2 1 1, 2 2 1, 1 2 1, 1 1 1))","POLYGON M ((0 0 1,10 0 1,10 10 1,0 10 1,0 0 1),(5 5 1,7 5 1,7 7 1,5 7 1,5 5 1),(1 1 1,2 1 1,2 2 1,1 2 1,1 1 1))",15,0,95,52,1,2,0,POINT(4.99473684210526 4.99473684210526),0,0,10,10, "PolygonZ ((0 0 1 , 10 0 1, 10 10 1, 0 10 1, 0 0 1))","POLYGON Z ((0 0 1,10 0 1,10 10 1,0 10 1,0 0 1))",5,0,100,40,1,0,0,POINT(5 5),0,0,10,10, "PolygonZ ((0 0 1, 10 0 1, 10 10 1, 0 10 1, 0 0 1),(5 5 1, 7 5 1, 7 7 1 , 5 7 1, 5 5 1))","POLYGON Z ((0 0 1,10 0 1,10 10 1,0 10 1,0 0 1),(5 5 1,7 5 1,7 7 1,5 7 1,5 5 1))",10,0,96,48,1,1,0,POINT(4.95833333333333 4.95833333333333),0,0,10,10, -"CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))","CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))",34,0,32087.8047511,770.542777805,1,1,0,Point (2678143.67774726310744882 1225948.36458417493849993),2678032.40842,1225804.43286112,2678251.06846707,1226083.94340025, +"CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))","CurvePolygon (CompoundCurve ((2678124.57778842002153397 1225804.43286111624911427, 2678251.0684670670889318 1225964.66278979112394154, 2678201.75901959836483002 1226077.95337014575488865, 2678199.27904875669628382 1226083.94340024818666279, 2678198.13904719380661845 1226083.5034040967002511, 2678188.08903313148766756 1226079.56343783461488783, 2678164.688993189483881 1226068.85351158352568746, 2678152.65896565327420831 1226061.85354482522234321, 2678133.63892276119440794 1226050.92359781125560403),CircularString (2678133.63892276119440794 1226050.92359781125560403, 2678124.47892813989892602 1226045.71365369856357574, 2678115.92887723352760077 1226039.55364341707900167, 2678110.39902341179549694 1226025.70413719792850316, 2678113.52874504774808884 1226011.12356549175456166, 2678117.04747172351926565 1226004.70550769660621881, 2678121.17868330329656601 1225998.66349145700223744, 2678122.91810466628521681 1225991.71089569479227066, 2678120.73861891590058804 1225984.88345037144608796, 2678109.63862727722153068 1225981.37343537760898471, 2678097.99861149024218321 1225981.17354299034923315, 2678087.07146158767864108 1225984.57272616261616349, 2678076.13865283969789743 1225987.95366438734345138, 2678068.26173875294625759 1225987.17441278789192438, 2678060.82864314317703247 1225984.45372360944747925, 2678052.37183309439569712 1225977.24715672573074698, 2678045.5285750487819314 1225968.49374381266534328, 2678039.90821403311565518 1225957.94303491595201194, 2678035.46847799280658364 1225946.84372220188379288, 2678033.64745173417031765 1225940.12937669246457517, 2678032.40841578459367156 1225933.28369381325319409, 2678033.68931351415812969 1225925.84029478346928954, 2678036.55834634136408567 1225918.85362965753301978, 2678039.0693440935574472 1225913.78109931806102395, 2678040.70829536207020283 1225908.363577825948596),(2678040.70829536207020283 1225908.363577825948596, 2678124.57778842002153397 1225804.43286111624911427)))",34,0,32087.8047511,770.542777805,1,1,0,Point (2678143.67776112211868167 1225948.36457449640147388),2678032.40842,1225804.43286112,2678251.06846707,1226083.94340025, From 650cf6a3fd390469d65f6e719a5f2346232e40d8 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 21:14:11 +0200 Subject: [PATCH 150/364] Remove line segmentation test checking for control point containement There are more tests in dedicated file (testqgscurve.cpp) --- tests/src/core/testqgsgeometry.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 18e384d2174..6ed5feec635 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -118,7 +118,6 @@ class TestQgsGeometry : public QObject void wkbInOut(); - void segmentizeCircularString(); void directionNeutralSegmentation(); void poleOfInaccessibility(); @@ -5290,23 +5289,6 @@ void TestQgsGeometry::wkbInOut() QCOMPARE( badHeader.wkbType(), QgsWkbTypes::Unknown ); } -void TestQgsGeometry::segmentizeCircularString() -{ - QString wkt( QStringLiteral( "CIRCULARSTRING( 0 0, 0.5 0.5, 2 0 )" ) ); - QgsCircularString *circularString = dynamic_cast( QgsGeometryFactory::geomFromWkt( wkt ).release() ); - QVERIFY( circularString ); - QgsLineString *lineString = circularString->curveToLine(); - QVERIFY( lineString ); - QgsPointSequence points; - lineString->points( points ); - - delete circularString; - delete lineString; - - //make sure the curve point is part of the segmentized result - QVERIFY( points.contains( QgsPoint( 0.5, 0.5 ) ) ); -} - void TestQgsGeometry::directionNeutralSegmentation() { //Tests, if segmentation of a circularstring is the same in both directions From 37052db39aca4824532d71dd182b8d2a9d634d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20MARCEL?= Date: Sun, 27 Aug 2017 07:22:13 +0200 Subject: [PATCH 151/364] Fix crash when closing a project with layers in edition (fixes #16771) Delete layers from project & not through layers tree, avoiding double deletion of layers. --- src/core/qgsproject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index 0e69b4ac3ec..8c359198408 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -492,8 +492,6 @@ void QgsProject::clear() mMapThemeCollection.reset( new QgsMapThemeCollection( this ) ); emit mapThemeCollectionChanged(); - mRootGroup->clear(); - mLabelingEngineSettings->clear(); mArchive->clear(); @@ -512,6 +510,8 @@ void QgsProject::clear() writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() ); removeAllMapLayers(); + mRootGroup->clear(); + setDirty( false ); } From 0dfdb6f3f4685eff7ef83592eb2ef97031dfe4b3 Mon Sep 17 00:00:00 2001 From: nirvn Date: Fri, 25 Aug 2017 10:35:22 +0700 Subject: [PATCH 152/364] fix open raster not focusing on raster data source manager tab --- src/app/qgisapp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 2144a38c422..30839c85dcd 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -1905,7 +1905,7 @@ void QgisApp::createActions() connect( mActionEmbedLayers, &QAction::triggered, this, &QgisApp::embedLayers ); connect( mActionAddLayerDefinition, &QAction::triggered, this, &QgisApp::addLayerDefinition ); connect( mActionAddOgrLayer, &QAction::triggered, this, [ = ] { dataSourceManager( QStringLiteral( "ogr" ) ); } ); - connect( mActionAddRasterLayer, &QAction::triggered, this, [ = ] { dataSourceManager( QStringLiteral( "raster" ) ); } ); + connect( mActionAddRasterLayer, &QAction::triggered, this, [ = ] { dataSourceManager( QStringLiteral( "gdal" ) ); } ); connect( mActionAddPgLayer, &QAction::triggered, this, [ = ] { dataSourceManager( QStringLiteral( "postgres" ) ); } ); connect( mActionAddSpatiaLiteLayer, &QAction::triggered, [ = ] { dataSourceManager( QStringLiteral( "spatialite" ) ); } ); connect( mActionAddMssqlLayer, &QAction::triggered, this, [ = ] { dataSourceManager( QStringLiteral( "mssql" ) ); } ); From 35765326aa7d54648886294ea5db2e9d170e95b8 Mon Sep 17 00:00:00 2001 From: nirvn Date: Fri, 25 Aug 2017 10:43:39 +0700 Subject: [PATCH 153/364] harmonize open raster and open vector UIs --- src/ui/qgsgdalsourceselectbase.ui | 39 +++++++++++++++++++++---------- src/ui/qgsogrsourceselectbase.ui | 4 ++-- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/ui/qgsgdalsourceselectbase.ui b/src/ui/qgsgdalsourceselectbase.ui index 5a954d60bf0..0bb0aaaac98 100644 --- a/src/ui/qgsgdalsourceselectbase.ui +++ b/src/ui/qgsgdalsourceselectbase.ui @@ -11,7 +11,7 @@ - Add Layer(s) from a Server + Add Raster Layer(s) @@ -24,17 +24,32 @@ true - - - - QDialogButtonBox::Help + + + + + 0 + 0 + + + Source + + + + + + Dataset + + + + + + + - - - Qt::Vertical @@ -47,10 +62,10 @@ - - - - Path to a raster data source + + + + QDialogButtonBox::Help diff --git a/src/ui/qgsogrsourceselectbase.ui b/src/ui/qgsogrsourceselectbase.ui index 2f91b72be5c..ddb8fc6c58e 100644 --- a/src/ui/qgsogrsourceselectbase.ui +++ b/src/ui/qgsogrsourceselectbase.ui @@ -127,9 +127,9 @@ - + - Browse + From 095bcae01d76976ee0b4e86166a48de9f050a014 Mon Sep 17 00:00:00 2001 From: nirvn Date: Fri, 25 Aug 2017 11:56:59 +0700 Subject: [PATCH 154/364] use a QgsFileWidget in open vector layer UI --- src/providers/gdal/qgsgdalsourceselect.cpp | 7 +- src/providers/ogr/qgsogrsourceselect.cpp | 99 ++++++++-------------- src/providers/ogr/qgsogrsourceselect.h | 6 +- src/ui/qgsgdalsourceselectbase.ui | 4 +- src/ui/qgsogrsourceselectbase.ui | 45 +++++----- 5 files changed, 68 insertions(+), 93 deletions(-) diff --git a/src/providers/gdal/qgsgdalsourceselect.cpp b/src/providers/gdal/qgsgdalsourceselect.cpp index fbb3ef618c6..ff61dbcd856 100644 --- a/src/providers/gdal/qgsgdalsourceselect.cpp +++ b/src/providers/gdal/qgsgdalsourceselect.cpp @@ -23,9 +23,10 @@ QgsGdalSourceSelect::QgsGdalSourceSelect( QWidget *parent, Qt::WindowFlags fl, Q { setupUi( this ); setupButtons( buttonBox ); - mQgsFileWidget->setFilter( QgsProviderRegistry::instance()->fileRasterFilters() ); - mQgsFileWidget->setStorageMode( QgsFileWidget::GetMultipleFiles ); - connect( mQgsFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path ) + mFileWidget->setDialogTitle( tr( "Open GDAL Supported Raster Dataset(s)" ) ); + mFileWidget->setFilter( QgsProviderRegistry::instance()->fileRasterFilters() ); + mFileWidget->setStorageMode( QgsFileWidget::GetMultipleFiles ); + connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path ) { mRasterPath = path; emit enableButtons( ! mRasterPath.isEmpty() ); diff --git a/src/providers/ogr/qgsogrsourceselect.cpp b/src/providers/ogr/qgsogrsourceselect.cpp index 279062fc7d8..42c108cb8bf 100644 --- a/src/providers/ogr/qgsogrsourceselect.cpp +++ b/src/providers/ogr/qgsogrsourceselect.cpp @@ -97,6 +97,16 @@ QgsOgrSourceSelect::QgsOgrSourceSelect( QWidget *parent, Qt::WindowFlags fl, Qgs } cmbDatabaseTypes->blockSignals( false ); cmbConnections->blockSignals( false ); + + mFileWidget->setDialogTitle( tr( "Open OGR Supported Vector Dataset(s)" ) ); + mFileWidget->setFilter( mVectorFileFilter ); + mFileWidget->setStorageMode( QgsFileWidget::GetMultipleFiles ); + + connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path ) + { + mVectorPath = path; + emit enableButtons( ! mVectorPath.isEmpty() ); + } ); } QgsOgrSourceSelect::~QgsOgrSourceSelect() @@ -105,43 +115,6 @@ QgsOgrSourceSelect::~QgsOgrSourceSelect() settings.setValue( QStringLiteral( "Windows/OpenVectorLayer/geometry" ), saveGeometry() ); } -QStringList QgsOgrSourceSelect::openFile() -{ - QStringList selectedFiles; - QgsDebugMsg( "Vector file filters: " + mVectorFileFilter ); - QString enc = encoding(); - QString title = tr( "Open an OGR Supported Vector Layer" ); - QgsGuiUtils::openFilesRememberingFilter( QStringLiteral( "lastVectorFileFilter" ), mVectorFileFilter, selectedFiles, enc, title ); - - return selectedFiles; -} - -QString QgsOgrSourceSelect::openDirectory() -{ - QgsSettings settings; - - bool haveLastUsedDir = settings.contains( QStringLiteral( "/UI/LastUsedDirectory" ) ); - QString lastUsedDir = settings.value( QStringLiteral( "UI/LastUsedDirectory" ), QDir::homePath() ).toString(); - if ( !haveLastUsedDir ) - lastUsedDir = QLatin1String( "" ); - - QString path = QFileDialog::getExistingDirectory( this, - tr( "Open Directory" ), lastUsedDir, - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks ); - - settings.setValue( QStringLiteral( "UI/LastUsedDirectory" ), path ); - //process path if it is grass - if ( cmbDirectoryTypes->currentText() == QLatin1String( "Grass Vector" ) ) - { -#ifdef Q_OS_WIN - //replace backslashes with forward slashes - path.replace( '\\', '/' ); -#endif - path = path + "/head"; - } - return path; -} - QStringList QgsOgrSourceSelect::dataSources() { return mDataSources; @@ -272,29 +245,6 @@ void QgsOgrSourceSelect::setSelectedConnection() QgsDebugMsg( "Setting selected connection to " + cmbConnections->currentText() ); } - -void QgsOgrSourceSelect::on_buttonSelectSrc_clicked() -{ - if ( radioSrcFile->isChecked() ) - { - QStringList selected = openFile(); - if ( !selected.isEmpty() ) - { - inputSrcDataset->setText( selected.join( QStringLiteral( ";" ) ) ); - addButton()->setFocus(); - emit enableButtons( true ); - } - } - else if ( radioSrcDirectory->isChecked() ) - { - inputSrcDataset->setText( openDirectory() ); - } - else if ( !radioSrcDatabase->isChecked() ) - { - Q_ASSERT( !"SHOULD NEVER GET HERE" ); - } -} - void QgsOgrSourceSelect::addButtonClicked() { QgsSettings settings; @@ -359,7 +309,7 @@ void QgsOgrSourceSelect::addButtonClicked() } else if ( radioSrcFile->isChecked() ) { - if ( inputSrcDataset->text().isEmpty() ) + if ( mVectorPath.isEmpty() ) { QMessageBox::information( this, tr( "Add vector layer" ), @@ -367,11 +317,11 @@ void QgsOgrSourceSelect::addButtonClicked() return; } - mDataSources << inputSrcDataset->text().split( ';' ); + mDataSources << QgsFileWidget::splitFilePaths( mVectorPath ); } else if ( radioSrcDirectory->isChecked() ) { - if ( inputSrcDataset->text().isEmpty() ) + if ( mVectorPath.isEmpty() ) { QMessageBox::information( this, tr( "Add vector layer" ), @@ -379,7 +329,17 @@ void QgsOgrSourceSelect::addButtonClicked() return; } - mDataSources << inputSrcDataset->text(); + //process path if it is grass + if ( cmbDirectoryTypes->currentText() == QLatin1String( "Grass Vector" ) ) + { +#ifdef Q_OS_WIN + //replace backslashes with forward slashes + mVectorPath.replace( '\\', '/' ); +#endif + mVectorPath = mVectorPath + "/head"; + } + + mDataSources << mVectorPath; } // Save the used encoding @@ -402,6 +362,12 @@ void QgsOgrSourceSelect::on_radioSrcFile_toggled( bool checked ) fileGroupBox->show(); dbGroupBox->hide(); protocolGroupBox->hide(); + + mFileWidget->setDialogTitle( tr( "Open an OGR Supported Vector Layer" ) ); + mFileWidget->setFilter( mVectorFileFilter ); + mFileWidget->setStorageMode( QgsFileWidget::GetMultipleFiles ); + mFileWidget->setFilePath( QString() ); + mDataSourceType = QStringLiteral( "file" ); } } @@ -415,6 +381,11 @@ void QgsOgrSourceSelect::on_radioSrcDirectory_toggled( bool checked ) fileGroupBox->show(); dbGroupBox->hide(); protocolGroupBox->hide(); + + mFileWidget->setDialogTitle( tr( "Open Directory" ) ); + mFileWidget->setStorageMode( QgsFileWidget::GetDirectory ); + mFileWidget->setFilePath( QString() ); + mDataSourceType = QStringLiteral( "directory" ); } } diff --git a/src/providers/ogr/qgsogrsourceselect.h b/src/providers/ogr/qgsogrsourceselect.h index 4233e8486fc..d9b43e50320 100644 --- a/src/providers/ogr/qgsogrsourceselect.h +++ b/src/providers/ogr/qgsogrsourceselect.h @@ -91,7 +91,6 @@ class QgsOgrSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsOg //! Sets the selected connection void setSelectedConnection(); - void on_buttonSelectSrc_clicked(); void on_radioSrcFile_toggled( bool checked ); void on_radioSrcDirectory_toggled( bool checked ); void on_radioSrcDatabase_toggled( bool checked ); @@ -102,6 +101,11 @@ class QgsOgrSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsOg void on_cmbDatabaseTypes_currentIndexChanged( const QString &text ); void on_cmbConnections_currentIndexChanged( const QString &text ); void showHelp(); + + private: + + QString mVectorPath; + }; #endif // QGSOGRSOURCESELECT_H diff --git a/src/ui/qgsgdalsourceselectbase.ui b/src/ui/qgsgdalsourceselectbase.ui index 0bb0aaaac98..ccaca001610 100644 --- a/src/ui/qgsgdalsourceselectbase.ui +++ b/src/ui/qgsgdalsourceselectbase.ui @@ -39,12 +39,12 @@ - Dataset + Raster Dataset(s) - + diff --git a/src/ui/qgsogrsourceselectbase.ui b/src/ui/qgsogrsourceselectbase.ui index ddb8fc6c58e..e943cc8d43f 100644 --- a/src/ui/qgsogrsourceselectbase.ui +++ b/src/ui/qgsogrsourceselectbase.ui @@ -103,35 +103,28 @@
    - - + + + + + 0 + 0 + + + - + - Dataset + Vector Dataset(s) - inputSrcDataset + mFileWidget - - - - - 200 - 0 - - - - - - - - - - + + @@ -297,6 +290,13 @@ + + + QgsFileWidget + QWidget +
    qgsfilewidget.h
    +
    +
    radioSrcFile radioSrcDirectory @@ -306,8 +306,7 @@ cmbProtocolTypes protocolURI cmbDirectoryTypes - inputSrcDataset - buttonSelectSrc + mFileWidget cmbDatabaseTypes cmbConnections btnNew From 487adb45f60235be8821632dc9fa800164f27767 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 28 Aug 2017 09:25:36 +0200 Subject: [PATCH 155/364] Removed some GDAL version ifdefs Now that 2.1 is required --- src/core/qgsvectorfilewriter.cpp | 15 --------------- src/providers/ogr/qgsogrprovider.cpp | 2 -- 2 files changed, 17 deletions(-) diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 3817ef69439..4b5eeeaae2a 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -808,15 +808,9 @@ QMap QgsVectorFileWriter::initMetaData() "Can be one of NULL for a simple .dbf file with no .shp file, POINT, " "ARC, POLYGON or MULTIPOINT for 2D, or POINTZ, ARCZ, POLYGONZ or " "MULTIPOINTZ for 3D;" ) + -#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,1,0) - QObject::tr( " Shapefiles with measure values are not supported," - " nor are MULTIPATCH files." ) + -#endif -#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0) QObject::tr( " POINTM, ARCM, POLYGONM or MULTIPOINTM for measured geometries" " and POINTZM, ARCZM, POLYGONZM or MULTIPOINTZM for 3D measured" " geometries." ) + -#endif #if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,2,0) QObject::tr( " MULTIPATCH files are supported since GDAL 2.2." ) + #endif @@ -831,7 +825,6 @@ QMap QgsVectorFileWriter::initMetaData() << QStringLiteral( "ARCZ" ) << QStringLiteral( "POLYGONZ" ) << QStringLiteral( "MULTIPOINTZ" ) -#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0) << QStringLiteral( "POINTM" ) << QStringLiteral( "ARCM" ) << QStringLiteral( "POLYGONM" ) @@ -840,7 +833,6 @@ QMap QgsVectorFileWriter::initMetaData() << QStringLiteral( "ARCZM" ) << QStringLiteral( "POLYGONZM" ) << QStringLiteral( "MULTIPOINTZM" ) -#endif #if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,2,0) << QStringLiteral( "MULTIPATCH" ) #endif @@ -1330,7 +1322,6 @@ QMap QgsVectorFileWriter::initMetaData() true // Allow None ) ); -#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,2) datasetOptions.insert( QStringLiteral( "BLOCK_SIZE" ), new IntOption( QObject::tr( "(multiples of 512): Block size for .map files. Defaults " "to 512. MapInfo 15.2 and above creates .tab files with a " @@ -1338,15 +1329,12 @@ QMap QgsVectorFileWriter::initMetaData() "able to handle block sizes from 512 to 32256." ), 512 ) ); -#endif -#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0) layerOptions.insert( QStringLiteral( "BOUNDS" ), new StringOption( QObject::tr( "xmin,ymin,xmax,ymax: Define custom layer bounds to increase the " "accuracy of the coordinates. Note: the geometry of written " "features must be within the defined box." ), QLatin1String( "" ) // Default value ) ); -#endif driverMetadata.insert( QStringLiteral( "MapInfo File" ), MetaData( @@ -1894,9 +1882,6 @@ QStringList QgsVectorFileWriter::defaultLayerOptions( const QString &driverName OGRwkbGeometryType QgsVectorFileWriter::ogrTypeFromWkbType( QgsWkbTypes::Type type ) { -#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,1,0) - type = QgsWkbTypes::dropM( type ); -#endif OGRwkbGeometryType ogrType = static_cast( type ); diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 1c220902b1f..902f5386329 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -569,7 +569,6 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const QString geom; // GDAL 2.1 can return M/ZM geometries -#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0) if ( wkbHasM( type ) ) { geom = ogrWkbGeometryTypeName( wkbFlatten( type ) ); @@ -579,7 +578,6 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const geom += "M"; return geom; } -#endif switch ( ( long )type ) { From 55a01dcb176ae74ba7777037f8f7777b86ff73c0 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 25 Aug 2017 23:54:40 +0200 Subject: [PATCH 156/364] Simplify linearizeArc code dropping useless conditionals --- src/core/geometry/qgsgeometryutils.cpp | 61 +++++++------------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index 2ce680b36f7..b877cb10dba 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -633,23 +633,17 @@ double QgsGeometryUtils::circleTangentDirection( const QgsPoint &tangentPoint, c void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance, QgsAbstractGeometry::SegmentationToleranceType toleranceType, bool hasZ, bool hasM ) { bool reversed = false; - bool clockwise = false; int segSide = segmentSide( p1, p3, p2 ); - if ( segSide == -1 ) - { - clockwise = true; - } QgsPoint circlePoint1; const QgsPoint circlePoint2 = p2; QgsPoint circlePoint3; - if ( clockwise ) + if ( segSide == -1 ) { // Reverse ! circlePoint1 = p3; circlePoint3 = p1; - clockwise = false; reversed = true; } else @@ -664,8 +658,8 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co double centerY = 0; circleCenterRadius( circlePoint1, circlePoint2, circlePoint3, radius, centerX, centerY ); - QgsDebugMsg( QString( "Center: POINT(%1 %2) - Radius: %3 - Clockwise: %4" ) - .arg( centerX ) .arg( centerY ) .arg( radius ) .arg( clockwise ) ); + QgsDebugMsg( QString( "Center: POINT(%1 %2) - Radius: %3 - Reversed: %4" ) + .arg( centerX ) .arg( centerY ) .arg( radius ) .arg( reversed ) ); if ( circlePoint1 != circlePoint3 && ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) ) //points are colinear { @@ -698,7 +692,7 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co const bool symmetric = true; if ( symmetric ) { - double angle = clockwise ? a1 - a3 : a3 - a1; + double angle = a3 - a1; if ( angle < 0 ) angle += M_PI * 2; QgsDebugMsg( QString( "total angle: %1 (%2)" ) .arg( angle ) .arg( angle * 180 / M_PI ) @@ -712,23 +706,11 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co QgsDebugMsg( QString( "symmetric adjusted increment:%1" ) .arg( increment ) ); } - if ( clockwise ) - { - increment *= -1; - /* Adjust a3 down so we can increment from a1 to a3 cleanly */ - if ( a3 > a1 ) - a3 -= 2.0 * M_PI; - if ( a2 > a1 ) - a2 -= 2.0 * M_PI; - } - else - { - /* Adjust a3 up so we can increment from a1 to a3 cleanly */ - if ( a3 < a1 ) - a3 += 2.0 * M_PI; - if ( a2 < a1 ) - a2 += 2.0 * M_PI; - } + /* Adjust a3 up so we can increment from a1 to a3 cleanly */ + if ( a3 < a1 ) + a3 += 2.0 * M_PI; + if ( a2 < a1 ) + a2 += 2.0 * M_PI; QgsDebugMsg( QString( "ADJUSTED - a1:%1 (%4) a2:%2 (%5) a3:%3 (%6)" ) .arg( a1 ).arg( a2 ).arg( a3 ) @@ -749,10 +731,10 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co if ( hasM ) pointWkbType = QgsWkbTypes::addM( pointWkbType ); - QgsDebugMsg( QString( "a1:%1 (%2), a3:%3 (%4), inc:%5, shi:?, cw:%6" ) + QgsDebugMsg( QString( "a1:%1 (%2), a3:%3 (%4), inc:%5" ) . arg( a1 ). arg( a1 * 180 / M_PI ) . arg( a3 ). arg( a3 * 180 / M_PI ) - . arg( increment ). arg( clockwise ) + . arg( increment ) ); //make sure the curve point p2 is part of the segmentized vertices. But only if p1 != p3 @@ -771,27 +753,16 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co // are more distant than requested. This is at most 1% off // from requested MaxAngle and less for MaxError. double tolError = increment / 100; - double stopAngle = clockwise ? a3 - tolError : a3 - tolError; + double stopAngle = a3 - tolError; QgsDebugMsg( QString( "stopAngle: %1 (%2)" ) . arg( stopAngle ) .arg( stopAngle * 180 / M_PI ) ); - for ( double angle = a1 + increment; clockwise ? angle > stopAngle : angle < stopAngle; angle += increment ) + for ( double angle = a1 + increment; angle < stopAngle; angle += increment ) { if ( addP2 && angle > a2 ) { - if ( clockwise ) + if ( stringPoints.empty() || stringPoints.back() != circlePoint2 ) { - if ( stringPoints.empty() || stringPoints.front() != circlePoint2 ) - { - QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); - stringPoints.insert( 0, circlePoint2 ); - } - } - else - { - if ( stringPoints.empty() || stringPoints.back() != circlePoint2 ) - { - QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); - stringPoints.insert( stringPoints.size(), circlePoint2 ); - } + QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); + stringPoints.insert( stringPoints.size(), circlePoint2 ); } addP2 = false; } From 1897bec2a87db49d1b369c33d84e5bdb804a149a Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 11 Jul 2017 16:29:59 +0100 Subject: [PATCH 157/364] [needs-doc] Add editable/upsert on edit/delete cascade options --- src/app/qgsjoindialog.cpp | 11 +++++++ src/app/qgsvectorlayerproperties.cpp | 23 +++++++++++--- src/core/qgsvectorlayerjoinbuffer.cpp | 6 ++++ src/core/qgsvectorlayerjoininfo.cpp | 11 +++++++ src/core/qgsvectorlayerjoininfo.h | 41 ++++++++++++++++++++++++ src/ui/qgsjoindialogbase.ui | 44 ++++++++++++++++++++++---- src/ui/qgsvectorlayerpropertiesbase.ui | 41 ++++++++++++++++-------- 7 files changed, 154 insertions(+), 23 deletions(-) diff --git a/src/app/qgsjoindialog.cpp b/src/app/qgsjoindialog.cpp index 5e4f0c7ee30..0ed756726cf 100644 --- a/src/app/qgsjoindialog.cpp +++ b/src/app/qgsjoindialog.cpp @@ -76,6 +76,10 @@ void QgsJoinDialog::setJoinInfo( const QgsVectorLayerJoinInfo &joinInfo ) mTargetFieldComboBox->setField( joinInfo.targetFieldName() ); mCacheInMemoryCheckBox->setChecked( joinInfo.isUsingMemoryCache() ); mDynamicFormCheckBox->setChecked( joinInfo.isDynamicFormEnabled() ); + mEditableJoinLayer->setChecked( joinInfo.isEditable() ); + mUpsertOnEditCheckBox->setChecked( joinInfo.isUpsertOnEdit() ); + mDeleteCascadeCheckBox->setChecked( joinInfo.isDeleteCascade() ); + if ( joinInfo.prefix().isNull() ) { mUseCustomPrefix->setChecked( false ); @@ -115,6 +119,13 @@ QgsVectorLayerJoinInfo QgsJoinDialog::joinInfo() const info.setUsingMemoryCache( mCacheInMemoryCheckBox->isChecked() ); info.setDynamicFormEnabled( mDynamicFormCheckBox->isChecked() ); + info.setEditable( mEditableJoinLayer->isChecked() ); + if ( info.isEditable() ) + { + info.setUpsertOnEdit( mUpsertOnEditCheckBox->isChecked() ); + info.setDeleteCascade( mDeleteCascadeCheckBox->isChecked() ); + } + if ( mUseCustomPrefix->isChecked() ) info.setPrefix( mCustomPrefix->text() ); else diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 23a8444b8f3..205b873471b 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -1241,16 +1241,31 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo joinItem->setText( 4, QChar( 0x2714 ) ); } - joinItem->setText( 5, join.prefix() ); + if ( join.isEditable() ) + { + joinItem->setText( 5, QChar( 0x2714 ) ); + } + + if ( join.isUpsertOnEdit() ) + { + joinItem->setText( 6, QChar( 0x2714 ) ); + } + + if ( join.isDeleteCascade() ) + { + joinItem->setText( 7, QChar( 0x2714 ) ); + } + + joinItem->setText( 8, join.prefix() ); const QStringList *list = join.joinFieldNamesSubset(); if ( list ) { - joinItem->setText( 6, QStringLiteral( "%1" ).arg( list->count() ) ); + joinItem->setText( 9, QStringLiteral( "%1" ).arg( list->count() ) ); } else { - joinItem->setText( 6, tr( "all" ) ); + joinItem->setText( 9, tr( "all" ) ); } if ( insertIndex >= 0 ) @@ -1261,7 +1276,7 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo { mJoinTreeWidget->addTopLevelItem( joinItem ); } - for ( int c = 0; c < 6; c++ ) + for ( int c = 0; c < 9; c++ ) { mJoinTreeWidget->resizeColumnToContents( c ); } diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index d25899130cf..5b075b86842 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -276,6 +276,9 @@ void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &doc joinElem.setAttribute( QStringLiteral( "memoryCache" ), joinIt->isUsingMemoryCache() ); joinElem.setAttribute( QStringLiteral( "dynamicForm" ), joinIt->isDynamicFormEnabled() ); + joinElem.setAttribute( QStringLiteral( "editable" ), joinIt->isEditable() ); + joinElem.setAttribute( QStringLiteral( "upsertOnEdit" ), joinIt->isUpsertOnEdit() ); + joinElem.setAttribute( QStringLiteral( "deleteCascade" ), joinIt->isDeleteCascade() ); if ( joinIt->joinFieldNamesSubset() ) { @@ -317,6 +320,9 @@ void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node ) info.setTargetFieldName( infoElem.attribute( QStringLiteral( "targetFieldName" ) ) ); info.setUsingMemoryCache( infoElem.attribute( QStringLiteral( "memoryCache" ) ).toInt() ); info.setDynamicFormEnabled( infoElem.attribute( QStringLiteral( "dynamicForm" ) ).toInt() ); + info.setEditable( infoElem.attribute( QStringLiteral( "editable" ) ).toInt() ); + info.setUpsertOnEdit( infoElem.attribute( QStringLiteral( "upsertOnEdit" ) ).toInt() ); + info.setDeleteCascade( infoElem.attribute( QStringLiteral( "deleteCascade" ) ).toInt() ); QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( "joinFieldsSubset" ) ); if ( !subsetElem.isNull() ) diff --git a/src/core/qgsvectorlayerjoininfo.cpp b/src/core/qgsvectorlayerjoininfo.cpp index 0d77c39c871..8fb8d0d8697 100644 --- a/src/core/qgsvectorlayerjoininfo.cpp +++ b/src/core/qgsvectorlayerjoininfo.cpp @@ -33,3 +33,14 @@ QString QgsVectorLayerJoinInfo::prefixedFieldName( const QgsField &f ) const return name; } + +void QgsVectorLayerJoinInfo::setEditable( bool enabled ) +{ + mEditable = enabled; + + if ( ! mEditable ) + { + setDeleteCascade( false ); + setUpsertOnEdit( false ); + } +} diff --git a/src/core/qgsvectorlayerjoininfo.h b/src/core/qgsvectorlayerjoininfo.h index 63347291c84..fa02e46cf1a 100644 --- a/src/core/qgsvectorlayerjoininfo.h +++ b/src/core/qgsvectorlayerjoininfo.h @@ -81,6 +81,41 @@ class CORE_EXPORT QgsVectorLayerJoinInfo */ void setDynamicFormEnabled( bool enabled ) { mDynamicForm = enabled; } + /** Returns whether joined fields may be edited through the form of + * the target layer. + * \since QGIS 3.0 + */ + bool isEditable() const { return mEditable; } + + /** Sets whether the form of the target layer allows to edit joined fields. + * \since QGIS 3.0 + */ + void setEditable( bool enabled ); + + /** Returns whether a feature created on the target layer has to impact + * the joined layer by creating a new feature if necessary. + * \since QGIS 3.0 + */ + bool isUpsertOnEdit() const { return mUpsertOnEdit; } + + /** Sets whether a feature created on the target layer has to impact + * the joined layer by creating a new feature if necessary. + * \since QGIS 3.0 + */ + void setUpsertOnEdit( bool enabled ) { mUpsertOnEdit = enabled; } + + /** Returns whether a feature deleted on the target layer has to impact the + * joined layer by deleting the corresponding joined feature. + * \since QGIS 3.0 + */ + bool isDeleteCascade() const { return mDeleteCascade; } + + /** Sets whether a feature deleted on the target layer has to impact the + * joined layer by deleting the corresponding joined feature. + * \since QGIS 3.0 + */ + void setDeleteCascade( bool enabled ) { mDeleteCascade = enabled; } + /** Returns the prefixed name of the field. * \param field the field * \returns the prefixed name of the field @@ -135,6 +170,12 @@ class CORE_EXPORT QgsVectorLayerJoinInfo bool mDynamicForm = false; + bool mEditable; + + bool mUpsertOnEdit; + + bool mDeleteCascade; + //! Cache for joined attributes to provide fast lookup (size is 0 if no memory caching) QHash< QString, QgsAttributes> cachedAttributes; diff --git a/src/ui/qgsjoindialogbase.ui b/src/ui/qgsjoindialogbase.ui index 679f6d20197..0a2bd99858b 100644 --- a/src/ui/qgsjoindialogbase.ui +++ b/src/ui/qgsjoindialogbase.ui @@ -7,7 +7,7 @@ 0 0 505 - 395 + 487 @@ -44,7 +44,7 @@ - + Choose which fields are &joined @@ -65,7 +65,7 @@ - + Custom field &name prefix @@ -86,7 +86,7 @@ - + Qt::Vertical @@ -116,7 +116,7 @@ - + Qt::Horizontal @@ -126,13 +126,45 @@ - + Dynamic form + + + + Edi&table join layer + + + true + + + false + + + true + + + + + + Upsert on edit + + + + + + + Delete cascade + + + + + + diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index ad1f95c6b89..10a81628884 100755 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -379,8 +379,8 @@ 0 0 - 653 - 542 + 306 + 508 @@ -830,8 +830,8 @@ border-radius: 2px; 0 0 - 653 - 542 + 268 + 321 @@ -1068,8 +1068,8 @@ border-radius: 2px; 0 0 - 653 - 542 + 100 + 30 @@ -1169,8 +1169,8 @@ border-radius: 2px; 0 0 - 653 - 542 + 100 + 30 @@ -1229,8 +1229,8 @@ border-radius: 2px; 0 0 - 653 - 542 + 632 + 276 @@ -1589,8 +1589,8 @@ border-radius: 2px; 0 0 - 653 - 542 + 100 + 30 @@ -1675,7 +1675,7 @@ border-radius: 2px; - 7 + 10 @@ -1702,6 +1702,21 @@ border-radius: 2px; Dynamic form + + + Editable + + + + + New Column + + + + + Delete cascade + + Prefix From e92f59ca066e78632b0938cb4b419ee79c127749 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 11 Jul 2017 16:30:43 +0100 Subject: [PATCH 158/364] Update sip binding --- python/core/qgsvectorlayerjoininfo.sip | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/python/core/qgsvectorlayerjoininfo.sip b/python/core/qgsvectorlayerjoininfo.sip index 005f6552775..ae8f7e9a826 100644 --- a/python/core/qgsvectorlayerjoininfo.sip +++ b/python/core/qgsvectorlayerjoininfo.sip @@ -101,6 +101,50 @@ Returns whether values from the joined layer should be cached in memory to speed Sets whether the form has to be dynamically updated with joined fields when a feature is being created in the target layer. .. versionadded:: 3.0 +%End + + bool isEditable() const; +%Docstring + Returns whether joined fields may be edited through the form of + the target layer. +.. versionadded:: 3.0 + :rtype: bool +%End + + void setEditable( bool enabled ); +%Docstring + Sets whether the form of the target layer allows to edit joined fields. +.. versionadded:: 3.0 +%End + + bool isUpsertOnEdit() const; +%Docstring + Returns whether a feature created on the target layer has to impact + the joined layer by creating a new feature if necessary. +.. versionadded:: 3.0 + :rtype: bool +%End + + void setUpsertOnEdit( bool enabled ); +%Docstring + Sets whether a feature created on the target layer has to impact + the joined layer by creating a new feature if necessary. +.. versionadded:: 3.0 +%End + + bool isDeleteCascade() const; +%Docstring + Returns whether a feature deleted on the target layer has to impact the + joined layer by deleting the corresponding joined feature. +.. versionadded:: 3.0 + :rtype: bool +%End + + void setDeleteCascade( bool enabled ); +%Docstring + Sets whether a feature deleted on the target layer has to impact the + joined layer by deleting the corresponding joined feature. +.. versionadded:: 3.0 %End QString prefixedFieldName( const QgsField &field ) const; @@ -137,6 +181,9 @@ Returns whether values from the joined layer should be cached in memory to speed + + + }; From 477775a5d7998aa20672a64f5ec62ca6e5793595 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 18 Jul 2017 10:35:55 +0100 Subject: [PATCH 159/364] [FEATURE] Joined fields are editable if the option is activated --- python/core/qgsvectorlayer.sip | 2 +- python/core/qgsvectorlayerjoinbuffer.sip | 9 +++++ src/core/qgsvectorlayer.cpp | 30 ++++++++++++++-- src/core/qgsvectorlayer.h | 2 +- src/core/qgsvectorlayercache.cpp | 36 +++++++++++++++++++ src/core/qgsvectorlayercache.h | 3 ++ src/core/qgsvectorlayerjoinbuffer.cpp | 20 +++++++++++ src/core/qgsvectorlayerjoinbuffer.h | 9 ++++- .../qgsattributetabledelegate.cpp | 17 ++++++++- .../attributetable/qgsattributetablemodel.cpp | 28 ++++++++++++--- .../attributetable/qgsattributetablemodel.h | 2 ++ src/gui/qgsattributeform.cpp | 36 ++++++++++++++----- src/gui/qgsattributeform.h | 4 +++ 13 files changed, 179 insertions(+), 19 deletions(-) diff --git a/python/core/qgsvectorlayer.sip b/python/core/qgsvectorlayer.sip index f6a8c33be20..8334b6d64d6 100644 --- a/python/core/qgsvectorlayer.sip +++ b/python/core/qgsvectorlayer.sip @@ -905,7 +905,7 @@ Return the provider type for this layer :rtype: QgsFeatureIterator %End - QgsFeature getFeature( QgsFeatureId fid ); + QgsFeature getFeature( QgsFeatureId fid ) const; %Docstring Query the layer for the feature with the given id. If there is no such feature, the returned feature will be invalid. diff --git a/python/core/qgsvectorlayerjoinbuffer.sip b/python/core/qgsvectorlayerjoinbuffer.sip index bae1a284915..bdc45784c34 100644 --- a/python/core/qgsvectorlayerjoinbuffer.sip +++ b/python/core/qgsvectorlayerjoinbuffer.sip @@ -117,6 +117,15 @@ Quick way to test if there is any join at all :rtype: QgsFeature %End + QgsFeature targetedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const; +%Docstring + Returns the targeted feature corresponding to the joined feature. + \param info the vector join information + \param feature the feature of the joined layer +.. versionadded:: 3.0 + :rtype: QgsFeature +%End + QgsVectorLayerJoinBuffer *clone() const /Factory/; %Docstring .. versionadded:: 2.6 diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 4b7c5d16a9f..c8907074001 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2258,10 +2258,34 @@ bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ) bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) { - if ( !mEditBuffer || !mDataProvider ) - return false; + if ( fields().fieldOrigin( field ) == QgsFields::OriginJoin ) + { + int srcFieldIndex; + const QgsVectorLayerJoinInfo *info = mJoinBuffer->joinForFieldIndex( field, fields(), srcFieldIndex ); + if ( info && info->joinLayer() && info->isEditable() ) + { + QgsFeature feature = getFeature( fid ); - return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); + if ( !feature.isValid() ) + return false; + + QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( info, feature ); + + if ( joinFeature.isValid() ) + return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue ); + else + return false; + } + else + return false; + } + else + { + if ( !mEditBuffer || !mDataProvider ) + return false; + else + return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); + } } bool QgsVectorLayer::addAttribute( const QgsField &field ) diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 3b8ac2d9843..69d1488bcd4 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -875,7 +875,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * Query the layer for the feature with the given id. * If there is no such feature, the returned feature will be invalid. */ - inline QgsFeature getFeature( QgsFeatureId fid ) + inline QgsFeature getFeature( QgsFeatureId fid ) const { QgsFeature feature; getFeatures( QgsFeatureRequest( fid ) ).nextFeature( feature ); diff --git a/src/core/qgsvectorlayercache.cpp b/src/core/qgsvectorlayercache.cpp index b62147cec23..21d5d571049 100644 --- a/src/core/qgsvectorlayercache.cpp +++ b/src/core/qgsvectorlayercache.cpp @@ -18,6 +18,8 @@ #include "qgsvectorlayercache.h" #include "qgscacheindex.h" #include "qgscachedfeatureiterator.h" +#include "qgsvectorlayerjoininfo.h" +#include "qgsvectorlayerjoinbuffer.h" QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent ) : QObject( parent ) @@ -37,6 +39,8 @@ QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerCache::invalidate ); connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsVectorLayerCache::invalidate ); connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onAttributeValueChanged ); + + connectJoinedLayers(); } QgsVectorLayerCache::~QgsVectorLayerCache() @@ -232,6 +236,28 @@ void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, emit attributeValueChanged( fid, field, value ); } +void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value ) +{ + const QgsVectorLayer *joinLayer = qobject_cast( sender() ); + + Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() ) + { + if ( joinLayer == info.joinLayer() ) + { + QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) ); + + const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) ); + const int fieldIndex = mLayer->fields().indexFromName( fieldName ); + + if ( feature.isValid() && fieldIndex != -1 ) + { + onAttributeValueChanged( feature.id(), fieldIndex, value ); + return; + } + } + } +} + void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid ) { mCache.remove( fid ); @@ -425,3 +451,13 @@ bool QgsVectorLayerCache::checkInformationCovered( const QgsFeatureRequest &feat return true; } + +void QgsVectorLayerCache::connectJoinedLayers() const +{ + Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() ) + { + const QgsVectorLayer *vl = info.joinLayer(); + if ( vl ) + connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onJoinAttributeValueChanged ); + } +} diff --git a/src/core/qgsvectorlayercache.h b/src/core/qgsvectorlayercache.h index 96d595c0ee6..b3c1d0d0c63 100644 --- a/src/core/qgsvectorlayercache.h +++ b/src/core/qgsvectorlayercache.h @@ -364,6 +364,7 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject private slots: void onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value ); + void onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value ); void featureDeleted( QgsFeatureId fid ); void onFeatureAdded( QgsFeatureId fid ); void attributeAdded( int field ); @@ -374,6 +375,8 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject private: + void connectJoinedLayers() const; + inline void cacheFeature( QgsFeature &feat ) { QgsCachedFeature *cachedFeature = new QgsCachedFeature( feat, this ); diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index 5b075b86842..ae69a0505b9 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -439,6 +439,26 @@ QgsFeature QgsVectorLayerJoinBuffer::joinedFeatureOf( const QgsVectorLayerJoinIn return joinedFeature; } +QgsFeature QgsVectorLayerJoinBuffer::targetedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const +{ + QgsFeature targetedFeature; + + if ( info->joinLayer() ) + { + const QVariant targetValue = feature.attribute( info->joinFieldName() ); + const QString filter = QgsExpression::createFieldEqualityExpression( info->targetFieldName(), targetValue ); + + QgsFeatureRequest request; + request.setFilterExpression( filter ); + request.setLimit( 1 ); + + QgsFeatureIterator it = mLayer->getFeatures( request ); + it.nextFeature( targetedFeature ); + } + + return targetedFeature; +} + QgsVectorLayerJoinBuffer *QgsVectorLayerJoinBuffer::clone() const { QgsVectorLayerJoinBuffer *cloned = new QgsVectorLayerJoinBuffer( mLayer ); diff --git a/src/core/qgsvectorlayerjoinbuffer.h b/src/core/qgsvectorlayerjoinbuffer.h index bcd33118fb4..02ed58194a7 100644 --- a/src/core/qgsvectorlayerjoinbuffer.h +++ b/src/core/qgsvectorlayerjoinbuffer.h @@ -87,7 +87,7 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject /** Returns joins where the field of a target layer is considered as an id. * \param field the field of a target layer * \returns a list of vector joins - * \since QGIS3.0 + * \since QGIS 3.0 */ QList joinsWhereFieldIsId( const QgsField &field ) const; @@ -98,6 +98,13 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject */ QgsFeature joinedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const; + /** Returns the targeted feature corresponding to the joined feature. + * \param info the vector join information + * \param feature the feature of the joined layer + * \since QGIS 3.0 + */ + QgsFeature targetedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const; + //! Create a copy of the join buffer //! \since QGIS 2.6 QgsVectorLayerJoinBuffer *clone() const SIP_FACTORY; diff --git a/src/gui/attributetable/qgsattributetabledelegate.cpp b/src/gui/attributetable/qgsattributetabledelegate.cpp index 78a46231a0d..477c5293476 100644 --- a/src/gui/attributetable/qgsattributetabledelegate.cpp +++ b/src/gui/attributetable/qgsattributetabledelegate.cpp @@ -30,6 +30,8 @@ #include "qgsvectordataprovider.h" #include "qgsactionmanager.h" #include "qgsgui.h" +#include "qgsvectorlayerjoininfo.h" +#include "qgsvectorlayerjoinbuffer.h" QgsVectorLayer *QgsAttributeTableDelegate::layer( const QAbstractItemModel *model ) { @@ -73,7 +75,20 @@ QWidget *QgsAttributeTableDelegate::createEditor( QWidget *parent, const QStyleO w->setAutoFillBackground( true ); w->setFocusPolicy( Qt::StrongFocus ); // to make sure QMouseEvents are propagated to the editor widget - eww->setEnabled( !vl->editFormConfig().readOnly( fieldIdx ) ); + const int fieldOrigin = vl->fields().fieldOrigin( fieldIdx ); + bool readOnly = true; + if ( fieldOrigin == QgsFields::OriginJoin ) + { + int srcFieldIndex; + const QgsVectorLayerJoinInfo *info = vl->joinBuffer()->joinForFieldIndex( fieldIdx, vl->fields(), srcFieldIndex ); + + if ( info && info->isEditable() ) + readOnly = info->joinLayer()->editFormConfig().readOnly( srcFieldIndex ); + } + else + readOnly = vl->editFormConfig().readOnly( fieldIdx ); + + eww->setEnabled( !readOnly ); return w; } diff --git a/src/gui/attributetable/qgsattributetablemodel.cpp b/src/gui/attributetable/qgsattributetablemodel.cpp index 7df70d02b6e..cf1283573a5 100644 --- a/src/gui/attributetable/qgsattributetablemodel.cpp +++ b/src/gui/attributetable/qgsattributetablemodel.cpp @@ -35,6 +35,8 @@ #include "qgsfieldformatterregistry.h" #include "qgsgui.h" #include "qgsexpressionnodeimpl.h" +#include "qgsvectorlayerjoininfo.h" +#include "qgsvectorlayerjoinbuffer.h" #include @@ -740,15 +742,33 @@ Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const Qt::ItemFlags flags = QAbstractItemModel::flags( index ); - if ( layer()->isEditable() && - !layer()->editFormConfig().readOnly( mAttributes[index.column()] ) && - ( ( layer()->dataProvider() && layer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || - FID_IS_NEW( rowToId( index.row() ) ) ) ) + bool editable = false; + const int fieldIndex = mAttributes[index.column()]; + const QgsFeatureId fid = rowToId( index.row() ); + if ( layer()->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin ) + { + int srcFieldIndex; + const QgsVectorLayerJoinInfo *info = layer()->joinBuffer()->joinForFieldIndex( fieldIndex, layer()->fields(), srcFieldIndex ); + + if ( info && info->isEditable() ) + editable = fieldIsEditable( *info->joinLayer(), srcFieldIndex, fid ); + } + else + editable = fieldIsEditable( *layer(), fieldIndex, fid ); + + if ( editable ) flags |= Qt::ItemIsEditable; return flags; } +bool QgsAttributeTableModel::fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const +{ + return ( layer.isEditable() && + !layer.editFormConfig().readOnly( fieldIndex ) && + ( ( layer.dataProvider() && layer.dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || FID_IS_NEW( fid ) ) ); +} + void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 ) { mFeat.setId( std::numeric_limits::min() ); diff --git a/src/gui/attributetable/qgsattributetablemodel.h b/src/gui/attributetable/qgsattributetablemodel.h index 5a75c0f98ee..14299972ff2 100644 --- a/src/gui/attributetable/qgsattributetablemodel.h +++ b/src/gui/attributetable/qgsattributetablemodel.h @@ -357,6 +357,8 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel */ virtual bool loadFeatureAtId( QgsFeatureId fid ) const; + bool fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const; + QgsFeatureRequest mFeatureRequest; //! The currently cached column diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index ba107f091e4..a11cd49a962 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -298,8 +298,7 @@ bool QgsAttributeForm::saveEdits() // be careful- sometimes two null qvariants will be reported as not equal!! (e.g., different types) bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() ) || ( dstVar.isNull() != srcVar.isNull() ); - if ( changed && srcVar.isValid() - && !mLayer->editFormConfig().readOnly( eww->fieldIdx() ) ) + if ( changed && srcVar.isValid() && fieldIsEditable( eww->fieldIdx() ) ) { dst[eww->fieldIdx()] = srcVar; @@ -344,7 +343,7 @@ bool QgsAttributeForm::saveEdits() { if ( ( dst.at( i ) == src.at( i ) && dst.at( i ).isNull() == src.at( i ).isNull() ) // If field is not changed... || !dst.at( i ).isValid() // or the widget returns invalid (== do not change) - || mLayer->editFormConfig().readOnly( i ) ) // or the field cannot be edited ... + || !fieldIsEditable( i ) ) // or the field cannot be edited ... { continue; } @@ -1019,15 +1018,12 @@ void QgsAttributeForm::synchronizeEnabledState() Q_FOREACH ( QgsWidgetWrapper *ww, mWidgets ) { - bool fieldEditable = true; QgsEditorWidgetWrapper *eww = qobject_cast( ww ); if ( eww ) { - fieldEditable = !mLayer->editFormConfig().readOnly( eww->fieldIdx() ) && - ( ( mLayer->dataProvider() && layer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || - FID_IS_NEW( mFeature.id() ) ); + bool enabled = isEditable && fieldIsEditable( eww->fieldIdx() ); + ww->setEnabled( enabled ); } - ww->setEnabled( isEditable && fieldEditable ); } // push a message and disable the OK button if constraints are invalid @@ -2019,3 +2015,27 @@ void QgsAttributeForm::updateJoinedFields( const QgsEditorWidgetWrapper &eww ) } } } + +bool QgsAttributeForm::fieldIsEditable( int fieldIndex ) const +{ + bool editable = false; + + if ( mLayer->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin ) + { + int srcFieldIndex; + const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( fieldIndex, mLayer->fields(), srcFieldIndex ); + + if ( info && info->isEditable() && info->joinLayer()->isEditable() ) + editable = fieldIsEditable( *( info->joinLayer() ), srcFieldIndex, mFeature.id() ); + } + else + editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.id() ); + + return editable; +} + +bool QgsAttributeForm::fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const +{ + return !layer.editFormConfig().readOnly( fieldIndex ) && + ( ( layer.dataProvider() && layer.dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || FID_IS_NEW( fid ) ); +} diff --git a/src/gui/qgsattributeform.h b/src/gui/qgsattributeform.h index 69000a4df0b..ebd71bef0b5 100644 --- a/src/gui/qgsattributeform.h +++ b/src/gui/qgsattributeform.h @@ -276,6 +276,10 @@ class GUI_EXPORT QgsAttributeForm : public QWidget void updateJoinedFields( const QgsEditorWidgetWrapper &eww ); + bool fieldIsEditable( int fieldIndex ) const; + + bool fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const ; + struct WidgetInfo { WidgetInfo() From b8a90ca458a1577a77338a5bb409c06daf6f9749 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 20 Jul 2017 08:50:37 +0200 Subject: [PATCH 160/364] Add tests --- tests/src/gui/testqgsattributeform.cpp | 120 +++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/tests/src/gui/testqgsattributeform.cpp b/tests/src/gui/testqgsattributeform.cpp index 7a634e96f00..11b56c95c48 100644 --- a/tests/src/gui/testqgsattributeform.cpp +++ b/tests/src/gui/testqgsattributeform.cpp @@ -44,6 +44,7 @@ class TestQgsAttributeForm : public QObject void testOKButtonStatus(); void testDynamicForm(); void testConstraintsOnJoinedFields(); + void testEditableJoin(); }; void TestQgsAttributeForm::initTestCase() @@ -511,5 +512,124 @@ void TestQgsAttributeForm::testConstraintsOnJoinedFields() QCOMPARE( label->text(), "layerB_" + warningLabel ); } +void TestQgsAttributeForm::testEditableJoin() +{ + // make temporary layers + QString defA = QStringLiteral( "Point?field=id_a:integer" ); + QgsVectorLayer *layerA = new QgsVectorLayer( defA, QStringLiteral( "layerA" ), QStringLiteral( "memory" ) ); + + QString defB = QStringLiteral( "Point?field=id_b:integer&field=col0:integer" ); + QgsVectorLayer *layerB = new QgsVectorLayer( defB, QStringLiteral( "layerB" ), QStringLiteral( "memory" ) ); + + QString defC = QStringLiteral( "Point?field=id_c:integer&field=col0:integer" ); + QgsVectorLayer *layerC = new QgsVectorLayer( defC, QStringLiteral( "layerC" ), QStringLiteral( "memory" ) ); + + // join configuration + QgsVectorLayerJoinInfo infoJoinAB; + infoJoinAB.setTargetFieldName( "id_a" ); + infoJoinAB.setJoinLayer( layerB ); + infoJoinAB.setJoinFieldName( "id_b" ); + infoJoinAB.setDynamicFormEnabled( true ); + infoJoinAB.setEditable( true ); + + layerA->addJoin( infoJoinAB ); + + QgsVectorLayerJoinInfo infoJoinAC; + infoJoinAC.setTargetFieldName( "id_a" ); + infoJoinAC.setJoinLayer( layerC ); + infoJoinAC.setJoinFieldName( "id_c" ); + infoJoinAC.setDynamicFormEnabled( true ); + infoJoinAC.setEditable( false ); + + layerA->addJoin( infoJoinAC ); + + // add features for main layer + QgsFeature ftA( layerA->fields() ); + ftA.setAttribute( QStringLiteral( "id_a" ), 31 ); + layerA->startEditing(); + layerA->addFeature( ftA ); + layerA->commitChanges(); + + // add features for joined layers + QgsFeature ft0B( layerB->fields() ); + ft0B.setAttribute( QStringLiteral( "id_b" ), 31 ); + ft0B.setAttribute( QStringLiteral( "col0" ), 11 ); + layerB->startEditing(); + layerB->addFeature( ft0B ); + layerB->commitChanges(); + + QgsFeature ft0C( layerC->fields() ); + ft0C.setAttribute( QStringLiteral( "id_c" ), 31 ); + ft0C.setAttribute( QStringLiteral( "col0" ), 13 ); + layerC->startEditing(); + layerC->addFeature( ft0C ); + layerC->commitChanges(); + + // start editing layers + layerA->startEditing(); + layerB->startEditing(); + layerC->startEditing(); + + // build a form with feature A + ftA = layerA->getFeature( 1 ); + + QgsAttributeForm form( layerA ); + form.setMode( QgsAttributeForm::SingleEditMode ); + form.setFeature( ftA ); + + for ( int i = 0; i < ftA.fields().count(); i++ ) + std::cout << " - " << ftA.fields().field( i ).name().toStdString() << " : " << ftA.attribute( i ).toString().toStdString() << std::endl; + + // change layerA join id field to join with layerB and layerC + QgsEditorWidgetWrapper *ww = nullptr; + + ww = qobject_cast( form.mWidgets[0] ); + QCOMPARE( ww->field().name(), QString( "id_a" ) ); + QCOMPARE( ww->value(), QVariant( 31 ) ); + + ww = qobject_cast( form.mWidgets[1] ); + QCOMPARE( ww->field().name(), QString( "layerB_col0" ) ); + QCOMPARE( ww->value(), QVariant( 11 ) ); + + ww = qobject_cast( form.mWidgets[2] ); + QCOMPARE( ww->field().name(), QString( "layerC_col0" ) ); + QCOMPARE( ww->value(), QVariant( 13 ) ); + + // test if widget is enabled for layerA + ww = qobject_cast( form.mWidgets[0] ); + QCOMPARE( ww->widget()->isEnabled(), true ); + + // test if widget is enabled for layerB + ww = qobject_cast( form.mWidgets[1] ); + QCOMPARE( ww->widget()->isEnabled(), true ); + + // test if widget is disabled for layerC + ww = qobject_cast( form.mWidgets[2] ); + QCOMPARE( ww->widget()->isEnabled(), false ); + + // change attributes + form.changeAttribute( "layerB_col0", QVariant( 333 ) ); + form.changeAttribute( "layerC_col0", QVariant( 444 ) ); + form.save(); + + // commit changes + layerA->commitChanges(); + layerB->commitChanges(); + layerC->commitChanges(); + + // check attributes + ft0B = layerB->getFeature( 1 ); + QCOMPARE( ft0B.attribute( "col0" ), QVariant( 333 ) ); + + ft0C = layerC->getFeature( 1 ); + QCOMPARE( ft0C.attribute( "col0" ), QVariant( 13 ) ); + + // clean + delete layerA; + delete layerB; + delete layerC; +} + + QGSTEST_MAIN( TestQgsAttributeForm ) #include "testqgsattributeform.moc" From db11248b1ddbae089b29491e82b1944b5f5e18bd Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 20 Jul 2017 18:13:27 +0200 Subject: [PATCH 161/364] Fix column name in join configuration --- src/ui/qgsvectorlayerpropertiesbase.ui | 369 +++++-------------------- 1 file changed, 63 insertions(+), 306 deletions(-) diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index 10a81628884..46d87d0c806 100755 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -46,16 +46,7 @@ QFrame::Raised - - 0 - - - 0 - - - 0 - - + 0 @@ -282,16 +273,7 @@ QFrame::Raised - - 0 - - - 0 - - - 0 - - + 0 @@ -307,16 +289,7 @@ - - 0 - - - 0 - - - 0 - - + 0 @@ -328,16 +301,7 @@ QFrame::Raised - - 0 - - - 0 - - - 0 - - + 0 @@ -354,16 +318,7 @@ - - 0 - - - 0 - - - 0 - - + 0 @@ -379,21 +334,12 @@ 0 0 - 306 - 508 + 309 + 468 - - 0 - - - 0 - - - 0 - - + 0 @@ -401,7 +347,7 @@ Description - + vectormeta @@ -580,7 +526,7 @@ border-radius: 2px; Attribution - + vectormeta @@ -626,7 +572,7 @@ border-radius: 2px; MetadataUrl - + vectormeta @@ -805,16 +751,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -830,21 +767,12 @@ border-radius: 2px; 0 0 - 268 - 321 + 282 + 315 - - 0 - - - 0 - - - 0 - - + 0 @@ -862,16 +790,7 @@ border-radius: 2px; QFrame::Raised - - 0 - - - 0 - - - 0 - - + 0 @@ -920,7 +839,7 @@ border-radius: 2px; false - + vectorgeneral @@ -935,7 +854,7 @@ border-radius: 2px; - + Qt::StrongFocus @@ -1043,16 +962,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1073,16 +983,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1112,16 +1013,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1144,16 +1036,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1174,16 +1057,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1204,16 +1078,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1229,21 +1094,12 @@ border-radius: 2px; 0 0 - 632 - 276 + 634 + 324 - - 0 - - - 0 - - - 0 - - + 0 @@ -1256,7 +1112,7 @@ border-radius: 2px; - + Qt::StrongFocus @@ -1456,16 +1312,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1474,16 +1321,7 @@ border-radius: 2px; true - - 0 - - - 0 - - - 0 - - + 0 @@ -1494,7 +1332,7 @@ border-radius: 2px; - + Qt::StrongFocus @@ -1545,7 +1383,7 @@ border-radius: 2px; - + Qt::StrongFocus @@ -1564,16 +1402,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1594,16 +1423,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1630,16 +1450,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1656,20 +1467,11 @@ border-radius: 2px; 0 0 653 - 542 + 538 - - 0 - - - 0 - - - 0 - - + 0 @@ -1709,7 +1511,7 @@ border-radius: 2px; - New Column + Upsert On Edit @@ -1787,16 +1589,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1819,16 +1612,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1851,16 +1635,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1879,16 +1654,7 @@ border-radius: 2px; - - 0 - - - 0 - - - 0 - - + 0 @@ -1963,16 +1729,7 @@ border-radius: 2px; QFrame::Raised - - 0 - - - 0 - - - 0 - - + 0 @@ -1991,12 +1748,6 @@ border-radius: 2px; - - QgsScrollArea - QScrollArea -
    qgsscrollarea.h
    - 1 -
    QgsCollapsibleGroupBox QGroupBox @@ -2010,10 +1761,9 @@ border-radius: 2px; 1 - QgsLayerTreeView - QTreeView -
    qgslayertreeview.h
    - 1 + QgsFilterLineEdit + QLineEdit +
    qgsfilterlineedit.h
    QgsProjectionSelectionWidget @@ -2021,17 +1771,29 @@ border-radius: 2px;
    qgsprojectionselectionwidget.h
    1
    + + QgsScaleRangeWidget + QWidget +
    qgsscalerangewidget.h
    +
    + + QgsScrollArea + QScrollArea +
    qgsscrollarea.h
    + 1 +
    + + QgsLayerTreeView + QTreeView +
    qgslayertreeview.h
    + 1 +
    QgsVariableEditorWidget QWidget
    qgsvariableeditorwidget.h
    1
    - - QgsScaleRangeWidget - QWidget -
    qgsscalerangewidget.h
    -
    QgsScaleComboBox QComboBox @@ -2048,11 +1810,6 @@ border-radius: 2px; QWidget
    qgscodeeditorhtml.h
    - - QgsFilterLineEdit - QLineEdit -
    qgsfilterlineedit.h
    -
    mOptionsListWidget From 1c402ef42129147a05f3bdb27510e2e58aab849e Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 20 Jul 2017 20:24:22 +0200 Subject: [PATCH 162/364] Implements upsertOnEdit option --- src/core/qgsvectorlayer.cpp | 60 +++++++++++++++++++++++++++++ src/core/qgsvectorlayer.h | 2 + src/core/qgsvectorlayerjoininfo.cpp | 23 +++++++++++ src/core/qgsvectorlayerjoininfo.h | 8 ++++ 4 files changed, 93 insertions(+) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index c8907074001..d286b578e1a 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -947,7 +947,10 @@ bool QgsVectorLayer::addFeature( QgsFeature &feature, Flags ) bool success = mEditBuffer->addFeature( feature ); if ( success ) + { updateExtents(); + success = addFeaturesToJoinedLayers( QgsFeatureList() << feature ); + } return success; } @@ -2633,9 +2636,66 @@ bool QgsVectorLayer::addFeatures( QgsFeatureList &features, Flags ) bool res = mEditBuffer->addFeatures( features ); updateExtents(); + if ( res ) + res = addFeaturesToJoinedLayers( features ); + return res; } +bool QgsVectorLayer::addFeaturesToJoinedLayers( QgsFeatureList &features, Flags ) +{ + // try to add/update a feature in each joined layer + Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() ) + { + QgsVectorLayer *joinLayer = info.joinLayer(); + + if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.isUpsertOnEdit() ) + { + QgsFeatureList joinFeatures; + + Q_FOREACH ( const QgsFeature &feature, features ) + { + const QgsFeature joinFeature = info.extractJoinedFeature( feature ); + + // we don't want to add a new feature in joined layer when the id + // column value yet exist, we just want to update the existing one + const QVariant idFieldValue = feature.attribute( info.targetFieldName() ); + const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() ); + + QgsFeatureRequest request; + request.setFilterExpression( filter ); + request.setLimit( 1 ); + + QgsFeatureIterator it = info.joinLayer()->getFeatures( request ); + QgsFeature existingFeature; + it.nextFeature( existingFeature ); + + if ( existingFeature.isValid() ) + { + const QStringList *subsetFields = info.joinFieldNamesSubset(); + if ( subsetFields ) + { + Q_FOREACH ( const QString &field, *subsetFields ) + existingFeature.setAttribute( field, joinFeature.attribute( field ) ); + } + else + { + Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + existingFeature.setAttribute( field.name(), joinFeature.attribute( field.name() ) ); + } + + joinLayer->updateFeature( existingFeature ); + } + else + joinFeatures << joinFeature; + } + + joinLayer->addFeatures( joinFeatures ); + } + } + + return true; +} void QgsVectorLayer::setCoordinateSystem() { diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 69d1488bcd4..d3fbece1932 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1932,6 +1932,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte //! Read simple labeling from layer's custom properties (QGIS 2.x projects) QgsAbstractVectorLayerLabeling *readLabelingFromCustomProperties(); + bool addFeaturesToJoinedLayers( QgsFeatureList &features, Flags flags = 0 ); + #ifdef SIP_RUN QgsVectorLayer( const QgsVectorLayer &rhs ); #endif diff --git a/src/core/qgsvectorlayerjoininfo.cpp b/src/core/qgsvectorlayerjoininfo.cpp index 8fb8d0d8697..1a3a5d1aa2a 100644 --- a/src/core/qgsvectorlayerjoininfo.cpp +++ b/src/core/qgsvectorlayerjoininfo.cpp @@ -44,3 +44,26 @@ void QgsVectorLayerJoinInfo::setEditable( bool enabled ) setUpsertOnEdit( false ); } } + +QgsFeature QgsVectorLayerJoinInfo::extractJoinedFeature( const QgsFeature &feature ) const +{ + QgsFeature joinFeature; + + if ( joinLayer() ) + { + const QVariant idFieldValue = feature.attribute( targetFieldName() ); + joinFeature.initAttributes( joinLayer()->fields().count() ); + joinFeature.setFields( joinLayer()->fields() ); + joinFeature.setAttribute( joinFieldName(), idFieldValue ); + + Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + { + const QString prefixedName = prefixedFieldName( field ); + + if ( feature.fieldNameIndex( prefixedName ) != -1 ) + joinFeature.setAttribute( field.name(), feature.attribute( prefixedName ) ); + } + } + + return joinFeature; +} diff --git a/src/core/qgsvectorlayerjoininfo.h b/src/core/qgsvectorlayerjoininfo.h index fa02e46cf1a..7cb4d3f9071 100644 --- a/src/core/qgsvectorlayerjoininfo.h +++ b/src/core/qgsvectorlayerjoininfo.h @@ -123,6 +123,14 @@ class CORE_EXPORT QgsVectorLayerJoinInfo */ QString prefixedFieldName( const QgsField &field ) const; + /** Extract the join feature from the target feature for the current + * join layer information. + * \param feature A feature from the target layer + * \returns the corresponding joined feature + * \since QGIS 3.0 + */ + QgsFeature extractJoinedFeature( const QgsFeature &feature ) const; + bool operator==( const QgsVectorLayerJoinInfo &other ) const { return mTargetFieldName == other.mTargetFieldName && From c347055d8257e2e1f2e71265957642db33521480 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 20 Jul 2017 20:25:01 +0200 Subject: [PATCH 163/364] Update sip binding --- python/core/qgsvectorlayerjoininfo.sip | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/core/qgsvectorlayerjoininfo.sip b/python/core/qgsvectorlayerjoininfo.sip index ae8f7e9a826..8c58def4611 100644 --- a/python/core/qgsvectorlayerjoininfo.sip +++ b/python/core/qgsvectorlayerjoininfo.sip @@ -156,6 +156,16 @@ Returns whether values from the joined layer should be cached in memory to speed :rtype: str %End + QgsFeature extractJoinedFeature( const QgsFeature &feature ) const; +%Docstring + Extract the join feature from the target feature for the current + join layer information. + \param feature A feature from the target layer + :return: the corresponding joined feature +.. versionadded:: 3.0 + :rtype: QgsFeature +%End + bool operator==( const QgsVectorLayerJoinInfo &other ) const; void setJoinFieldNamesSubset( QStringList *fieldNamesSubset /Transfer/ ); From cf5ab4b0f1242caba40c77eb1bd037676fa325af Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 20 Jul 2017 20:25:09 +0200 Subject: [PATCH 164/364] Add tests for upsertOnEdit option --- tests/src/gui/testqgsattributeform.cpp | 132 ++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/tests/src/gui/testqgsattributeform.cpp b/tests/src/gui/testqgsattributeform.cpp index 11b56c95c48..882430e68dc 100644 --- a/tests/src/gui/testqgsattributeform.cpp +++ b/tests/src/gui/testqgsattributeform.cpp @@ -45,6 +45,7 @@ class TestQgsAttributeForm : public QObject void testDynamicForm(); void testConstraintsOnJoinedFields(); void testEditableJoin(); + void testUpsertOnEdit(); }; void TestQgsAttributeForm::initTestCase() @@ -577,9 +578,6 @@ void TestQgsAttributeForm::testEditableJoin() form.setMode( QgsAttributeForm::SingleEditMode ); form.setFeature( ftA ); - for ( int i = 0; i < ftA.fields().count(); i++ ) - std::cout << " - " << ftA.fields().field( i ).name().toStdString() << " : " << ftA.attribute( i ).toString().toStdString() << std::endl; - // change layerA join id field to join with layerB and layerC QgsEditorWidgetWrapper *ww = nullptr; @@ -630,6 +628,134 @@ void TestQgsAttributeForm::testEditableJoin() delete layerC; } +void TestQgsAttributeForm::testUpsertOnEdit() +{ + // make temporary layers + QString defA = QStringLiteral( "Point?field=id_a:integer" ); + QgsVectorLayer *layerA = new QgsVectorLayer( defA, QStringLiteral( "layerA" ), QStringLiteral( "memory" ) ); + + QString defB = QStringLiteral( "Point?field=id_b:integer&field=col0:integer" ); + QgsVectorLayer *layerB = new QgsVectorLayer( defB, QStringLiteral( "layerB" ), QStringLiteral( "memory" ) ); + + QString defC = QStringLiteral( "Point?field=id_c:integer&field=col0:integer" ); + QgsVectorLayer *layerC = new QgsVectorLayer( defC, QStringLiteral( "layerC" ), QStringLiteral( "memory" ) ); + + // join configuration + QgsVectorLayerJoinInfo infoJoinAB; + infoJoinAB.setTargetFieldName( "id_a" ); + infoJoinAB.setJoinLayer( layerB ); + infoJoinAB.setJoinFieldName( "id_b" ); + infoJoinAB.setDynamicFormEnabled( true ); + infoJoinAB.setEditable( true ); + infoJoinAB.setUpsertOnEdit( true ); + + layerA->addJoin( infoJoinAB ); + + QgsVectorLayerJoinInfo infoJoinAC; + infoJoinAC.setTargetFieldName( "id_a" ); + infoJoinAC.setJoinLayer( layerC ); + infoJoinAC.setJoinFieldName( "id_c" ); + infoJoinAC.setDynamicFormEnabled( true ); + infoJoinAC.setEditable( true ); + infoJoinAC.setUpsertOnEdit( false ); + + layerA->addJoin( infoJoinAC ); + + // add features for main layer + QgsFeature ftA( layerA->fields() ); + ftA.setAttribute( QStringLiteral( "id_a" ), 31 ); + layerA->startEditing(); + layerA->addFeature( ftA ); + layerA->commitChanges(); + + // add features for joined layers + QgsFeature ft0B( layerB->fields() ); + ft0B.setAttribute( QStringLiteral( "id_b" ), 33 ); + ft0B.setAttribute( QStringLiteral( "col0" ), 11 ); + layerB->startEditing(); + layerB->addFeature( ft0B ); + layerB->commitChanges(); + + QgsFeature ft0C( layerC->fields() ); + ft0C.setAttribute( QStringLiteral( "id_c" ), 31 ); + ft0C.setAttribute( QStringLiteral( "col0" ), 13 ); + layerC->startEditing(); + layerC->addFeature( ft0C ); + layerC->commitChanges(); + + // start editing layers + layerA->startEditing(); + layerB->startEditing(); + layerC->startEditing(); + + // build a form with feature A + ftA = layerA->getFeature( 1 ); + + QgsAttributeForm form( layerA ); + form.setMode( QgsAttributeForm::AddFeatureMode ); + form.setFeature( ftA ); + + // count features + QCOMPARE( ( int )layerA->featureCount(), 1 ); + QCOMPARE( ( int )layerB->featureCount(), 1 ); + QCOMPARE( ( int )layerC->featureCount(), 1 ); + + // add a new feature + form.changeAttribute( "id_a", QVariant( 32 ) ); + form.changeAttribute( "layerB_col0", QVariant( 3232 ) ); + form.changeAttribute( "layerC_col0", QVariant( 323232 ) ); + form.save(); + + // count features + QCOMPARE( ( int )layerA->featureCount(), 2 ); + QCOMPARE( ( int )layerB->featureCount(), 2 ); + QCOMPARE( ( int )layerC->featureCount(), 1 ); + + // commit changes + layerA->commitChanges(); + layerB->commitChanges(); + layerC->commitChanges(); + + // check attributes + ft0B = layerB->getFeature( 2 ); + QCOMPARE( ft0B.attribute( "id_b" ), QVariant( 32 ) ); + QCOMPARE( ft0B.attribute( "col0" ), QVariant( 3232 ) ); + + // start editing layers + layerA->startEditing(); + layerB->startEditing(); + layerC->startEditing(); + + // create a target feature but update a joined feature + QgsAttributeForm formUpdate( layerA ); + formUpdate.setMode( QgsAttributeForm::AddFeatureMode ); + formUpdate.setFeature( ftA ); + formUpdate.changeAttribute( "id_a", QVariant( 33 ) ); + formUpdate.changeAttribute( "layerB_col0", QVariant( 3333 ) ); + formUpdate.changeAttribute( "layerC_col0", QVariant( 323232 ) ); + formUpdate.save(); + + // count features + QCOMPARE( ( int )layerA->featureCount(), 3 ); + QCOMPARE( ( int )layerB->featureCount(), 2 ); + QCOMPARE( ( int )layerC->featureCount(), 1 ); + + // commit changes + layerA->commitChanges(); + layerB->commitChanges(); + layerC->commitChanges(); + + // check attributes + ft0B = layerB->getFeature( 1 ); + QCOMPARE( ft0B.attribute( "id_b" ), QVariant( 33 ) ); + QCOMPARE( ft0B.attribute( "col0" ), QVariant( 3333 ) ); + + // clean + delete layerA; + delete layerB; + delete layerC; +} + QGSTEST_MAIN( TestQgsAttributeForm ) #include "testqgsattributeform.moc" From f92c23fdfb57a6657d01f8f5fcf44d9582496a33 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 20 Jul 2017 20:34:36 +0200 Subject: [PATCH 165/364] Small cleanup --- src/core/qgsvectorlayer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index d286b578e1a..8154c2ee031 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -949,7 +949,9 @@ bool QgsVectorLayer::addFeature( QgsFeature &feature, Flags ) if ( success ) { updateExtents(); - success = addFeaturesToJoinedLayers( QgsFeatureList() << feature ); + + if ( mJoinBuffer->containsJoins() ) + success = addFeaturesToJoinedLayers( QgsFeatureList() << feature ); } return success; @@ -2636,7 +2638,7 @@ bool QgsVectorLayer::addFeatures( QgsFeatureList &features, Flags ) bool res = mEditBuffer->addFeatures( features ); updateExtents(); - if ( res ) + if ( res && mJoinBuffer->containsJoins() ) res = addFeaturesToJoinedLayers( features ); return res; From 293f0f41ba9dc740eb146f39879c491d6b632e79 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 21 Jul 2017 08:59:58 +0100 Subject: [PATCH 166/364] Implements deleteCascade option --- src/core/qgsvectorlayer.cpp | 26 ++++++++++++++++++++++++++ src/core/qgsvectorlayer.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 8154c2ee031..01072f8a937 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2438,6 +2438,9 @@ bool QgsVectorLayer::deleteFeature( QgsFeatureId fid ) if ( !mEditBuffer ) return false; + if ( mJoinBuffer->containsJoins() ) + deleteFeaturesFromJoinedLayers( QgsFeatureIds() << fid ); + bool res = mEditBuffer->deleteFeature( fid ); if ( res ) { @@ -2456,6 +2459,9 @@ bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds &fids ) return false; } + if ( mJoinBuffer->containsJoins() ) + deleteFeaturesFromJoinedLayers( fids ); + bool res = mEditBuffer->deleteFeatures( fids ); if ( res ) @@ -2467,6 +2473,26 @@ bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds &fids ) return res; } +bool QgsVectorLayer::deleteFeaturesFromJoinedLayers( QgsFeatureIds fids ) +{ + bool rc = false; + + Q_FOREACH ( const QgsFeatureId &fid, fids ) + { + Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() ) + { + if ( info.isEditable() && info.isDeleteCascade() ) + { + QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( &info, getFeature( fid ) ); + if ( joinFeature.isValid() ) + info.joinLayer()->deleteFeature( joinFeature.id() ); + } + } + } + + return rc; +} + QgsAttributeList QgsVectorLayer::pkAttributeList() const { QgsAttributeList pkAttributesList; diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index d3fbece1932..28fdc70b64d 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1933,6 +1933,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte QgsAbstractVectorLayerLabeling *readLabelingFromCustomProperties(); bool addFeaturesToJoinedLayers( QgsFeatureList &features, Flags flags = 0 ); + bool deleteFeaturesFromJoinedLayers( QgsFeatureIds fids ); #ifdef SIP_RUN QgsVectorLayer( const QgsVectorLayer &rhs ); From 4be1cc006742a4601fdf7f2783d342f6cdbbedc6 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 21 Jul 2017 09:00:51 +0100 Subject: [PATCH 167/364] Add tests for deleteCascade option --- tests/src/python/test_qgsvectorlayer.py | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index 30fe77f7e80..f754a02af97 100644 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -54,6 +54,7 @@ from qgis.core import (QgsWkbTypes, QgsDiagramLayerSettings, QgsTextFormat, QgsVectorLayerSelectedFeatureSource, + QgsExpression, NULL) from qgis.gui import (QgsAttributeTableModel, QgsGui @@ -453,6 +454,53 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertEqual(layer.dataProvider().featureCount(), 0) + def test_DeleteJoinedFeature(self): + joinLayer = createJoinLayer() + joinLayer2 = createJoinLayer() + QgsProject.instance().addMapLayers([joinLayer, joinLayer2]) + + layer = createLayerWithOnePoint() + + join = QgsVectorLayerJoinInfo() + join.setTargetFieldName("fldint") + join.setJoinLayer(joinLayer) + join.setJoinFieldName("y") + join.setUsingMemoryCache(True) + join.setEditable(True) + join.setDeleteCascade(True) + + layer.addJoin(join) + + join2 = QgsVectorLayerJoinInfo() + join2.setTargetFieldName("fldint") + join2.setJoinLayer(joinLayer2) + join2.setJoinFieldName("y") + join2.setUsingMemoryCache(True) + join2.setPrefix("custom-prefix_") + join2.setEditable(True) + join2.setDeleteCascade(False) + + layer.addJoin(join2) + + # check number of features + self.assertEqual(layer.featureCount(), 1) + self.assertEqual(joinLayer.featureCount(), 4) + self.assertEqual(joinLayer2.featureCount(), 4) + + # delete a feature which is also in joined layers + layer.startEditing() + joinLayer.startEditing() + joinLayer2.startEditing() + + filter = QgsExpression.createFieldEqualityExpression('fldint', '123') + feature = next(layer.getFeatures(QgsFeatureRequest().setFilterExpression(filter))) + layer.deleteFeature(feature.id()) + + # check number of features + self.assertEqual(layer.featureCount(), 0) + self.assertEqual(joinLayer.featureCount(), 3) # deleteCascade activated + self.assertEqual(joinLayer2.featureCount(), 4) # deleteCascade deactivated + # CHANGE ATTRIBUTE def test_ChangeAttribute(self): From cad038b1bf8fb20261ebc290d8ca9df8e21cdaec Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 24 Jul 2017 07:51:04 +0100 Subject: [PATCH 168/364] Initialize joined feature properly --- src/core/qgsvectorlayerjoinbuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index ae69a0505b9..1221d37e716 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -422,6 +422,7 @@ QgsFeature QgsVectorLayerJoinBuffer::joinedFeatureOf( const QgsVectorLayerJoinIn if ( info->joinLayer() ) { + joinedFeature.initAttributes( info->joinLayer()->fields().count() ); joinedFeature.setFields( info->joinLayer()->fields() ); QString joinFieldName = info->joinFieldName(); From 97ad226f64b6f96ca9c2769b380229a474623304 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 24 Jul 2017 16:55:37 +0100 Subject: [PATCH 169/364] Rename isUpsertOnEdit in hasUpsertOnEdit --- python/core/qgsvectorlayerjoininfo.sip | 2 +- src/app/qgsjoindialog.cpp | 2 +- src/app/qgsvectorlayerproperties.cpp | 2 +- src/core/qgsvectorlayer.cpp | 2 +- src/core/qgsvectorlayerjoinbuffer.cpp | 2 +- src/core/qgsvectorlayerjoininfo.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/core/qgsvectorlayerjoininfo.sip b/python/core/qgsvectorlayerjoininfo.sip index 8c58def4611..5a320bbf13e 100644 --- a/python/core/qgsvectorlayerjoininfo.sip +++ b/python/core/qgsvectorlayerjoininfo.sip @@ -117,7 +117,7 @@ Returns whether values from the joined layer should be cached in memory to speed .. versionadded:: 3.0 %End - bool isUpsertOnEdit() const; + bool hasUpsertOnEdit() const; %Docstring Returns whether a feature created on the target layer has to impact the joined layer by creating a new feature if necessary. diff --git a/src/app/qgsjoindialog.cpp b/src/app/qgsjoindialog.cpp index 0ed756726cf..641944bcb1d 100644 --- a/src/app/qgsjoindialog.cpp +++ b/src/app/qgsjoindialog.cpp @@ -77,7 +77,7 @@ void QgsJoinDialog::setJoinInfo( const QgsVectorLayerJoinInfo &joinInfo ) mCacheInMemoryCheckBox->setChecked( joinInfo.isUsingMemoryCache() ); mDynamicFormCheckBox->setChecked( joinInfo.isDynamicFormEnabled() ); mEditableJoinLayer->setChecked( joinInfo.isEditable() ); - mUpsertOnEditCheckBox->setChecked( joinInfo.isUpsertOnEdit() ); + mUpsertOnEditCheckBox->setChecked( joinInfo.hasUpsertOnEdit() ); mDeleteCascadeCheckBox->setChecked( joinInfo.isDeleteCascade() ); if ( joinInfo.prefix().isNull() ) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 205b873471b..95a809f2e7f 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -1246,7 +1246,7 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo joinItem->setText( 5, QChar( 0x2714 ) ); } - if ( join.isUpsertOnEdit() ) + if ( join.hasUpsertOnEdit() ) { joinItem->setText( 6, QChar( 0x2714 ) ); } diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 01072f8a937..a5502fc516e 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2677,7 +2677,7 @@ bool QgsVectorLayer::addFeaturesToJoinedLayers( QgsFeatureList &features, Flags { QgsVectorLayer *joinLayer = info.joinLayer(); - if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.isUpsertOnEdit() ) + if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() ) { QgsFeatureList joinFeatures; diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index 1221d37e716..f721faeec92 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -277,7 +277,7 @@ void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &doc joinElem.setAttribute( QStringLiteral( "memoryCache" ), joinIt->isUsingMemoryCache() ); joinElem.setAttribute( QStringLiteral( "dynamicForm" ), joinIt->isDynamicFormEnabled() ); joinElem.setAttribute( QStringLiteral( "editable" ), joinIt->isEditable() ); - joinElem.setAttribute( QStringLiteral( "upsertOnEdit" ), joinIt->isUpsertOnEdit() ); + joinElem.setAttribute( QStringLiteral( "upsertOnEdit" ), joinIt->hasUpsertOnEdit() ); joinElem.setAttribute( QStringLiteral( "deleteCascade" ), joinIt->isDeleteCascade() ); if ( joinIt->joinFieldNamesSubset() ) diff --git a/src/core/qgsvectorlayerjoininfo.h b/src/core/qgsvectorlayerjoininfo.h index 7cb4d3f9071..ed35e8ce6bc 100644 --- a/src/core/qgsvectorlayerjoininfo.h +++ b/src/core/qgsvectorlayerjoininfo.h @@ -96,7 +96,7 @@ class CORE_EXPORT QgsVectorLayerJoinInfo * the joined layer by creating a new feature if necessary. * \since QGIS 3.0 */ - bool isUpsertOnEdit() const { return mUpsertOnEdit; } + bool hasUpsertOnEdit() const { return mUpsertOnEdit; } /** Sets whether a feature created on the target layer has to impact * the joined layer by creating a new feature if necessary. From 8b70d7fa4b29decf5dd0c22f136c7e8248b894f3 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 24 Jul 2017 17:06:50 +0100 Subject: [PATCH 170/364] Rename setDeleteCascade/isDeleteCascade in setCascadedDelete/hasCascadedDelete --- python/core/qgsvectorlayerjoininfo.sip | 4 ++-- src/app/qgsjoindialog.cpp | 4 ++-- src/app/qgsvectorlayerproperties.cpp | 2 +- src/core/qgsvectorlayer.cpp | 2 +- src/core/qgsvectorlayerjoinbuffer.cpp | 4 ++-- src/core/qgsvectorlayerjoininfo.cpp | 2 +- src/core/qgsvectorlayerjoininfo.h | 6 +++--- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/python/core/qgsvectorlayerjoininfo.sip b/python/core/qgsvectorlayerjoininfo.sip index 5a320bbf13e..9d3d38411ea 100644 --- a/python/core/qgsvectorlayerjoininfo.sip +++ b/python/core/qgsvectorlayerjoininfo.sip @@ -132,7 +132,7 @@ Returns whether values from the joined layer should be cached in memory to speed .. versionadded:: 3.0 %End - bool isDeleteCascade() const; + bool hasCascadedDelete() const; %Docstring Returns whether a feature deleted on the target layer has to impact the joined layer by deleting the corresponding joined feature. @@ -140,7 +140,7 @@ Returns whether values from the joined layer should be cached in memory to speed :rtype: bool %End - void setDeleteCascade( bool enabled ); + void setCascadedDelete( bool enabled ); %Docstring Sets whether a feature deleted on the target layer has to impact the joined layer by deleting the corresponding joined feature. diff --git a/src/app/qgsjoindialog.cpp b/src/app/qgsjoindialog.cpp index 641944bcb1d..61f50d04956 100644 --- a/src/app/qgsjoindialog.cpp +++ b/src/app/qgsjoindialog.cpp @@ -78,7 +78,7 @@ void QgsJoinDialog::setJoinInfo( const QgsVectorLayerJoinInfo &joinInfo ) mDynamicFormCheckBox->setChecked( joinInfo.isDynamicFormEnabled() ); mEditableJoinLayer->setChecked( joinInfo.isEditable() ); mUpsertOnEditCheckBox->setChecked( joinInfo.hasUpsertOnEdit() ); - mDeleteCascadeCheckBox->setChecked( joinInfo.isDeleteCascade() ); + mDeleteCascadeCheckBox->setChecked( joinInfo.hasCascadedDelete() ); if ( joinInfo.prefix().isNull() ) { @@ -123,7 +123,7 @@ QgsVectorLayerJoinInfo QgsJoinDialog::joinInfo() const if ( info.isEditable() ) { info.setUpsertOnEdit( mUpsertOnEditCheckBox->isChecked() ); - info.setDeleteCascade( mDeleteCascadeCheckBox->isChecked() ); + info.setCascadedDelete( mDeleteCascadeCheckBox->isChecked() ); } if ( mUseCustomPrefix->isChecked() ) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 95a809f2e7f..bbdab2d6dd2 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -1251,7 +1251,7 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo joinItem->setText( 6, QChar( 0x2714 ) ); } - if ( join.isDeleteCascade() ) + if ( join.hasCascadedDelete() ) { joinItem->setText( 7, QChar( 0x2714 ) ); } diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index a5502fc516e..5660c570a4f 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2481,7 +2481,7 @@ bool QgsVectorLayer::deleteFeaturesFromJoinedLayers( QgsFeatureIds fids ) { Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() ) { - if ( info.isEditable() && info.isDeleteCascade() ) + if ( info.isEditable() && info.hasCascadedDelete() ) { QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( &info, getFeature( fid ) ); if ( joinFeature.isValid() ) diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index f721faeec92..86ed5c15444 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -278,7 +278,7 @@ void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &doc joinElem.setAttribute( QStringLiteral( "dynamicForm" ), joinIt->isDynamicFormEnabled() ); joinElem.setAttribute( QStringLiteral( "editable" ), joinIt->isEditable() ); joinElem.setAttribute( QStringLiteral( "upsertOnEdit" ), joinIt->hasUpsertOnEdit() ); - joinElem.setAttribute( QStringLiteral( "deleteCascade" ), joinIt->isDeleteCascade() ); + joinElem.setAttribute( QStringLiteral( "cascadedDelete" ), joinIt->hasCascadedDelete() ); if ( joinIt->joinFieldNamesSubset() ) { @@ -322,7 +322,7 @@ void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node ) info.setDynamicFormEnabled( infoElem.attribute( QStringLiteral( "dynamicForm" ) ).toInt() ); info.setEditable( infoElem.attribute( QStringLiteral( "editable" ) ).toInt() ); info.setUpsertOnEdit( infoElem.attribute( QStringLiteral( "upsertOnEdit" ) ).toInt() ); - info.setDeleteCascade( infoElem.attribute( QStringLiteral( "deleteCascade" ) ).toInt() ); + info.setCascadedDelete( infoElem.attribute( QStringLiteral( "cascadedDelete" ) ).toInt() ); QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( "joinFieldsSubset" ) ); if ( !subsetElem.isNull() ) diff --git a/src/core/qgsvectorlayerjoininfo.cpp b/src/core/qgsvectorlayerjoininfo.cpp index 1a3a5d1aa2a..7b3bfda29b3 100644 --- a/src/core/qgsvectorlayerjoininfo.cpp +++ b/src/core/qgsvectorlayerjoininfo.cpp @@ -40,7 +40,7 @@ void QgsVectorLayerJoinInfo::setEditable( bool enabled ) if ( ! mEditable ) { - setDeleteCascade( false ); + setCascadedDelete( false ); setUpsertOnEdit( false ); } } diff --git a/src/core/qgsvectorlayerjoininfo.h b/src/core/qgsvectorlayerjoininfo.h index ed35e8ce6bc..d6722abc6b4 100644 --- a/src/core/qgsvectorlayerjoininfo.h +++ b/src/core/qgsvectorlayerjoininfo.h @@ -108,13 +108,13 @@ class CORE_EXPORT QgsVectorLayerJoinInfo * joined layer by deleting the corresponding joined feature. * \since QGIS 3.0 */ - bool isDeleteCascade() const { return mDeleteCascade; } + bool hasCascadedDelete() const { return mCascadedDelete; } /** Sets whether a feature deleted on the target layer has to impact the * joined layer by deleting the corresponding joined feature. * \since QGIS 3.0 */ - void setDeleteCascade( bool enabled ) { mDeleteCascade = enabled; } + void setCascadedDelete( bool enabled ) { mCascadedDelete = enabled; } /** Returns the prefixed name of the field. * \param field the field @@ -182,7 +182,7 @@ class CORE_EXPORT QgsVectorLayerJoinInfo bool mUpsertOnEdit; - bool mDeleteCascade; + bool mCascadedDelete; //! Cache for joined attributes to provide fast lookup (size is 0 if no memory caching) QHash< QString, QgsAttributes> cachedAttributes; From e0c708c8a1845398f5eb90e8972a3e82f06af773 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 24 Jul 2017 17:07:59 +0100 Subject: [PATCH 171/364] Initialize attributes --- src/core/qgsvectorlayerjoininfo.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/qgsvectorlayerjoininfo.h b/src/core/qgsvectorlayerjoininfo.h index d6722abc6b4..a2d867aba42 100644 --- a/src/core/qgsvectorlayerjoininfo.h +++ b/src/core/qgsvectorlayerjoininfo.h @@ -178,11 +178,11 @@ class CORE_EXPORT QgsVectorLayerJoinInfo bool mDynamicForm = false; - bool mEditable; + bool mEditable = false; - bool mUpsertOnEdit; + bool mUpsertOnEdit = false; - bool mCascadedDelete; + bool mCascadedDelete = false; //! Cache for joined attributes to provide fast lookup (size is 0 if no memory caching) QHash< QString, QgsAttributes> cachedAttributes; From d4789db19a3cdbcaef8007d6fc892d02e7786b70 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 25 Jul 2017 07:31:21 +0100 Subject: [PATCH 172/364] Fix tests --- tests/src/python/test_qgsvectorlayer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index f754a02af97..8ff0fe89810 100644 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -467,7 +467,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): join.setJoinFieldName("y") join.setUsingMemoryCache(True) join.setEditable(True) - join.setDeleteCascade(True) + join.setCascadedDelete(True) layer.addJoin(join) @@ -478,7 +478,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): join2.setUsingMemoryCache(True) join2.setPrefix("custom-prefix_") join2.setEditable(True) - join2.setDeleteCascade(False) + join2.setCascadedDelete(False) layer.addJoin(join2) From 5c5006c7552bbe0683cd1cbd3ab8f7dbc24a3cb3 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Wed, 26 Jul 2017 10:42:53 +0100 Subject: [PATCH 173/364] Add icons for joins status in attribute form --- images/images.qrc | 3 + .../default/mIconJoinHasNotUpsertOnEdit.svg | 161 +++++++++++++++ .../themes/default/mIconJoinNotEditable.svg | 167 +++++++++++++++ .../default/mIconJoinedLayerNotEditable.svg | 194 ++++++++++++++++++ src/gui/qgsattributeform.cpp | 63 +++++- src/gui/qgsattributeform.h | 6 + 6 files changed, 591 insertions(+), 3 deletions(-) create mode 100644 images/themes/default/mIconJoinHasNotUpsertOnEdit.svg create mode 100644 images/themes/default/mIconJoinNotEditable.svg create mode 100644 images/themes/default/mIconJoinedLayerNotEditable.svg diff --git a/images/images.qrc b/images/images.qrc index cf994dbbe4e..85adac47d00 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -342,6 +342,9 @@ themes/default/mIconDelete.png themes/default/mIconDeselected.svg themes/default/mIconDropDownMenu.svg + themes/default/mIconJoinNotEditable.svg + themes/default/mIconJoinedLayerNotEditable.svg + themes/default/mIconJoinHasNotUpsertOnEdit.svg themes/default/mIconEditableEdits.svg themes/default/mIconExpand.svg themes/default/mIconExpandSmall.svg diff --git a/images/themes/default/mIconJoinHasNotUpsertOnEdit.svg b/images/themes/default/mIconJoinHasNotUpsertOnEdit.svg new file mode 100644 index 00000000000..153c764ec47 --- /dev/null +++ b/images/themes/default/mIconJoinHasNotUpsertOnEdit.svg @@ -0,0 +1,161 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/themes/default/mIconJoinNotEditable.svg b/images/themes/default/mIconJoinNotEditable.svg new file mode 100644 index 00000000000..8b4bb54764b --- /dev/null +++ b/images/themes/default/mIconJoinNotEditable.svg @@ -0,0 +1,167 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/themes/default/mIconJoinedLayerNotEditable.svg b/images/themes/default/mIconJoinedLayerNotEditable.svg new file mode 100644 index 00000000000..9dbe149fe56 --- /dev/null +++ b/images/themes/default/mIconJoinedLayerNotEditable.svg @@ -0,0 +1,194 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index a11cd49a962..46da91e79fc 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -50,7 +50,6 @@ #include #include #include -#include int QgsAttributeForm::sFormCounter = 0; @@ -1023,6 +1022,8 @@ void QgsAttributeForm::synchronizeEnabledState() { bool enabled = isEditable && fieldIsEditable( eww->fieldIdx() ); ww->setEnabled( enabled ); + + updateIcon( eww ); } } @@ -1230,6 +1231,8 @@ void QgsAttributeForm::init() // Autogenerate Layout // If there is still no layout loaded (defined as autogenerate or other methods failed) + mIconMap.clear(); + if ( !formWidget ) { formWidget = new QWidget( this ); @@ -1271,6 +1274,9 @@ void QgsAttributeForm::init() // This will also create the widget QLabel *l = new QLabel( fieldName ); + QSvgWidget *i = new QSvgWidget(); + i->setFixedSize( 18, 18 ); + QgsEditorWidgetWrapper *eww = QgsGui::editorWidgetRegistry()->create( widgetSetup.type(), mLayer, idx, widgetSetup.config(), nullptr, this, mContext ); QWidget *w = nullptr; @@ -1293,17 +1299,22 @@ void QgsAttributeForm::init() w->setObjectName( field.name() ); if ( eww ) + { addWidgetWrapper( eww ); + mIconMap[eww->widget()] = i; + } if ( labelOnTop ) { gridLayout->addWidget( l, row++, 0, 1, 2 ); gridLayout->addWidget( w, row++, 0, 1, 2 ); + gridLayout->addWidget( i, row++, 0, 1, 2 ); } else { gridLayout->addWidget( l, row, 0 ); - gridLayout->addWidget( w, row++, 1 ); + gridLayout->addWidget( w, row, 1 ); + gridLayout->addWidget( i, row++, 2 ); } } @@ -2025,7 +2036,9 @@ bool QgsAttributeForm::fieldIsEditable( int fieldIndex ) const int srcFieldIndex; const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( fieldIndex, mLayer->fields(), srcFieldIndex ); - if ( info && info->isEditable() && info->joinLayer()->isEditable() ) + if ( !info->hasUpsertOnEdit() && mMode == QgsAttributeForm::AddFeatureMode ) + editable = false; + else if ( info && info->isEditable() && info->joinLayer()->isEditable() ) editable = fieldIsEditable( *( info->joinLayer() ), srcFieldIndex, mFeature.id() ); } else @@ -2039,3 +2052,47 @@ bool QgsAttributeForm::fieldIsEditable( const QgsVectorLayer &layer, int fieldIn return !layer.editFormConfig().readOnly( fieldIndex ) && ( ( layer.dataProvider() && layer.dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || FID_IS_NEW( fid ) ); } + +void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww ) +{ + // no icon by default + mIconMap[eww->widget()]->hide(); + + if ( !eww->widget()->isEnabled() && mLayer->isEditable() ) + { + if ( mLayer->fields().fieldOrigin( eww->fieldIdx() ) == QgsFields::OriginJoin ) + { + int srcFieldIndex; + const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( eww->fieldIdx(), mLayer->fields(), srcFieldIndex ); + + if ( !info ) + return; + + if ( !info->isEditable() ) + { + QString file = QStringLiteral( "/mIconJoinNotEditable.svg" ); + QString tooltip = tr( "Join settings do not allow editing" ); + reloadIcon( file, tooltip, mIconMap[eww->widget()] ); + } + else if ( mMode == QgsAttributeForm::AddFeatureMode && !info->hasUpsertOnEdit() ) + { + QString file = QStringLiteral( "mIconJoinHasNotUpsertOnEdit.svg" ); + QString tooltip = tr( "Join settings do not allow upsert on edit" ); + reloadIcon( file, tooltip, mIconMap[eww->widget()] ); + } + else if ( !info->joinLayer()->isEditable() ) + { + QString file = QStringLiteral( "/mIconJoinedLayerNotEditable.svg" ); + QString tooltip = tr( "Joined layer is not toggled editable" ); + reloadIcon( file, tooltip, mIconMap[eww->widget()] ); + } + } + } +} + +void QgsAttributeForm::reloadIcon( const QString &file, const QString &tooltip, QSvgWidget *sw ) +{ + sw->load( QgsApplication::iconPath( file ) ); + sw->setToolTip( tooltip ); + sw->show(); +} diff --git a/src/gui/qgsattributeform.h b/src/gui/qgsattributeform.h index ebd71bef0b5..62e45ec0b5c 100644 --- a/src/gui/qgsattributeform.h +++ b/src/gui/qgsattributeform.h @@ -22,6 +22,7 @@ #include "qgseditorwidgetwrapper.h" #include +#include #include #include #include "qgis_gui.h" @@ -373,6 +374,10 @@ class GUI_EXPORT QgsAttributeForm : public QWidget void registerContainerInformation( ContainerInformation *info ); + void updateIcon( QgsEditorWidgetWrapper *eww ); + + void reloadIcon( const QString &file, const QString &tooltip, QSvgWidget *sw ); + // Contains information about tabs and groupboxes, their visibility state visibility conditions QVector mContainerVisibilityInformation; QMap > mContainerInformationDependency; @@ -401,6 +406,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget //! Backlinks widgets to buddies. QMap mBuddyMap; + QMap mIconMap; friend class TestQgsDualView; friend class TestQgsAttributeForm; From 3a0ab2380e8cfa540e0f576a386e1a8069306983 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Wed, 26 Jul 2017 10:43:34 +0100 Subject: [PATCH 174/364] Add some tooltips --- src/app/qgsjoindialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/qgsjoindialog.cpp b/src/app/qgsjoindialog.cpp index 61f50d04956..abda87d9a6c 100644 --- a/src/app/qgsjoindialog.cpp +++ b/src/app/qgsjoindialog.cpp @@ -44,6 +44,10 @@ QgsJoinDialog::QgsJoinDialog( QgsVectorLayer *layer, QList alread mDynamicFormCheckBox->setToolTip( tr( "This option allows values of the joined fields to be automatically reloaded when the \"Target Field\" is changed" ) ); + mEditableJoinLayer->setToolTip( tr( "This option allows values of the joined layers to be editable if they're themselves editable" ) ); + mUpsertOnEditCheckBox->setToolTip( tr( "Automatically adds a matching row to the joined table, but if one already exists then update that matching row instead" ) ); + mDeleteCascadeCheckBox->setToolTip( tr( "Automatically delete the corresponding feature of the linked layer if one exists" ) ); + mJoinLayerComboBox->setFilters( QgsMapLayerProxyModel::VectorLayer ); mJoinLayerComboBox->setExceptedLayerList( alreadyJoinedLayers ); connect( mJoinLayerComboBox, &QgsMapLayerComboBox::layerChanged, mJoinFieldComboBox, &QgsFieldComboBox::setLayer ); From 9df519f13f34e8609e496cb221ab371ff8960c42 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 3 Aug 2017 08:16:31 +0200 Subject: [PATCH 175/364] Update vector layer properties ui list for join --- src/app/qgsvectorlayerproperties.cpp | 84 +++--- src/ui/qgsvectorlayerpropertiesbase.ui | 379 +++++++++++++++++++------ 2 files changed, 345 insertions(+), 118 deletions(-) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index bbdab2d6dd2..7b3a16a1e67 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -1218,6 +1218,7 @@ void QgsVectorLayerProperties::on_mJoinTreeWidget_itemDoubleClicked( QTreeWidget void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo &join, const int insertIndex ) { QTreeWidgetItem *joinItem = new QTreeWidgetItem(); + joinItem->setFlags( Qt::ItemIsEnabled ); QgsVectorLayer *joinLayer = join.joinLayer(); if ( !mLayer || !joinLayer ) @@ -1225,62 +1226,79 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo return; } - joinItem->setText( 0, joinLayer->name() ); + joinItem->setText( 0, "Join layer" ); + joinItem->setText( 1, joinLayer->name() ); + + QFont f = joinItem->font( 0 ); + f.setBold( true ); + joinItem->setFont( 0, f ); + joinItem->setFont( 1, f ); + joinItem->setData( 0, Qt::UserRole, join.joinLayerId() ); - joinItem->setText( 1, join.joinFieldName() ); - joinItem->setText( 2, join.targetFieldName() ); + QTreeWidgetItem *childJoinField = new QTreeWidgetItem(); + childJoinField->setText( 0, "Join field" ); + childJoinField->setText( 1, join.joinFieldName() ); + childJoinField->setFlags( Qt::ItemIsEnabled ); + joinItem->addChild( childJoinField ); + QTreeWidgetItem *childTargetField = new QTreeWidgetItem(); + childTargetField->setText( 0, "Target field" ); + childTargetField->setText( 1, join.targetFieldName() ); + joinItem->addChild( childTargetField ); + + QTreeWidgetItem *childMemCache = new QTreeWidgetItem(); + childMemCache->setText( 0, "Cache join layer in virtual memory" ); if ( join.isUsingMemoryCache() ) - { - joinItem->setText( 3, QChar( 0x2714 ) ); - } + childMemCache->setText( 1, QChar( 0x2714 ) ); + joinItem->addChild( childMemCache ); + QTreeWidgetItem *childDynForm = new QTreeWidgetItem(); + childDynForm->setText( 0, "Dynamic form" ); if ( join.isDynamicFormEnabled() ) - { - joinItem->setText( 4, QChar( 0x2714 ) ); - } + childDynForm->setText( 1, QChar( 0x2714 ) ); + joinItem->addChild( childDynForm ); + QTreeWidgetItem *childEditable = new QTreeWidgetItem(); + childEditable->setText( 0, "Editable join layer" ); if ( join.isEditable() ) - { - joinItem->setText( 5, QChar( 0x2714 ) ); - } + childEditable->setText( 1, QChar( 0x2714 ) ); + joinItem->addChild( childEditable ); + QTreeWidgetItem *childUpsert = new QTreeWidgetItem(); + childUpsert->setText( 0, "Upsert on edit" ); if ( join.hasUpsertOnEdit() ) - { - joinItem->setText( 6, QChar( 0x2714 ) ); - } + childUpsert->setText( 1, QChar( 0x2714 ) ); + joinItem->addChild( childUpsert ); + QTreeWidgetItem *childCascade = new QTreeWidgetItem(); + childCascade->setText( 0, "Delete cascade" ); if ( join.hasCascadedDelete() ) - { - joinItem->setText( 7, QChar( 0x2714 ) ); - } + childCascade->setText( 1, QChar( 0x2714 ) ); + joinItem->addChild( childCascade ); - joinItem->setText( 8, join.prefix() ); + QTreeWidgetItem *childPrefix = new QTreeWidgetItem(); + childPrefix->setText( 0, "Custom field name prefix" ); + childPrefix->setText( 1, join.prefix() ); + joinItem->addChild( childPrefix ); + QTreeWidgetItem *childFields = new QTreeWidgetItem(); + childFields->setText( 0, "Joined fields" ); const QStringList *list = join.joinFieldNamesSubset(); + QString fields; if ( list ) - { - joinItem->setText( 9, QStringLiteral( "%1" ).arg( list->count() ) ); - } + childFields->setText( 1, QStringLiteral( "%1" ).arg( list->count() ) ); else - { - joinItem->setText( 9, tr( "all" ) ); - } + childFields->setText( 1, tr( "all" ) ); + joinItem->addChild( childFields ); if ( insertIndex >= 0 ) - { mJoinTreeWidget->insertTopLevelItem( insertIndex, joinItem ); - } else - { mJoinTreeWidget->addTopLevelItem( joinItem ); - } - for ( int c = 0; c < 9; c++ ) - { - mJoinTreeWidget->resizeColumnToContents( c ); - } + mJoinTreeWidget->setCurrentItem( joinItem ); + mJoinTreeWidget->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); } QgsExpressionContext QgsVectorLayerProperties::createExpressionContext() const diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index 46d87d0c806..d16b1a27dd3 100755 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -46,7 +46,16 @@ QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0 @@ -273,7 +282,16 @@ QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0 @@ -289,7 +307,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -301,7 +328,16 @@ QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0 @@ -318,7 +354,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -334,12 +379,21 @@ 0 0 - 309 - 468 + 306 + 508 - + + 0 + + + 0 + + + 0 + + 0 @@ -347,7 +401,7 @@ Description - + vectormeta @@ -526,7 +580,7 @@ border-radius: 2px; Attribution - + vectormeta @@ -572,7 +626,7 @@ border-radius: 2px; MetadataUrl - + vectormeta @@ -751,7 +805,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -767,12 +830,21 @@ border-radius: 2px; 0 0 - 282 - 315 + 268 + 321 - + + 0 + + + 0 + + + 0 + + 0 @@ -790,7 +862,16 @@ border-radius: 2px; QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0 @@ -839,7 +920,7 @@ border-radius: 2px; false - + vectorgeneral @@ -854,7 +935,7 @@ border-radius: 2px;
    - + Qt::StrongFocus @@ -962,7 +1043,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -983,7 +1073,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1013,7 +1112,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1036,7 +1144,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1057,7 +1174,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1078,7 +1204,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1094,12 +1229,21 @@ border-radius: 2px; 0 0 - 634 - 324 + 632 + 276 - + + 0 + + + 0 + + + 0 + + 0 @@ -1112,7 +1256,7 @@ border-radius: 2px; - + Qt::StrongFocus @@ -1312,7 +1456,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1321,7 +1474,16 @@ border-radius: 2px; true - + + 0 + + + 0 + + + 0 + + 0 @@ -1332,7 +1494,7 @@ border-radius: 2px; - + Qt::StrongFocus @@ -1383,7 +1545,7 @@ border-radius: 2px; - + Qt::StrongFocus @@ -1402,7 +1564,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1423,7 +1594,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1450,7 +1630,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1467,66 +1656,41 @@ border-radius: 2px; 0 0 653 - 538 + 542 - + + 0 + + + 0 + + + 0 + + 0 + + true + + + QAbstractItemView::NoSelection + - 10 + 2 - Join layer + Setting - Join field - - - - - Target field - - - - - Memory cache - - - - - Dynamic form - - - - - Editable - - - - - Upsert On Edit - - - - - Delete cascade - - - - - Prefix - - - - - Joined fields + Value @@ -1589,7 +1753,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1612,7 +1785,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1635,7 +1817,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1654,7 +1845,16 @@ border-radius: 2px; - + + 0 + + + 0 + + + 0 + + 0 @@ -1729,7 +1929,16 @@ border-radius: 2px; QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0 From 94d7e75a7e27878eda880a5f98de15ffc8cce979 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 21 Aug 2017 05:14:32 +0100 Subject: [PATCH 176/364] Fix spell check --- python/core/qgsvectorlayerjoininfo.sip | 2 +- scripts/spell_check/.agignore | 3 +++ src/core/qgsvectorlayerjoininfo.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/python/core/qgsvectorlayerjoininfo.sip b/python/core/qgsvectorlayerjoininfo.sip index 9d3d38411ea..9dfab650625 100644 --- a/python/core/qgsvectorlayerjoininfo.sip +++ b/python/core/qgsvectorlayerjoininfo.sip @@ -113,7 +113,7 @@ Returns whether values from the joined layer should be cached in memory to speed void setEditable( bool enabled ); %Docstring - Sets whether the form of the target layer allows to edit joined fields. + Sets whether the form of the target layer allows editing joined fields. .. versionadded:: 3.0 %End diff --git a/scripts/spell_check/.agignore b/scripts/spell_check/.agignore index 7f98cc47cd0..22708baacac 100644 --- a/scripts/spell_check/.agignore +++ b/scripts/spell_check/.agignore @@ -39,6 +39,9 @@ images/svg/symbol/red-marker.svg images/themes/default/propertyicons/diagram.svg images/themes/default/propertyicons/general.svg images/themes/default/svgbase/hammer.svg +images/themes/default/mIconJoinHasNotUpsertOnEdit.svg +images/themes/default/mIconJoinNotEditable.svg +images/themes/default/mIconJoinedLayerNotEditable.svg LexerR.py python/plugins/MetaSearch/resources/connections-default.xml python/plugins/processing/algs/grass7/description/i.landsat.toar.txt diff --git a/src/core/qgsvectorlayerjoininfo.h b/src/core/qgsvectorlayerjoininfo.h index a2d867aba42..7b973da7c22 100644 --- a/src/core/qgsvectorlayerjoininfo.h +++ b/src/core/qgsvectorlayerjoininfo.h @@ -87,7 +87,7 @@ class CORE_EXPORT QgsVectorLayerJoinInfo */ bool isEditable() const { return mEditable; } - /** Sets whether the form of the target layer allows to edit joined fields. + /** Sets whether the form of the target layer allows editing joined fields. * \since QGIS 3.0 */ void setEditable( bool enabled ); From 3ba138bff99ed58749b2218e74356e7995363067 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 21 Aug 2017 15:46:39 +0100 Subject: [PATCH 177/364] Joined features are created on changeAttributeValue when upsertOnEdit is activated --- src/core/qgsvectorlayer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 5660c570a4f..a2f6f139a62 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2279,7 +2279,10 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV if ( joinFeature.isValid() ) return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue ); else - return false; + { + feature.setAttribute( field, newValue ); + return addFeaturesToJoinedLayers( QgsFeatureList() << feature ); + } } else return false; From 927dea3c18bf2f3ea7f87517d44bf8943f9e2cbe Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 21 Aug 2017 15:47:58 +0100 Subject: [PATCH 178/364] Add tests --- tests/src/gui/testqgsattributeform.cpp | 55 +++++++++++++++++++++----- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/tests/src/gui/testqgsattributeform.cpp b/tests/src/gui/testqgsattributeform.cpp index 882430e68dc..6a366d5234a 100644 --- a/tests/src/gui/testqgsattributeform.cpp +++ b/tests/src/gui/testqgsattributeform.cpp @@ -640,6 +640,13 @@ void TestQgsAttributeForm::testUpsertOnEdit() QString defC = QStringLiteral( "Point?field=id_c:integer&field=col0:integer" ); QgsVectorLayer *layerC = new QgsVectorLayer( defC, QStringLiteral( "layerC" ), QStringLiteral( "memory" ) ); + // add a feature in layer A + QgsFeature ft0A( layerA->fields() ); + ft0A.setAttribute( QStringLiteral( "id_a" ), 34 ); + layerA->startEditing(); + layerA->addFeature( ft0A ); + layerA->commitChanges(); + // join configuration QgsVectorLayerJoinInfo infoJoinAB; infoJoinAB.setTargetFieldName( "id_a" ); @@ -662,10 +669,10 @@ void TestQgsAttributeForm::testUpsertOnEdit() layerA->addJoin( infoJoinAC ); // add features for main layer - QgsFeature ftA( layerA->fields() ); - ftA.setAttribute( QStringLiteral( "id_a" ), 31 ); + QgsFeature ft1A( layerA->fields() ); + ft1A.setAttribute( QStringLiteral( "id_a" ), 31 ); layerA->startEditing(); - layerA->addFeature( ftA ); + layerA->addFeature( ft1A ); layerA->commitChanges(); // add features for joined layers @@ -689,14 +696,14 @@ void TestQgsAttributeForm::testUpsertOnEdit() layerC->startEditing(); // build a form with feature A - ftA = layerA->getFeature( 1 ); + ft1A = layerA->getFeature( 2 ); QgsAttributeForm form( layerA ); form.setMode( QgsAttributeForm::AddFeatureMode ); - form.setFeature( ftA ); + form.setFeature( ft1A ); // count features - QCOMPARE( ( int )layerA->featureCount(), 1 ); + QCOMPARE( ( int )layerA->featureCount(), 2 ); QCOMPARE( ( int )layerB->featureCount(), 1 ); QCOMPARE( ( int )layerC->featureCount(), 1 ); @@ -707,7 +714,7 @@ void TestQgsAttributeForm::testUpsertOnEdit() form.save(); // count features - QCOMPARE( ( int )layerA->featureCount(), 2 ); + QCOMPARE( ( int )layerA->featureCount(), 3 ); QCOMPARE( ( int )layerB->featureCount(), 2 ); QCOMPARE( ( int )layerC->featureCount(), 1 ); @@ -729,14 +736,14 @@ void TestQgsAttributeForm::testUpsertOnEdit() // create a target feature but update a joined feature QgsAttributeForm formUpdate( layerA ); formUpdate.setMode( QgsAttributeForm::AddFeatureMode ); - formUpdate.setFeature( ftA ); + formUpdate.setFeature( ft1A ); formUpdate.changeAttribute( "id_a", QVariant( 33 ) ); formUpdate.changeAttribute( "layerB_col0", QVariant( 3333 ) ); formUpdate.changeAttribute( "layerC_col0", QVariant( 323232 ) ); formUpdate.save(); // count features - QCOMPARE( ( int )layerA->featureCount(), 3 ); + QCOMPARE( ( int )layerA->featureCount(), 4 ); QCOMPARE( ( int )layerB->featureCount(), 2 ); QCOMPARE( ( int )layerC->featureCount(), 1 ); @@ -750,6 +757,36 @@ void TestQgsAttributeForm::testUpsertOnEdit() QCOMPARE( ft0B.attribute( "id_b" ), QVariant( 33 ) ); QCOMPARE( ft0B.attribute( "col0" ), QVariant( 3333 ) ); + // start editing + layerA->startEditing(); + layerB->startEditing(); + layerC->startEditing(); + + // build a form with feature A + ft0A = layerA->getFeature( 1 ); + + QgsAttributeForm formUpsert( layerA ); + formUpsert.setMode( QgsAttributeForm::SingleEditMode ); + formUpsert.setFeature( ft0A ); + + // count features + QCOMPARE( ( int )layerA->featureCount(), 4 ); + QCOMPARE( ( int )layerB->featureCount(), 2 ); + QCOMPARE( ( int )layerC->featureCount(), 1 ); + + // update feature which does not exist in joined layer + formUpsert.changeAttribute( "layerB_col0", QVariant( 77777 ) ); + formUpsert.save(); + + // count features + QCOMPARE( ( int )layerA->featureCount(), 4 ); + QCOMPARE( ( int )layerB->featureCount(), 3 ); + QCOMPARE( ( int )layerC->featureCount(), 1 ); + + // get new feature + ft0B = layerB->getFeature( 3 ); + QCOMPARE( ft0B.attribute( "id_b" ), QVariant() ); + // clean delete layerA; delete layerB; From 11c62ca2050c0d6003a512e7b11a2994a531f370 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 22 Aug 2017 11:07:26 +0100 Subject: [PATCH 179/364] A joined feature is added only when one of its field is not null --- src/core/qgsvectorlayer.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index a2f6f139a62..7a196b4607f 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2718,7 +2718,24 @@ bool QgsVectorLayer::addFeaturesToJoinedLayers( QgsFeatureList &features, Flags joinLayer->updateFeature( existingFeature ); } else - joinFeatures << joinFeature; + { + // joined feature is added only if one of its field is not null + bool notNullFields = false; + Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + { + if ( field.name() == info.joinFieldName() ) + continue; + + if ( !joinFeature.attribute( field.name() ).isNull() ) + { + notNullFields = true; + break; + } + } + + if ( notNullFields ) + joinFeatures << joinFeature; + } } joinLayer->addFeatures( joinFeatures ); From 64aa40510d7fefb5c52925525bd9e51449c5266b Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 22 Aug 2017 11:07:37 +0100 Subject: [PATCH 180/364] Update tests --- tests/src/gui/testqgsattributeform.cpp | 177 +++++++++++++++++-------- 1 file changed, 121 insertions(+), 56 deletions(-) diff --git a/tests/src/gui/testqgsattributeform.cpp b/tests/src/gui/testqgsattributeform.cpp index 6a366d5234a..4d9e3db5fb7 100644 --- a/tests/src/gui/testqgsattributeform.cpp +++ b/tests/src/gui/testqgsattributeform.cpp @@ -640,13 +640,6 @@ void TestQgsAttributeForm::testUpsertOnEdit() QString defC = QStringLiteral( "Point?field=id_c:integer&field=col0:integer" ); QgsVectorLayer *layerC = new QgsVectorLayer( defC, QStringLiteral( "layerC" ), QStringLiteral( "memory" ) ); - // add a feature in layer A - QgsFeature ft0A( layerA->fields() ); - ft0A.setAttribute( QStringLiteral( "id_a" ), 34 ); - layerA->startEditing(); - layerA->addFeature( ft0A ); - layerA->commitChanges(); - // join configuration QgsVectorLayerJoinInfo infoJoinAB; infoJoinAB.setTargetFieldName( "id_a" ); @@ -669,10 +662,10 @@ void TestQgsAttributeForm::testUpsertOnEdit() layerA->addJoin( infoJoinAC ); // add features for main layer - QgsFeature ft1A( layerA->fields() ); - ft1A.setAttribute( QStringLiteral( "id_a" ), 31 ); + QgsFeature ft0A( layerA->fields() ); + ft0A.setAttribute( QStringLiteral( "id_a" ), 31 ); layerA->startEditing(); - layerA->addFeature( ft1A ); + layerA->addFeature( ft0A ); layerA->commitChanges(); // add features for joined layers @@ -690,102 +683,174 @@ void TestQgsAttributeForm::testUpsertOnEdit() layerC->addFeature( ft0C ); layerC->commitChanges(); + // get committed feature from layerA + QgsFeature feature; + QString filter = QgsExpression::createFieldEqualityExpression( "id_a", 31 ); + + QgsFeatureRequest request; + request.setFilterExpression( filter ); + request.setLimit( 1 ); + layerA->getFeatures( request ).nextFeature( ft0A ); + // start editing layers layerA->startEditing(); layerB->startEditing(); layerC->startEditing(); // build a form with feature A - ft1A = layerA->getFeature( 2 ); - QgsAttributeForm form( layerA ); form.setMode( QgsAttributeForm::AddFeatureMode ); - form.setFeature( ft1A ); + form.setFeature( ft0A ); + + // count features + QCOMPARE( ( int )layerA->featureCount(), 1 ); + QCOMPARE( ( int )layerB->featureCount(), 1 ); + QCOMPARE( ( int )layerC->featureCount(), 1 ); + + // add a new feature with null joined fields. Joined feature should not be + // added + form.changeAttribute( "id_a", QVariant( 32 ) ); + form.changeAttribute( "layerB_col0", QVariant() ); + form.changeAttribute( "layerC_col0", QVariant() ); + form.save(); + + // commit + layerA->commitChanges(); + layerB->commitChanges(); + layerC->commitChanges(); // count features QCOMPARE( ( int )layerA->featureCount(), 2 ); QCOMPARE( ( int )layerB->featureCount(), 1 ); QCOMPARE( ( int )layerC->featureCount(), 1 ); - // add a new feature - form.changeAttribute( "id_a", QVariant( 32 ) ); - form.changeAttribute( "layerB_col0", QVariant( 3232 ) ); - form.changeAttribute( "layerC_col0", QVariant( 323232 ) ); - form.save(); + // start editing layers + layerA->startEditing(); + layerB->startEditing(); + layerC->startEditing(); + + // add a new feature with not null joined fields. Joined feature should be + // added + QgsAttributeForm form1( layerA ); + form1.setMode( QgsAttributeForm::AddFeatureMode ); + form1.setFeature( ft0A ); + + form1.changeAttribute( "id_a", QVariant( 34 ) ); + form1.changeAttribute( "layerB_col0", QVariant( 3434 ) ); + form1.changeAttribute( "layerC_col0", QVariant( 343434 ) ); + form1.save(); + + // commit + layerA->commitChanges(); + layerB->commitChanges(); + layerC->commitChanges(); // count features QCOMPARE( ( int )layerA->featureCount(), 3 ); QCOMPARE( ( int )layerB->featureCount(), 2 ); QCOMPARE( ( int )layerC->featureCount(), 1 ); - // commit changes - layerA->commitChanges(); - layerB->commitChanges(); - layerC->commitChanges(); + // check joined feature value + filter = QgsExpression::createFieldEqualityExpression( "id_a", 34 ); - // check attributes - ft0B = layerB->getFeature( 2 ); - QCOMPARE( ft0B.attribute( "id_b" ), QVariant( 32 ) ); - QCOMPARE( ft0B.attribute( "col0" ), QVariant( 3232 ) ); + request.setFilterExpression( filter ); + request.setLimit( 1 ); + layerA->getFeatures( request ).nextFeature( feature ); + + QCOMPARE( feature.attribute( "layerB_col0" ), QVariant( 3434 ) ); // start editing layers layerA->startEditing(); layerB->startEditing(); layerC->startEditing(); - // create a target feature but update a joined feature - QgsAttributeForm formUpdate( layerA ); - formUpdate.setMode( QgsAttributeForm::AddFeatureMode ); - formUpdate.setFeature( ft1A ); - formUpdate.changeAttribute( "id_a", QVariant( 33 ) ); - formUpdate.changeAttribute( "layerB_col0", QVariant( 3333 ) ); - formUpdate.changeAttribute( "layerC_col0", QVariant( 323232 ) ); - formUpdate.save(); + // create a target feature but update a joined feature. A new feature should + // be added in layerA and values in layerB should be updated + QgsAttributeForm form2( layerA ); + form2.setMode( QgsAttributeForm::AddFeatureMode ); + form2.setFeature( ft0A ); + form2.changeAttribute( "id_a", QVariant( 33 ) ); + form2.changeAttribute( "layerB_col0", QVariant( 3333 ) ); + form2.changeAttribute( "layerC_col0", QVariant( 323232 ) ); + form2.save(); - // count features - QCOMPARE( ( int )layerA->featureCount(), 4 ); - QCOMPARE( ( int )layerB->featureCount(), 2 ); - QCOMPARE( ( int )layerC->featureCount(), 1 ); - - // commit changes + // commit layerA->commitChanges(); layerB->commitChanges(); layerC->commitChanges(); - // check attributes - ft0B = layerB->getFeature( 1 ); - QCOMPARE( ft0B.attribute( "id_b" ), QVariant( 33 ) ); - QCOMPARE( ft0B.attribute( "col0" ), QVariant( 3333 ) ); + // count features + QCOMPARE( ( int )layerA->featureCount(), 4 ); + QCOMPARE( ( int )layerB->featureCount(), 2 ); + QCOMPARE( ( int )layerC->featureCount(), 1 ); - // start editing + // check joined feature value + filter = QgsExpression::createFieldEqualityExpression( "id_a", 33 ); + + request.setFilterExpression( filter ); + request.setLimit( 1 ); + layerA->getFeatures( request ).nextFeature( feature ); + + QCOMPARE( feature.attribute( "layerB_col0" ), QVariant( 3333 ) ); + + // start editing layers layerA->startEditing(); layerB->startEditing(); layerC->startEditing(); - // build a form with feature A - ft0A = layerA->getFeature( 1 ); + // update feature which does not exist in joined layer but with null joined + // fields. A new feature should NOT be added in joined layer + QgsAttributeForm form3( layerA ); + form3.setMode( QgsAttributeForm::SingleEditMode ); + form3.setFeature( ft0A ); + form3.changeAttribute( "id_a", QVariant( 31 ) ); + form3.changeAttribute( "layerB_col0", QVariant() ); + form3.changeAttribute( "layerC_col0", QVariant() ); + form3.save(); - QgsAttributeForm formUpsert( layerA ); - formUpsert.setMode( QgsAttributeForm::SingleEditMode ); - formUpsert.setFeature( ft0A ); + // commit + layerA->commitChanges(); + layerB->commitChanges(); + layerC->commitChanges(); // count features QCOMPARE( ( int )layerA->featureCount(), 4 ); QCOMPARE( ( int )layerB->featureCount(), 2 ); QCOMPARE( ( int )layerC->featureCount(), 1 ); - // update feature which does not exist in joined layer - formUpsert.changeAttribute( "layerB_col0", QVariant( 77777 ) ); - formUpsert.save(); + // start editing layers + layerA->startEditing(); + layerB->startEditing(); + layerC->startEditing(); + + // update feature which does not exist in joined layer with NOT null joined + // fields. A new feature should be added in joined layer + QgsAttributeForm form4( layerA ); + form4.setMode( QgsAttributeForm::SingleEditMode ); + form4.setFeature( ft0A ); + form4.changeAttribute( "id_a", QVariant( 31 ) ); + form4.changeAttribute( "layerB_col0", QVariant( 1111 ) ); + form4.changeAttribute( "layerC_col0", QVariant( 3131 ) ); + form4.save(); + + // commit + layerA->commitChanges(); + layerB->commitChanges(); + layerC->commitChanges(); // count features QCOMPARE( ( int )layerA->featureCount(), 4 ); QCOMPARE( ( int )layerB->featureCount(), 3 ); QCOMPARE( ( int )layerC->featureCount(), 1 ); - // get new feature - ft0B = layerB->getFeature( 3 ); - QCOMPARE( ft0B.attribute( "id_b" ), QVariant() ); + // check joined feature value + filter = QgsExpression::createFieldEqualityExpression( "id_a", 31 ); + + request.setFilterExpression( filter ); + request.setLimit( 1 ); + layerA->getFeatures( request ).nextFeature( feature ); + + QCOMPARE( feature.attribute( "layerB_col0" ), QVariant( 1111 ) ); // clean delete layerA; From 500348eec38462a249a18bce7432066032f34b3a Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Thu, 24 Aug 2017 13:53:15 +0100 Subject: [PATCH 181/364] Add some const --- src/app/qgsvectorlayerproperties.cpp | 2 +- src/core/qgsvectorlayer.cpp | 4 ++-- src/core/qgsvectorlayercache.cpp | 2 +- src/gui/qgsattributeform.cpp | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 7b3a16a1e67..8ea09cc0cd1 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -1435,4 +1435,4 @@ void QgsVectorLayerProperties::updateFieldsPropertiesDialog() void QgsVectorLayerProperties::showHelp() { QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html" ) ); -} \ No newline at end of file +} diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 7a196b4607f..e8f1002455d 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2274,7 +2274,7 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV if ( !feature.isValid() ) return false; - QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( info, feature ); + const QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( info, feature ); if ( joinFeature.isValid() ) return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue ); @@ -2486,7 +2486,7 @@ bool QgsVectorLayer::deleteFeaturesFromJoinedLayers( QgsFeatureIds fids ) { if ( info.isEditable() && info.hasCascadedDelete() ) { - QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( &info, getFeature( fid ) ); + const QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( &info, getFeature( fid ) ); if ( joinFeature.isValid() ) info.joinLayer()->deleteFeature( joinFeature.id() ); } diff --git a/src/core/qgsvectorlayercache.cpp b/src/core/qgsvectorlayercache.cpp index 21d5d571049..1e52fb976e2 100644 --- a/src/core/qgsvectorlayercache.cpp +++ b/src/core/qgsvectorlayercache.cpp @@ -244,7 +244,7 @@ void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int fie { if ( joinLayer == info.joinLayer() ) { - QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) ); + const QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) ); const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) ); const int fieldIndex = mLayer->fields().indexFromName( fieldName ); diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index 46da91e79fc..bb19dbea3dc 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -2070,20 +2070,20 @@ void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww ) if ( !info->isEditable() ) { - QString file = QStringLiteral( "/mIconJoinNotEditable.svg" ); - QString tooltip = tr( "Join settings do not allow editing" ); + const QString file = QStringLiteral( "/mIconJoinNotEditable.svg" ); + const QString tooltip = tr( "Join settings do not allow editing" ); reloadIcon( file, tooltip, mIconMap[eww->widget()] ); } else if ( mMode == QgsAttributeForm::AddFeatureMode && !info->hasUpsertOnEdit() ) { - QString file = QStringLiteral( "mIconJoinHasNotUpsertOnEdit.svg" ); - QString tooltip = tr( "Join settings do not allow upsert on edit" ); + const QString file = QStringLiteral( "mIconJoinHasNotUpsertOnEdit.svg" ); + const QString tooltip = tr( "Join settings do not allow upsert on edit" ); reloadIcon( file, tooltip, mIconMap[eww->widget()] ); } else if ( !info->joinLayer()->isEditable() ) { - QString file = QStringLiteral( "/mIconJoinedLayerNotEditable.svg" ); - QString tooltip = tr( "Joined layer is not toggled editable" ); + const QString file = QStringLiteral( "/mIconJoinedLayerNotEditable.svg" ); + const QString tooltip = tr( "Joined layer is not toggled editable" ); reloadIcon( file, tooltip, mIconMap[eww->widget()] ); } } From b6e42c7d2b4b8625c5853ee4f73f82016884f6e6 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 25 Aug 2017 01:21:56 +0100 Subject: [PATCH 182/364] Move join intelligence in QgsVectorlayerJoinBuffer --- python/core/qgsvectorlayerjoinbuffer.sip | 76 +++++++++++++ src/core/qgsvectorlayer.cpp | 122 +-------------------- src/core/qgsvectorlayer.h | 3 - src/core/qgsvectorlayerjoinbuffer.cpp | 134 +++++++++++++++++++++++ src/core/qgsvectorlayerjoinbuffer.h | 71 ++++++++++++ 5 files changed, 286 insertions(+), 120 deletions(-) diff --git a/python/core/qgsvectorlayerjoinbuffer.sip b/python/core/qgsvectorlayerjoinbuffer.sip index bdc45784c34..0484c3eab3e 100644 --- a/python/core/qgsvectorlayerjoinbuffer.sip +++ b/python/core/qgsvectorlayerjoinbuffer.sip @@ -132,6 +132,82 @@ Quick way to test if there is any join at all :rtype: QgsVectorLayerJoinBuffer %End + bool addFeature( const QgsFeature &feature ) const; +%Docstring + Adds a feature in joined layers. The feature given in parameter is the + one added in target layer. If a corresponding joined feature yet exists + in a joined layer, then this feature is just updated. Note that if a + corresponding joined feature has only empty fields, then it's not + created nor added. + + \param feature The feature added in the target layer. + + :return: false if an error happened, true otherwise + +.. versionadded:: 3.0 + :rtype: bool +%End + + bool addFeatures( const QgsFeatureList &features ) const; +%Docstring + Adds a list of features in joined layers. Features given in parameter + are those added in target layer. If a corresponding joined feature yet + exists in a joined layer, then this feature is just updated. Note that + if a corresponding joined feature has only empty fields, then it's not + created nor added. + + \param features The list of features added in the target layer + + :return: false if an error happened, true otherwise + +.. versionadded:: 3.0 + :rtype: bool +%End + + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) const; +%Docstring + Changes attribute value in joined layers. The feature id given in + parameter is the one added in target layer. If the corresponding joined + feature does not exist in a joined layer, then it's automatically + created if its fields are not empty. + + \param fid The feature id + \param field The field to update + \param newValue The new value of the attribute + \param oldValue The old value of the attribute + + :return: false if an error happened, true otherwise + +.. versionadded:: 3.0 + :rtype: bool +%End + + bool deleteFeature( QgsFeatureId fid ) const; +%Docstring + Deletes a feature from joined layers. The feature id given in + parameter is the one coming from the target layer. + + \param fid The feature id from the target layer to delete + + :return: false if an error happened, true otherwise + +.. versionadded:: 3.0 + :rtype: bool +%End + + bool deleteFeatures( const QgsFeatureIds &fids ) const; +%Docstring + Deletes a list of features from joined layers. Feature ids given + in aprameter are those coming from the target layer. + + \param fids Feature ids from the target layer to delete + + :return: false if an error happened, true otherwise + +.. versionadded:: 3.0 + :rtype: bool +%End + signals: void joinedFieldsChanged(); %Docstring diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index e8f1002455d..b105ae6bba3 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -951,7 +951,7 @@ bool QgsVectorLayer::addFeature( QgsFeature &feature, Flags ) updateExtents(); if ( mJoinBuffer->containsJoins() ) - success = addFeaturesToJoinedLayers( QgsFeatureList() << feature ); + success = mJoinBuffer->addFeature( feature ); } return success; @@ -2265,27 +2265,7 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV { if ( fields().fieldOrigin( field ) == QgsFields::OriginJoin ) { - int srcFieldIndex; - const QgsVectorLayerJoinInfo *info = mJoinBuffer->joinForFieldIndex( field, fields(), srcFieldIndex ); - if ( info && info->joinLayer() && info->isEditable() ) - { - QgsFeature feature = getFeature( fid ); - - if ( !feature.isValid() ) - return false; - - const QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( info, feature ); - - if ( joinFeature.isValid() ) - return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue ); - else - { - feature.setAttribute( field, newValue ); - return addFeaturesToJoinedLayers( QgsFeatureList() << feature ); - } - } - else - return false; + return mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue ); } else { @@ -2442,7 +2422,7 @@ bool QgsVectorLayer::deleteFeature( QgsFeatureId fid ) return false; if ( mJoinBuffer->containsJoins() ) - deleteFeaturesFromJoinedLayers( QgsFeatureIds() << fid ); + mJoinBuffer->deleteFeature( fid ); bool res = mEditBuffer->deleteFeature( fid ); if ( res ) @@ -2463,7 +2443,7 @@ bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds &fids ) } if ( mJoinBuffer->containsJoins() ) - deleteFeaturesFromJoinedLayers( fids ); + mJoinBuffer->deleteFeatures( fids ); bool res = mEditBuffer->deleteFeatures( fids ); @@ -2476,26 +2456,6 @@ bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds &fids ) return res; } -bool QgsVectorLayer::deleteFeaturesFromJoinedLayers( QgsFeatureIds fids ) -{ - bool rc = false; - - Q_FOREACH ( const QgsFeatureId &fid, fids ) - { - Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() ) - { - if ( info.isEditable() && info.hasCascadedDelete() ) - { - const QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( &info, getFeature( fid ) ); - if ( joinFeature.isValid() ) - info.joinLayer()->deleteFeature( joinFeature.id() ); - } - } - } - - return rc; -} - QgsAttributeList QgsVectorLayer::pkAttributeList() const { QgsAttributeList pkAttributesList; @@ -2668,83 +2628,11 @@ bool QgsVectorLayer::addFeatures( QgsFeatureList &features, Flags ) updateExtents(); if ( res && mJoinBuffer->containsJoins() ) - res = addFeaturesToJoinedLayers( features ); + res = mJoinBuffer->addFeatures( features ); return res; } -bool QgsVectorLayer::addFeaturesToJoinedLayers( QgsFeatureList &features, Flags ) -{ - // try to add/update a feature in each joined layer - Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() ) - { - QgsVectorLayer *joinLayer = info.joinLayer(); - - if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() ) - { - QgsFeatureList joinFeatures; - - Q_FOREACH ( const QgsFeature &feature, features ) - { - const QgsFeature joinFeature = info.extractJoinedFeature( feature ); - - // we don't want to add a new feature in joined layer when the id - // column value yet exist, we just want to update the existing one - const QVariant idFieldValue = feature.attribute( info.targetFieldName() ); - const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() ); - - QgsFeatureRequest request; - request.setFilterExpression( filter ); - request.setLimit( 1 ); - - QgsFeatureIterator it = info.joinLayer()->getFeatures( request ); - QgsFeature existingFeature; - it.nextFeature( existingFeature ); - - if ( existingFeature.isValid() ) - { - const QStringList *subsetFields = info.joinFieldNamesSubset(); - if ( subsetFields ) - { - Q_FOREACH ( const QString &field, *subsetFields ) - existingFeature.setAttribute( field, joinFeature.attribute( field ) ); - } - else - { - Q_FOREACH ( const QgsField &field, joinFeature.fields() ) - existingFeature.setAttribute( field.name(), joinFeature.attribute( field.name() ) ); - } - - joinLayer->updateFeature( existingFeature ); - } - else - { - // joined feature is added only if one of its field is not null - bool notNullFields = false; - Q_FOREACH ( const QgsField &field, joinFeature.fields() ) - { - if ( field.name() == info.joinFieldName() ) - continue; - - if ( !joinFeature.attribute( field.name() ).isNull() ) - { - notNullFields = true; - break; - } - } - - if ( notNullFields ) - joinFeatures << joinFeature; - } - } - - joinLayer->addFeatures( joinFeatures ); - } - } - - return true; -} - void QgsVectorLayer::setCoordinateSystem() { QgsDebugMsg( "----- Computing Coordinate System" ); diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 28fdc70b64d..69d1488bcd4 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1932,9 +1932,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte //! Read simple labeling from layer's custom properties (QGIS 2.x projects) QgsAbstractVectorLayerLabeling *readLabelingFromCustomProperties(); - bool addFeaturesToJoinedLayers( QgsFeatureList &features, Flags flags = 0 ); - bool deleteFeaturesFromJoinedLayers( QgsFeatureIds fids ); - #ifdef SIP_RUN QgsVectorLayer( const QgsVectorLayer &rhs ); #endif diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index 86ed5c15444..1f1d8134872 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -517,3 +517,137 @@ void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl ) connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection ); connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection ); } + +bool QgsVectorLayerJoinBuffer::addFeature( const QgsFeature &feature ) const +{ + return addFeatures( QgsFeatureList() << feature ); +} + +bool QgsVectorLayerJoinBuffer::addFeatures( const QgsFeatureList &features ) const +{ + if ( !containsJoins() ) + return false; + + // try to add/update a feature in each joined layer + Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() ) + { + QgsVectorLayer *joinLayer = info.joinLayer(); + + if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() ) + { + QgsFeatureList joinFeatures; + + Q_FOREACH ( const QgsFeature &feature, features ) + { + const QgsFeature joinFeature = info.extractJoinedFeature( feature ); + + // we don't want to add a new feature in joined layer when the id + // column value yet exist, we just want to update the existing one + const QVariant idFieldValue = feature.attribute( info.targetFieldName() ); + const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() ); + + QgsFeatureRequest request; + request.setFilterExpression( filter ); + request.setLimit( 1 ); + + QgsFeatureIterator it = info.joinLayer()->getFeatures( request ); + QgsFeature existingFeature; + it.nextFeature( existingFeature ); + + if ( existingFeature.isValid() ) + { + const QStringList *subsetFields = info.joinFieldNamesSubset(); + if ( subsetFields ) + { + Q_FOREACH ( const QString &field, *subsetFields ) + existingFeature.setAttribute( field, joinFeature.attribute( field ) ); + } + else + { + Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + existingFeature.setAttribute( field.name(), joinFeature.attribute( field.name() ) ); + } + + joinLayer->updateFeature( existingFeature ); + } + else + { + // joined feature is added only if one of its field is not null + bool notNullFields = false; + Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + { + if ( field.name() == info.joinFieldName() ) + continue; + + if ( !joinFeature.attribute( field.name() ).isNull() ) + { + notNullFields = true; + break; + } + } + + if ( notNullFields ) + joinFeatures << joinFeature; + } + } + + joinLayer->addFeatures( joinFeatures ); + } + } + + return true; +} + +bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) const +{ + if ( mLayer->fields().fieldOrigin( field ) != QgsFields::OriginJoin ) + return false; + + int srcFieldIndex; + const QgsVectorLayerJoinInfo *info = joinForFieldIndex( field, mLayer->fields(), srcFieldIndex ); + if ( info && info->joinLayer() && info->isEditable() ) + { + QgsFeature feature = mLayer->getFeature( fid ); + + if ( !feature.isValid() ) + return false; + + const QgsFeature joinFeature = joinedFeatureOf( info, feature ); + + if ( joinFeature.isValid() ) + return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue ); + else + { + feature.setAttribute( field, newValue ); + return addFeatures( QgsFeatureList() << feature ); + } + } + else + return false; +} + +bool QgsVectorLayerJoinBuffer::deleteFeature( QgsFeatureId fid ) const +{ + return deleteFeatures( QgsFeatureIds() << fid ); +} + +bool QgsVectorLayerJoinBuffer::deleteFeatures( const QgsFeatureIds &fids ) const +{ + if ( !containsJoins() ) + return false; + + Q_FOREACH ( const QgsFeatureId &fid, fids ) + { + Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() ) + { + if ( info.isEditable() && info.hasCascadedDelete() ) + { + const QgsFeature joinFeature = joinedFeatureOf( &info, mLayer->getFeature( fid ) ); + if ( joinFeature.isValid() ) + info.joinLayer()->deleteFeature( joinFeature.id() ); + } + } + } + + return true; +} diff --git a/src/core/qgsvectorlayerjoinbuffer.h b/src/core/qgsvectorlayerjoinbuffer.h index 02ed58194a7..e38dcefe704 100644 --- a/src/core/qgsvectorlayerjoinbuffer.h +++ b/src/core/qgsvectorlayerjoinbuffer.h @@ -109,6 +109,77 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject //! \since QGIS 2.6 QgsVectorLayerJoinBuffer *clone() const SIP_FACTORY; + /** + * Adds a feature in joined layers. The feature given in parameter is the + * one added in target layer. If a corresponding joined feature yet exists + * in a joined layer, then this feature is just updated. Note that if a + * corresponding joined feature has only empty fields, then it's not + * created nor added. + * + * \param feature The feature added in the target layer. + * + * \returns false if an error happened, true otherwise + * + * \since QGIS 3.0 + */ + bool addFeature( const QgsFeature &feature ) const; + + /** + * Adds a list of features in joined layers. Features given in parameter + * are those added in target layer. If a corresponding joined feature yet + * exists in a joined layer, then this feature is just updated. Note that + * if a corresponding joined feature has only empty fields, then it's not + * created nor added. + * + * \param features The list of features added in the target layer + * + * \returns false if an error happened, true otherwise + * + * \since QGIS 3.0 + */ + bool addFeatures( const QgsFeatureList &features ) const; + + /** + * Changes attribute value in joined layers. The feature id given in + * parameter is the one added in target layer. If the corresponding joined + * feature does not exist in a joined layer, then it's automatically + * created if its fields are not empty. + * + * \param fid The feature id + * \param field The field to update + * \param newValue The new value of the attribute + * \param oldValue The old value of the attribute + * + * \returns false if an error happened, true otherwise + * + * \since QGIS 3.0 + */ + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) const; + + /** + * Deletes a feature from joined layers. The feature id given in + * parameter is the one coming from the target layer. + * + * \param fid The feature id from the target layer to delete + * + * \returns false if an error happened, true otherwise + * + * \since QGIS 3.0 + */ + bool deleteFeature( QgsFeatureId fid ) const; + + /** + * Deletes a list of features from joined layers. Feature ids given + * in aprameter are those coming from the target layer. + * + * \param fids Feature ids from the target layer to delete + * + * \returns false if an error happened, true otherwise + * + * \since QGIS 3.0 + */ + bool deleteFeatures( const QgsFeatureIds &fids ) const; + signals: //! Emitted whenever the list of joined fields changes (e.g. added join or joined layer's fields change) //! \since QGIS 2.6 From f27764d2bae0d59761da99c96bee381d88bf72e3 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 25 Aug 2017 07:43:55 +0100 Subject: [PATCH 183/364] Replace if by switch --- src/core/qgsvectorlayer.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index b105ae6bba3..80ed6d6af9b 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2263,17 +2263,26 @@ bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ) bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) { - if ( fields().fieldOrigin( field ) == QgsFields::OriginJoin ) + switch ( fields().fieldOrigin( field ) ) { - return mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue ); - } - else - { - if ( !mEditBuffer || !mDataProvider ) + case QgsFields::OriginJoin: + return mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue ); + + case QgsFields::OriginProvider: + case QgsFields::OriginEdit: + case QgsFields::OriginExpression: + { + if ( !mEditBuffer || !mDataProvider ) + return false; + else + return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); + } + + case QgsFields::OriginUnknown: return false; - else - return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); } + + return false; } bool QgsVectorLayer::addAttribute( const QgsField &field ) From 1fb88a778c5a72450fa14eb7cd0d576e9cf91dce Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 25 Aug 2017 07:56:48 +0100 Subject: [PATCH 184/364] QgsVectorLayerJoinBuffer inherits from QgsFeatureSink --- python/core/qgsvectorlayerjoinbuffer.sip | 22 ++++------------------ src/core/qgsvectorlayerjoinbuffer.cpp | 10 ++-------- src/core/qgsvectorlayerjoinbuffer.h | 22 ++++------------------ 3 files changed, 10 insertions(+), 44 deletions(-) diff --git a/python/core/qgsvectorlayerjoinbuffer.sip b/python/core/qgsvectorlayerjoinbuffer.sip index 0484c3eab3e..cd182d3f616 100644 --- a/python/core/qgsvectorlayerjoinbuffer.sip +++ b/python/core/qgsvectorlayerjoinbuffer.sip @@ -14,7 +14,7 @@ typedef QList< QgsVectorLayerJoinInfo > QgsVectorJoinList; -class QgsVectorLayerJoinBuffer : QObject +class QgsVectorLayerJoinBuffer : QObject, QgsFeatureSink { %Docstring Manages joined fields for a vector layer* @@ -132,23 +132,8 @@ Quick way to test if there is any join at all :rtype: QgsVectorLayerJoinBuffer %End - bool addFeature( const QgsFeature &feature ) const; -%Docstring - Adds a feature in joined layers. The feature given in parameter is the - one added in target layer. If a corresponding joined feature yet exists - in a joined layer, then this feature is just updated. Note that if a - corresponding joined feature has only empty fields, then it's not - created nor added. + virtual bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = 0 ); - \param feature The feature added in the target layer. - - :return: false if an error happened, true otherwise - -.. versionadded:: 3.0 - :rtype: bool -%End - - bool addFeatures( const QgsFeatureList &features ) const; %Docstring Adds a list of features in joined layers. Features given in parameter are those added in target layer. If a corresponding joined feature yet @@ -157,6 +142,7 @@ Quick way to test if there is any join at all created nor added. \param features The list of features added in the target layer + \param flags Unused parameter :return: false if an error happened, true otherwise @@ -164,7 +150,7 @@ Quick way to test if there is any join at all :rtype: bool %End - bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) const; + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ); %Docstring Changes attribute value in joined layers. The feature id given in parameter is the one added in target layer. If the corresponding joined diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index 1f1d8134872..cdc566d5c71 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -135,7 +135,6 @@ void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorLayerJoinInfo &joinInfo QgsFeatureRequest request; request.setFlags( QgsFeatureRequest::NoGeometry ); - // maybe user requested just a subset of layer's attributes // so we do not have to cache everything bool hasSubset = joinInfo.joinFieldNamesSubset(); @@ -518,12 +517,7 @@ void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl ) connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection ); } -bool QgsVectorLayerJoinBuffer::addFeature( const QgsFeature &feature ) const -{ - return addFeatures( QgsFeatureList() << feature ); -} - -bool QgsVectorLayerJoinBuffer::addFeatures( const QgsFeatureList &features ) const +bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags ) { if ( !containsJoins() ) return false; @@ -598,7 +592,7 @@ bool QgsVectorLayerJoinBuffer::addFeatures( const QgsFeatureList &features ) con return true; } -bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) const +bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) { if ( mLayer->fields().fieldOrigin( field ) != QgsFields::OriginJoin ) return false; diff --git a/src/core/qgsvectorlayerjoinbuffer.h b/src/core/qgsvectorlayerjoinbuffer.h index e38dcefe704..ac67767a55e 100644 --- a/src/core/qgsvectorlayerjoinbuffer.h +++ b/src/core/qgsvectorlayerjoinbuffer.h @@ -31,7 +31,7 @@ typedef QList< QgsVectorLayerJoinInfo > QgsVectorJoinList; /** \ingroup core * Manages joined fields for a vector layer*/ -class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject +class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject, public QgsFeatureSink { Q_OBJECT public: @@ -109,21 +109,6 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject //! \since QGIS 2.6 QgsVectorLayerJoinBuffer *clone() const SIP_FACTORY; - /** - * Adds a feature in joined layers. The feature given in parameter is the - * one added in target layer. If a corresponding joined feature yet exists - * in a joined layer, then this feature is just updated. Note that if a - * corresponding joined feature has only empty fields, then it's not - * created nor added. - * - * \param feature The feature added in the target layer. - * - * \returns false if an error happened, true otherwise - * - * \since QGIS 3.0 - */ - bool addFeature( const QgsFeature &feature ) const; - /** * Adds a list of features in joined layers. Features given in parameter * are those added in target layer. If a corresponding joined feature yet @@ -132,12 +117,13 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject * created nor added. * * \param features The list of features added in the target layer + * \param flags Unused parameter * * \returns false if an error happened, true otherwise * * \since QGIS 3.0 */ - bool addFeatures( const QgsFeatureList &features ) const; + bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = 0 ) override; /** * Changes attribute value in joined layers. The feature id given in @@ -154,7 +140,7 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject * * \since QGIS 3.0 */ - bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) const; + bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ); /** * Deletes a feature from joined layers. The feature id given in From cbeb8b27e54caeaac24549ae012470db6d6bb6e3 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 25 Aug 2017 08:49:38 +0100 Subject: [PATCH 185/364] Improve performance --- src/core/qgsvectorlayerjoinbuffer.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index cdc566d5c71..683f81897b5 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -541,6 +541,7 @@ bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeature const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() ); QgsFeatureRequest request; + request.setFlags( QgsFeatureRequest::NoGeometry ); request.setFilterExpression( filter ); request.setLimit( 1 ); @@ -554,15 +555,21 @@ bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeature if ( subsetFields ) { Q_FOREACH ( const QString &field, *subsetFields ) - existingFeature.setAttribute( field, joinFeature.attribute( field ) ); + { + QVariant newValue = joinFeature.attribute( field ); + int fieldIndex = joinLayer->fields().indexOf( field ); + joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue ); + } } else { Q_FOREACH ( const QgsField &field, joinFeature.fields() ) - existingFeature.setAttribute( field.name(), joinFeature.attribute( field.name() ) ); + { + QVariant newValue = joinFeature.attribute( field.name() ); + int fieldIndex = joinLayer->fields().indexOf( field.name() ); + joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue ); + } } - - joinLayer->updateFeature( existingFeature ); } else { From e008f5881b7d36f552e93da87a30892e0b891ddb Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Sun, 27 Aug 2017 23:14:44 +0200 Subject: [PATCH 186/364] Request without attributes --- src/core/qgsvectorlayerjoinbuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index 683f81897b5..ea4350a6fbe 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -542,6 +542,7 @@ bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeature QgsFeatureRequest request; request.setFlags( QgsFeatureRequest::NoGeometry ); + request.setSubsetOfAttributes( QgsAttributeList() ); request.setFilterExpression( filter ); request.setLimit( 1 ); From 19ce33f0119b13acb047c233ad6d4a79675d3b29 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Sun, 27 Aug 2017 23:21:54 +0200 Subject: [PATCH 187/364] Add a safety guard --- src/gui/qgsattributeform.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index bb19dbea3dc..f7cb3e52bf1 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -2055,6 +2055,9 @@ bool QgsAttributeForm::fieldIsEditable( const QgsVectorLayer &layer, int fieldIn void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww ) { + if ( !eww->widget() || !mIconMap[eww->widget()] ) + return; + // no icon by default mIconMap[eww->widget()]->hide(); From 029f741cfaac27b9e1959629afe454faed46e0ca Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 04:02:17 +1000 Subject: [PATCH 188/364] Add qgsAsConst(...) This is the equivalent of std::as_const (which requires c++17) or qAsConst (which requires Qt 5.7), neither of which we have as minimum dependancies. By wrapping implicitly shared Qt containers in qgsAsConst we can safely use c++ for ranged loops instead of Q_FOREACH. (Since Q_FOREACH's future is shaky) See https://www.kdab.com/goodbye-q_foreach/ for further details on why for causes a detach for Qt containers and why Q_FOREACH is being removed from Qt. --- python/core/qgis.sip | 2 ++ src/core/qgis.h | 25 +++++++++++++++++++++++++ tests/src/core/testqgis.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/python/core/qgis.sip b/python/core/qgis.sip index c5598d33ade..fc9bbba32ea 100644 --- a/python/core/qgis.sip +++ b/python/core/qgis.sip @@ -161,6 +161,8 @@ double qgsRound( double number, double places ); :rtype: float %End + + double qgsPermissiveToDouble( QString string, bool &ok ); %Docstring Converts a string to a double in a permissive way, e.g., allowing for incorrect diff --git a/src/core/qgis.h b/src/core/qgis.h index 69d6482a68e..94ccc635912 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -250,6 +250,31 @@ inline double qgsRound( double number, double places ) return static_cast( static_cast( number * scaleFactor + 0.5 ) ) / scaleFactor; } + +#ifndef SIP_RUN + +///@cond PRIVATE + +/** + * Adds const to non-const objects. + * + * To be used as a proxy for std::as_const until we target c++17 minimum. + * + * \since QGIS 3.0 + * \note not available in Python bindings + */ +// TODO - remove when we target c++17 minimum and can use std::as_const +template struct QgsAddConst { typedef const T Type; }; + +template +constexpr typename QgsAddConst::Type &qgsAsConst( T &t ) noexcept { return t; } + +template +void qgsAsConst( const T && ) = delete; + +///@endcond +#endif + /** Converts a string to a double in a permissive way, e.g., allowing for incorrect * numbers of digits between thousand separators * \param string string to convert diff --git a/tests/src/core/testqgis.cpp b/tests/src/core/testqgis.cpp index 91180a73299..7d94bcdd0eb 100644 --- a/tests/src/core/testqgis.cpp +++ b/tests/src/core/testqgis.cpp @@ -41,6 +41,7 @@ class TestQgis : public QObject void signalBlocker(); void qVariantCompare_data(); void qVariantCompare(); + void testQgsAsConst(); private: QString mReport; @@ -278,6 +279,32 @@ void TestQgis::qVariantCompare() QCOMPARE( qgsVariantGreaterThan( lhs, rhs ), greaterThan ); } +class ConstTester +{ + public: + + void doSomething() + { + mVal = 1; + } + + void doSomething() const + { + mVal = 2; + } + + mutable int mVal = 0; +}; + +void TestQgis::testQgsAsConst() +{ + ConstTester ct; + ct.doSomething(); + QCOMPARE( ct.mVal, 1 ); + qgsAsConst( ct ).doSomething(); + QCOMPARE( ct.mVal, 2 ); +} + QGSTEST_MAIN( TestQgis ) #include "testqgis.moc" From 347a8171b8d587d51566ce42f18119c11286d2a4 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 11:07:32 +1000 Subject: [PATCH 189/364] Ignore C4091 warnings caused by inclusion of DbgHelp.h on Windows There's a bug in this header file provided by the Win SDK, so just disable this warning for any QGIS source files which include DbgHelp.h --- src/app/CMakeLists.txt | 9 +++++++++ src/core/CMakeLists.txt | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 14d10cde980..0e118d3879c 100755 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -480,6 +480,15 @@ IF(PEDANTIC) ENDIF(MSVC) ENDIF(PEDANTIC) +IF(MSVC) +# -wd4091 Avoid 'typedef' ignored on left of '' when no variable is declared warning in DbgHelp.h +SET_SOURCE_FILES_PROPERTIES( + qgisapp.cpp + main.cpp + qgscrashhandler.cpp + PROPERTIES COMPILE_FLAGS -wd4091) +ENDIF(MSVC) + INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/app ${CMAKE_SOURCE_DIR}/src/app/composer diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c6f51a402e4..07bac53a9f7 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -540,6 +540,10 @@ ELSE(NOT MSVC) pal/feature.cpp pal/pointset.cpp PROPERTIES COMPILE_FLAGS -wd4702) + # -wd4091 Avoid 'typedef' ignored on left of '' when no variable is declared warning in DbgHelp.h + SET_SOURCE_FILES_PROPERTIES( + qgsstacktrace.cpp + PROPERTIES COMPILE_FLAGS -wd4091) ENDIF(NOT MSVC) SET(QGIS_CORE_MOC_HDRS From 2af10cb2ca1df8231f2143360f4dcc4b2f02de98 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 12:11:07 +1000 Subject: [PATCH 190/364] Fix C4566 warning on Windows build --- tests/src/core/testqgsexpression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 87c33809718..e1e274c0f20 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -921,7 +921,7 @@ class TestQgsExpression: public QObject QTest::newRow( "wordwrap with negative length" ) << "wordwrap('university of qgis',-3)" << false << QVariant( "university\nof qgis" ); QTest::newRow( "wordwrap with negative length, custom delimiter" ) << "wordwrap('university of qgis',-3,' ')" << false << QVariant( "university\nof qgis" ); QTest::newRow( "wordwrap on multi line" ) << "wordwrap('university of qgis\nsupports many multiline',-5,' ')" << false << QVariant( "university\nof qgis\nsupports\nmany multiline" ); - QTest::newRow( "wordwrap on zero-space width" ) << "wordwrap('test\u200Bzero-width space',4)" << false << QVariant( "test\nzero-width\nspace" ); + QTest::newRow( "wordwrap on zero-space width" ) << QStringLiteral( "wordwrap('test%1zero-width space',4)" ).arg( QChar(8203) ) << false << QVariant( "test\nzero-width\nspace" ); QTest::newRow( "format" ) << "format('%1 %2 %3 %1', 'One', 'Two', 'Three')" << false << QVariant( "One Two Three One" ); QTest::newRow( "concat" ) << "concat('a', 'b', 'c', 'd')" << false << QVariant( "abcd" ); QTest::newRow( "concat function single" ) << "concat('a')" << false << QVariant( "a" ); From 21a3b8a4f31c08e55fcb3719c7135eaa99eb328e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 12:39:59 +1000 Subject: [PATCH 191/364] Use QGSCOMPARENEAR instead of QVERIFY( qgsDoubleNear(... ) ) QGSCOMPARENEAR gives better debugging output when the test fails --- .../src/analysis/testqgsrastercalculator.cpp | 3 +- tests/src/app/testqgsattributetable.cpp | 9 +- tests/src/app/testqgsfieldcalculator.cpp | 9 +- .../src/app/testqgsmaptoolidentifyaction.cpp | 17 +-- tests/src/core/testqgscomposermap.cpp | 37 +++---- tests/src/core/testqgscomposerutils.cpp | 78 +++++++------- tests/src/core/testqgscomposition.cpp | 100 +++++++++--------- tests/src/core/testqgscoordinatetransform.cpp | 9 +- tests/src/core/testqgsdistancearea.cpp | 4 +- tests/src/core/testqgsexpression.cpp | 47 ++++---- tests/src/core/testqgsgeometry.cpp | 62 +++++------ tests/src/core/testqgsgeometryimport.cpp | 14 +-- tests/src/core/testqgsgeometryutils.cpp | 20 ++-- tests/src/core/testqgshistogram.cpp | 3 +- tests/src/core/testqgslayoutitem.cpp | 25 ++--- tests/src/core/testqgslayoutunits.cpp | 63 +++++------ tests/src/core/testqgslayoututils.cpp | 4 +- tests/src/core/testqgspoint.cpp | 31 +++--- tests/src/core/testqgsproperty.cpp | 8 +- tests/src/core/testqgsrasterlayer.cpp | 4 +- tests/src/core/testqgsrastersublayer.cpp | 5 +- tests/src/core/testqgsstatisticalsummary.cpp | 11 +- tests/src/providers/testqgsgdalprovider.cpp | 9 +- 23 files changed, 294 insertions(+), 278 deletions(-) diff --git a/tests/src/analysis/testqgsrastercalculator.cpp b/tests/src/analysis/testqgsrastercalculator.cpp index 7cda97bdca8..a83c634344b 100644 --- a/tests/src/analysis/testqgsrastercalculator.cpp +++ b/tests/src/analysis/testqgsrastercalculator.cpp @@ -21,6 +21,7 @@ Email : nyall dot dawson at gmail dot com #include "qgsrastermatrix.h" #include "qgsapplication.h" #include "qgsproject.h" +#include "qgstestutils.h" Q_DECLARE_METATYPE( QgsRasterCalcNode::Operator ) @@ -212,7 +213,7 @@ void TestQgsRasterCalculator::singleOp() QVERIFY( node.calculate( rasterData, result ) ); qDebug() << "Result: " << result.number() << " expected: " << expected; - QVERIFY( qgsDoubleNear( result.number(), expected, 0.0000000001 ) ); + QGSCOMPARENEAR( result.number(), expected, 0.0000000001 ); } diff --git a/tests/src/app/testqgsattributetable.cpp b/tests/src/app/testqgsattributetable.cpp index 0fea0b0152f..fffe70c0b62 100644 --- a/tests/src/app/testqgsattributetable.cpp +++ b/tests/src/app/testqgsattributetable.cpp @@ -28,6 +28,7 @@ #include "qgsvectorfilewriter.h" #include "qgstest.h" +#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the attribute table dialog @@ -114,7 +115,7 @@ void TestQgsAttributeTable::testFieldCalculation() QgsFeature f; QVERIFY( fit.nextFeature( f ) ); double expected = 26932.156; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 0.001 ); // change project length unit, check calculation respects unit QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceFeet ); @@ -126,7 +127,7 @@ void TestQgsAttributeTable::testFieldCalculation() fit = tempLayer->dataProvider()->getFeatures(); QVERIFY( fit.nextFeature( f ) ); expected = 88360.0918635; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 0.001 ); } void TestQgsAttributeTable::testFieldCalculationArea() @@ -164,7 +165,7 @@ void TestQgsAttributeTable::testFieldCalculationArea() QgsFeature f; QVERIFY( fit.nextFeature( f ) ); double expected = 1009089817.0; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 1.0 ); // change project area unit, check calculation respects unit QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMiles ); @@ -176,7 +177,7 @@ void TestQgsAttributeTable::testFieldCalculationArea() fit = tempLayer->dataProvider()->getFeatures(); QVERIFY( fit.nextFeature( f ) ); expected = 389.6117565069; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 0.001 ); } void TestQgsAttributeTable::testNoGeom() diff --git a/tests/src/app/testqgsfieldcalculator.cpp b/tests/src/app/testqgsfieldcalculator.cpp index ab0478301d4..697e34eb56c 100644 --- a/tests/src/app/testqgsfieldcalculator.cpp +++ b/tests/src/app/testqgsfieldcalculator.cpp @@ -24,6 +24,7 @@ #include "qgsproject.h" #include "qgsmapcanvas.h" #include "qgsunittypes.h" +#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the field calculator @@ -107,7 +108,7 @@ void TestQgsFieldCalculator::testLengthCalculations() QgsFeature f; QVERIFY( fit.nextFeature( f ) ); double expected = 26932.156; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 0.001 ); // change project length unit, check calculation respects unit QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceFeet ); @@ -122,7 +123,7 @@ void TestQgsFieldCalculator::testLengthCalculations() fit = tempLayer->dataProvider()->getFeatures(); QVERIFY( fit.nextFeature( f ) ); expected = 88360.0918635; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 0.001 ); } @@ -168,7 +169,7 @@ void TestQgsFieldCalculator::testAreaCalculations() QgsFeature f; QVERIFY( fit.nextFeature( f ) ); double expected = 1009089817.0; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 1.0 ); // change project area unit, check calculation respects unit QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMiles ); @@ -183,7 +184,7 @@ void TestQgsFieldCalculator::testAreaCalculations() fit = tempLayer->dataProvider()->getFeatures(); QVERIFY( fit.nextFeature( f ) ); expected = 389.6117565069; - QVERIFY( qgsDoubleNear( f.attribute( "col1" ).toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( f.attribute( "col1" ).toDouble(), expected, 0.001 ); } QGSTEST_MAIN( TestQgsFieldCalculator ) diff --git a/tests/src/app/testqgsmaptoolidentifyaction.cpp b/tests/src/app/testqgsmaptoolidentifyaction.cpp index 8f3ff2920a6..77bd17a66b6 100644 --- a/tests/src/app/testqgsmaptoolidentifyaction.cpp +++ b/tests/src/app/testqgsmaptoolidentifyaction.cpp @@ -26,6 +26,7 @@ #include "qgsunittypes.h" #include "qgsmaptoolidentifyaction.h" #include "qgssettings.h" +#include "qgstestutils.h" #include "cpl_conv.h" @@ -142,7 +143,7 @@ void TestQgsMapToolIdentifyAction::lengthCalculation() QCOMPARE( result.length(), 1 ); QString derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length" )]; double length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( length, 26932.2, 0.1 ) ); + QGSCOMPARENEAR( length, 26932.2, 0.1 ); //check that project units are respected QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceFeet ); @@ -150,7 +151,7 @@ void TestQgsMapToolIdentifyAction::lengthCalculation() QCOMPARE( result.length(), 1 ); derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length" )]; length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( length, 88360.1, 0.1 ) ); + QGSCOMPARENEAR( length, 88360.1, 0.1 ); //test unchecked "keep base units" setting s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), false ); @@ -158,7 +159,7 @@ void TestQgsMapToolIdentifyAction::lengthCalculation() QCOMPARE( result.length(), 1 ); derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length" )]; length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( length, 16.735, 0.001 ) ); + QGSCOMPARENEAR( length, 16.735, 0.001 ); } void TestQgsMapToolIdentifyAction::perimeterCalculation() @@ -203,7 +204,7 @@ void TestQgsMapToolIdentifyAction::perimeterCalculation() QCOMPARE( result.length(), 1 ); derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter" )]; perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( perimeter, 420896.0, 0.1 ) ); + QGSCOMPARENEAR( perimeter, 420896.0, 0.1 ); //test unchecked "keep base units" setting s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), false ); @@ -211,7 +212,7 @@ void TestQgsMapToolIdentifyAction::perimeterCalculation() QCOMPARE( result.length(), 1 ); derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter" )]; perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( perimeter, 79.715, 0.001 ) ); + QGSCOMPARENEAR( perimeter, 79.715, 0.001 ); } void TestQgsMapToolIdentifyAction::areaCalculation() @@ -249,7 +250,7 @@ void TestQgsMapToolIdentifyAction::areaCalculation() QCOMPARE( result.length(), 1 ); QString derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area" )]; double area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( area, 1009089817.0, 1.0 ) ); + QGSCOMPARENEAR( area, 1009089817.0, 1.0 ); //check that project units are respected QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMiles ); @@ -257,7 +258,7 @@ void TestQgsMapToolIdentifyAction::areaCalculation() QCOMPARE( result.length(), 1 ); derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area" )]; area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( area, 389.6117, 0.001 ) ); + QGSCOMPARENEAR( area, 389.6117, 0.001 ); //test unchecked "keep base units" setting s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), false ); @@ -266,7 +267,7 @@ void TestQgsMapToolIdentifyAction::areaCalculation() QCOMPARE( result.length(), 1 ); derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area" )]; area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble(); - QVERIFY( qgsDoubleNear( area, 389.6117, 0.001 ) ); + QGSCOMPARENEAR( area, 389.6117, 0.001 ); } // private diff --git a/tests/src/core/testqgscomposermap.cpp b/tests/src/core/testqgscomposermap.cpp index 98b4c13cce5..9853e6dea95 100644 --- a/tests/src/core/testqgscomposermap.cpp +++ b/tests/src/core/testqgscomposermap.cpp @@ -29,6 +29,7 @@ #include "qgsproperty.h" #include #include "qgstest.h" +#include "qgstestutils.h" class TestQgsComposerMap : public QObject { @@ -176,34 +177,34 @@ void TestQgsComposerMap::worldFileGeneration() double a, b, c, d, e, f; mComposition->computeWorldFileParameters( a, b, c, d, e, f ); - QVERIFY( qgsDoubleNear( a, 4.18048, 0.001 ) ); - QVERIFY( qgsDoubleNear( b, 2.41331, 0.001 ) ); - QVERIFY( qgsDoubleNear( c, 779444, 1 ) ); - QVERIFY( qgsDoubleNear( d, 2.4136, 0.001 ) ); - QVERIFY( qgsDoubleNear( e, -4.17997, 0.001 ) ); - QVERIFY( qgsDoubleNear( f, 3.34241e+06, 1e+03 ) ); + QGSCOMPARENEAR( a, 4.18048, 0.001 ); + QGSCOMPARENEAR( b, 2.41331, 0.001 ); + QGSCOMPARENEAR( c, 779444, 1 ); + QGSCOMPARENEAR( d, 2.4136, 0.001 ); + QGSCOMPARENEAR( e, -4.17997, 0.001 ); + QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 ); //test with map on second page. Parameters should be the same mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 ); mComposition->computeWorldFileParameters( a, b, c, d, e, f ); - QVERIFY( qgsDoubleNear( a, 4.18048, 0.001 ) ); - QVERIFY( qgsDoubleNear( b, 2.41331, 0.001 ) ); - QVERIFY( qgsDoubleNear( c, 779444, 1 ) ); - QVERIFY( qgsDoubleNear( d, 2.4136, 0.001 ) ); - QVERIFY( qgsDoubleNear( e, -4.17997, 0.001 ) ); - QVERIFY( qgsDoubleNear( f, 3.34241e+06, 1e+03 ) ); + QGSCOMPARENEAR( a, 4.18048, 0.001 ); + QGSCOMPARENEAR( b, 2.41331, 0.001 ); + QGSCOMPARENEAR( c, 779444, 1 ); + QGSCOMPARENEAR( d, 2.4136, 0.001 ); + QGSCOMPARENEAR( e, -4.17997, 0.001 ); + QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 ); //test computing parameters for specific region mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 ); mComposition->computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f ); - QVERIFY( qgsDoubleNear( a, 4.18061, 0.001 ) ); - QVERIFY( qgsDoubleNear( b, 2.41321, 0.001 ) ); - QVERIFY( qgsDoubleNear( c, 773810, 1 ) ); - QVERIFY( qgsDoubleNear( d, 2.4137, 0.001 ) ); - QVERIFY( qgsDoubleNear( e, -4.1798, 0.001 ) ); - QVERIFY( qgsDoubleNear( f, 3.35331e+06, 1e+03 ) ); + QGSCOMPARENEAR( a, 4.18061, 0.001 ); + QGSCOMPARENEAR( b, 2.41321, 0.001 ); + QGSCOMPARENEAR( c, 773810, 1 ); + QGSCOMPARENEAR( d, 2.4137, 0.001 ); + QGSCOMPARENEAR( e, -4.1798, 0.001 ); + QGSCOMPARENEAR( f, 3.35331e+06, 1e+03 ); mComposition->setGenerateWorldFile( false ); mComposerMap->setMapRotation( 0.0 ); diff --git a/tests/src/core/testqgscomposerutils.cpp b/tests/src/core/testqgscomposerutils.cpp index 24fce8899d7..36daa711cc4 100644 --- a/tests/src/core/testqgscomposerutils.cpp +++ b/tests/src/core/testqgscomposerutils.cpp @@ -169,8 +169,8 @@ void TestQgsComposerUtils::rotate() double x = ( *it ).first.x1(); double y = ( *it ).first.y1(); QgsComposerUtils::rotate( ( *it ).second, x, y ); - QVERIFY( qgsDoubleNear( x, ( *it ).first.x2() ) ); - QVERIFY( qgsDoubleNear( y, ( *it ).first.y2() ) ); + QGSCOMPARENEAR( x, ( *it ).first.x2(), 4 * DBL_EPSILON ); + QGSCOMPARENEAR( y, ( *it ).first.y2(), 4 * DBL_EPSILON ); } } @@ -194,7 +194,7 @@ void TestQgsComposerUtils::normalizedAngle() QList< QPair< double, double > >::const_iterator it = testVals.constBegin(); for ( ; it != testVals.constEnd(); ++it ) { - QVERIFY( qgsDoubleNear( QgsComposerUtils::normalizedAngle( ( *it ).first ), ( *it ).second ) ); + QGSCOMPARENEAR( QgsComposerUtils::normalizedAngle( ( *it ).first ), ( *it ).second, 4 * DBL_EPSILON ); } } @@ -243,7 +243,7 @@ void TestQgsComposerUtils::snappedAngle() QList< QPair< double, double > >::const_iterator it = testVals.constBegin(); for ( ; it != testVals.constEnd(); ++it ) { - QVERIFY( qgsDoubleNear( QgsComposerUtils::snappedAngle( ( *it ).first ), ( *it ).second ) ); + QGSCOMPARENEAR( QgsComposerUtils::snappedAngle( ( *it ).first ), ( *it ).second, 4 * DBL_EPSILON ); } } @@ -293,7 +293,7 @@ void TestQgsComposerUtils::largestRotatedRect() || ( qgsDoubleNear( rotatedRectBounds.height(), bounds.height(), 0.001 ) && ( rotatedRectBounds.width() <= bounds.width() ) ) ); //also verify that aspect ratio of rectangle has not changed - QVERIFY( qgsDoubleNear( result.width() / result.height(), wideRect.width() / wideRect.height() ) ); + QGSCOMPARENEAR( result.width() / result.height(), wideRect.width() / wideRect.height(), 4 * DBL_EPSILON ); } //and again for the high rectangle for ( double rotation = 10; rotation < 360; rotation += 10 ) @@ -308,48 +308,48 @@ void TestQgsComposerUtils::largestRotatedRect() || ( qgsDoubleNear( rotatedRectBounds.height(), bounds.height(), 0.001 ) && ( rotatedRectBounds.width() <= bounds.width() ) ) ); //also verify that aspect ratio of rectangle has not changed - QVERIFY( qgsDoubleNear( result.width() / result.height(), highRect.width() / highRect.height() ) ); + QGSCOMPARENEAR( result.width() / result.height(), highRect.width() / highRect.height(), 4 * DBL_EPSILON ); } } void TestQgsComposerUtils::pointsToMM() { //test conversion of points to mm, based on 1 point = 1 / 72 of an inch - QVERIFY( qgsDoubleNear( QgsComposerUtils::pointsToMM( 72 / 25.4 ), 1, 0.001 ) ); + QGSCOMPARENEAR( QgsComposerUtils::pointsToMM( 72 / 25.4 ), 1, 0.001 ); } void TestQgsComposerUtils::mmToPoints() { //test conversion of mm to points, based on 1 point = 1 / 72 of an inch - QVERIFY( qgsDoubleNear( QgsComposerUtils::mmToPoints( 25.4 / 72 ), 1, 0.001 ) ); + QGSCOMPARENEAR( QgsComposerUtils::mmToPoints( 25.4 / 72 ), 1, 0.001 ); } void TestQgsComposerUtils::relativePosition() { //+ve gradient - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 1, 0, 2, 0, 4 ), 2, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 0, 0, 2, 0, 4 ), 0, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 2, 0, 2, 0, 4 ), 4, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 4, 0, 2, 0, 4 ), 8, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( -2, 0, 2, 0, 4 ), -4, 0.001 ) ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 0, 2, 0, 4 ), 2, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 0, 2, 0, 4 ), 0, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 0, 2, 0, 4 ), 4, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 0, 2, 0, 4 ), 8, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 0, 2, 0, 4 ), -4, 0.001 ); //-ve gradient - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 1, 0, 2, 4, 0 ), 2, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 0, 0, 2, 4, 0 ), 4, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 2, 0, 2, 4, 0 ), 0, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 4, 0, 2, 4, 0 ), -4, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( -2, 0, 2, 4, 0 ), 8, 0.001 ) ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 0, 2, 4, 0 ), 2, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 0, 2, 4, 0 ), 4, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 0, 2, 4, 0 ), 0, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 0, 2, 4, 0 ), -4, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 0, 2, 4, 0 ), 8, 0.001 ); //-ve domain - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 1, 2, 0, 0, 4 ), 2, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 0, 2, 0, 0, 4 ), 4, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 2, 2, 0, 0, 4 ), 0, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 4, 2, 0, 0, 4 ), -4, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( -2, 2, 0, 0, 4 ), 8, 0.001 ) ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 2, 0, 0, 4 ), 2, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 2, 0, 0, 4 ), 4, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 2, 0, 0, 4 ), 0, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 2, 0, 0, 4 ), -4, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 2, 0, 0, 4 ), 8, 0.001 ); //-ve domain and gradient - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 1, 2, 0, 4, 0 ), 2, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 0, 2, 0, 4, 0 ), 0, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 2, 2, 0, 4, 0 ), 4, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( 4, 2, 0, 4, 0 ), 8, 0.001 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::relativePosition( -2, 2, 0, 4, 0 ), -4, 0.001 ) ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 2, 0, 4, 0 ), 2, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 2, 0, 4, 0 ), 0, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 2, 0, 4, 0 ), 4, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 2, 0, 4, 0 ), 8, 0.001 ); + QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 2, 0, 4, 0 ), -4, 0.001 ); } void TestQgsComposerUtils::relativeResizeRect() @@ -528,36 +528,36 @@ void TestQgsComposerUtils::fontAscentMM() { mTestFont.setPointSize( 12 ); //platform specific font rendering differences mean these tests need to be very leniant - QVERIFY( qgsDoubleNear( QgsComposerUtils::fontAscentMM( mTestFont ), 3.9, 0.5 ) ); + QGSCOMPARENEAR( QgsComposerUtils::fontAscentMM( mTestFont ), 3.9, 0.5 ); } void TestQgsComposerUtils::fontDescentMM() { mTestFont.setPointSize( 12 ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::fontDescentMM( mTestFont ), 0.9, 0.05 ) ); + QGSCOMPARENEAR( QgsComposerUtils::fontDescentMM( mTestFont ), 0.9, 0.05 ); } void TestQgsComposerUtils::fontHeightMM() { mTestFont.setPointSize( 12 ); //platform specific font rendering differences mean these tests need to be very leniant - QVERIFY( qgsDoubleNear( QgsComposerUtils::fontHeightMM( mTestFont ), 4.9, 0.5 ) ); + QGSCOMPARENEAR( QgsComposerUtils::fontHeightMM( mTestFont ), 4.9, 0.5 ); } void TestQgsComposerUtils::fontHeightCharacterMM() { mTestFont.setPointSize( 12 ); //platform specific font rendering differences mean these tests need to be very leniant - QVERIFY( qgsDoubleNear( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'a' ) ), 2.4, 0.15 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'l' ) ), 3.15, 0.16 ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'g' ) ), 3.2, 0.11 ) ); + QGSCOMPARENEAR( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'a' ) ), 2.4, 0.15 ); + QGSCOMPARENEAR( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'l' ) ), 3.15, 0.16 ); + QGSCOMPARENEAR( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'g' ) ), 3.2, 0.11 ); } void TestQgsComposerUtils::textWidthMM() { //platform specific font rendering differences mean this test needs to be very leniant mTestFont.setPointSize( 12 ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::textWidthMM( mTestFont, QString( "test string" ) ), 20, 2 ) ); + QGSCOMPARENEAR( QgsComposerUtils::textWidthMM( mTestFont, QString( "test string" ) ), 20, 2 ); } void TestQgsComposerUtils::textHeightMM() @@ -565,13 +565,13 @@ void TestQgsComposerUtils::textHeightMM() //platform specific font rendering differences mean this test needs to be very leniant mTestFont.setPointSize( 12 ); QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test string" ) ) ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::textHeightMM( mTestFont, QString( "test string" ) ), 3.9, 0.2 ) ); + QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test string" ) ), 3.9, 0.2 ); QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ) ) ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ) ), 8.7, 0.2 ) ); + QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ) ), 8.7, 0.2 ); QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ), 2 ) ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ), 2 ), 13.5, 0.2 ) ); + QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ), 2 ), 13.5, 0.2 ); QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring\nstring" ) ) ) ); - QVERIFY( qgsDoubleNear( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring\nstring" ) ), 13.5, 0.2 ) ); + QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring\nstring" ) ), 13.5, 0.2 ); } void TestQgsComposerUtils::drawTextPos() diff --git a/tests/src/core/testqgscomposition.cpp b/tests/src/core/testqgscomposition.cpp index 9eb184d7974..380ec809940 100644 --- a/tests/src/core/testqgscomposition.cpp +++ b/tests/src/core/testqgscomposition.cpp @@ -365,34 +365,34 @@ void TestQgsComposition::bounds() //check bounds QRectF compositionBounds = composition->compositionBounds( false ); - QVERIFY( qgsDoubleNear( compositionBounds.height(), 372.15, 0.01 ) ); - QVERIFY( qgsDoubleNear( compositionBounds.width(), 301.00, 0.01 ) ); - QVERIFY( qgsDoubleNear( compositionBounds.left(), -2, 0.01 ) ); - QVERIFY( qgsDoubleNear( compositionBounds.top(), -2, 0.01 ) ); + QGSCOMPARENEAR( compositionBounds.height(), 372.15, 0.01 ); + QGSCOMPARENEAR( compositionBounds.width(), 301.00, 0.01 ); + QGSCOMPARENEAR( compositionBounds.left(), -2, 0.01 ); + QGSCOMPARENEAR( compositionBounds.top(), -2, 0.01 ); QRectF compositionBoundsNoPage = composition->compositionBounds( true ); - QVERIFY( qgsDoubleNear( compositionBoundsNoPage.height(), 320.36, 0.01 ) ); - QVERIFY( qgsDoubleNear( compositionBoundsNoPage.width(), 250.30, 0.01 ) ); - QVERIFY( qgsDoubleNear( compositionBoundsNoPage.left(), 9.85, 0.01 ) ); - QVERIFY( qgsDoubleNear( compositionBoundsNoPage.top(), 49.79, 0.01 ) ); + QGSCOMPARENEAR( compositionBoundsNoPage.height(), 320.36, 0.01 ); + QGSCOMPARENEAR( compositionBoundsNoPage.width(), 250.30, 0.01 ); + QGSCOMPARENEAR( compositionBoundsNoPage.left(), 9.85, 0.01 ); + QGSCOMPARENEAR( compositionBoundsNoPage.top(), 49.79, 0.01 ); QRectF page1Bounds = composition->pageItemBounds( 0, true ); - QVERIFY( qgsDoubleNear( page1Bounds.height(), 150.36, 0.01 ) ); - QVERIFY( qgsDoubleNear( page1Bounds.width(), 155.72, 0.01 ) ); - QVERIFY( qgsDoubleNear( page1Bounds.left(), 54.43, 0.01 ) ); - QVERIFY( qgsDoubleNear( page1Bounds.top(), 49.79, 0.01 ) ); + QGSCOMPARENEAR( page1Bounds.height(), 150.36, 0.01 ); + QGSCOMPARENEAR( page1Bounds.width(), 155.72, 0.01 ); + QGSCOMPARENEAR( page1Bounds.left(), 54.43, 0.01 ); + QGSCOMPARENEAR( page1Bounds.top(), 49.79, 0.01 ); QRectF page2Bounds = composition->pageItemBounds( 1, true ); - QVERIFY( qgsDoubleNear( page2Bounds.height(), 100.30, 0.01 ) ); - QVERIFY( qgsDoubleNear( page2Bounds.width(), 50.30, 0.01 ) ); - QVERIFY( qgsDoubleNear( page2Bounds.left(), 209.85, 0.01 ) ); - QVERIFY( qgsDoubleNear( page2Bounds.top(), 249.85, 0.01 ) ); + QGSCOMPARENEAR( page2Bounds.height(), 100.30, 0.01 ); + QGSCOMPARENEAR( page2Bounds.width(), 50.30, 0.01 ); + QGSCOMPARENEAR( page2Bounds.left(), 209.85, 0.01 ); + QGSCOMPARENEAR( page2Bounds.top(), 249.85, 0.01 ); QRectF page2BoundsWithHidden = composition->pageItemBounds( 1, false ); - QVERIFY( qgsDoubleNear( page2BoundsWithHidden.height(), 120.30, 0.01 ) ); - QVERIFY( qgsDoubleNear( page2BoundsWithHidden.width(), 250.30, 0.01 ) ); - QVERIFY( qgsDoubleNear( page2BoundsWithHidden.left(), 9.85, 0.01 ) ); - QVERIFY( qgsDoubleNear( page2BoundsWithHidden.top(), 249.85, 0.01 ) ); + QGSCOMPARENEAR( page2BoundsWithHidden.height(), 120.30, 0.01 ); + QGSCOMPARENEAR( page2BoundsWithHidden.width(), 250.30, 0.01 ); + QGSCOMPARENEAR( page2BoundsWithHidden.left(), 9.85, 0.01 ); + QGSCOMPARENEAR( page2BoundsWithHidden.top(), 249.85, 0.01 ); delete composition; } @@ -539,54 +539,54 @@ void TestQgsComposition::georeference() composition->addComposerMap( map ); t = composition->computeGeoTransform( map ); - QVERIFY( qgsDoubleNear( t[0], 1925.0, 1.0 ) ); - QVERIFY( qgsDoubleNear( t[1], 0.211719, 0.0001 ) ); - QVERIFY( qgsDoubleNear( t[2], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[3], 3200, 1 ) ); - QVERIFY( qgsDoubleNear( t[4], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[5], -0.211694, 0.0001 ) ); + QGSCOMPARENEAR( t[0], 1925.0, 1.0 ); + QGSCOMPARENEAR( t[1], 0.211719, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 3200, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.211694, 0.0001 ); delete[] t; // don't specify map composition->setReferenceMap( map ); t = composition->computeGeoTransform(); - QVERIFY( qgsDoubleNear( t[0], 1925.0, 1.0 ) ); - QVERIFY( qgsDoubleNear( t[1], 0.211719, 0.0001 ) ); - QVERIFY( qgsDoubleNear( t[2], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[3], 3200, 1 ) ); - QVERIFY( qgsDoubleNear( t[4], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[5], -0.211694, 0.0001 ) ); + QGSCOMPARENEAR( t[0], 1925.0, 1.0 ); + QGSCOMPARENEAR( t[1], 0.211719, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 3200, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.211694, 0.0001 ); delete[] t; // specify extent t = composition->computeGeoTransform( map, QRectF( 70, 100, 50, 60 ) ); - QVERIFY( qgsDoubleNear( t[0], 2100.0, 1.0 ) ); - QVERIFY( qgsDoubleNear( t[1], 0.211864, 0.0001 ) ); - QVERIFY( qgsDoubleNear( t[2], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[3], 2950, 1 ) ); - QVERIFY( qgsDoubleNear( t[4], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[5], -0.211864, 0.0001 ) ); + QGSCOMPARENEAR( t[0], 2100.0, 1.0 ); + QGSCOMPARENEAR( t[1], 0.211864, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 2950, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.211864, 0.0001 ); delete[] t; // specify dpi t = composition->computeGeoTransform( map, QRectF(), 75 ); - QVERIFY( qgsDoubleNear( t[0], 1925.0, 1 ) ); - QVERIFY( qgsDoubleNear( t[1], 0.847603, 0.0001 ) ); - QVERIFY( qgsDoubleNear( t[2], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[3], 3200.0, 1 ) ); - QVERIFY( qgsDoubleNear( t[4], 0.0 ) ); - QVERIFY( qgsDoubleNear( t[5], -0.846774, 0.0001 ) ); + QGSCOMPARENEAR( t[0], 1925.0, 1 ); + QGSCOMPARENEAR( t[1], 0.847603, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 3200.0, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.846774, 0.0001 ); delete[] t; // rotation map->setMapRotation( 45 ); t = composition->computeGeoTransform( map ); - QVERIFY( qgsDoubleNear( t[0], 1825.7, 1 ) ); - QVERIFY( qgsDoubleNear( t[1], 0.149708, 0.0001 ) ); - QVERIFY( qgsDoubleNear( t[2], 0.149708, 0.0001 ) ); - QVERIFY( qgsDoubleNear( t[3], 2889.64, 1 ) ); - QVERIFY( qgsDoubleNear( t[4], 0.14969, 0.0001 ) ); - QVERIFY( qgsDoubleNear( t[5], -0.14969, 0.0001 ) ); + QGSCOMPARENEAR( t[0], 1825.7, 1 ); + QGSCOMPARENEAR( t[1], 0.149708, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.149708, 0.0001 ); + QGSCOMPARENEAR( t[3], 2889.64, 1 ); + QGSCOMPARENEAR( t[4], 0.14969, 0.0001 ); + QGSCOMPARENEAR( t[5], -0.14969, 0.0001 ); delete[] t; delete composition; diff --git a/tests/src/core/testqgscoordinatetransform.cpp b/tests/src/core/testqgscoordinatetransform.cpp index 0c50498ed9c..8a71e3be93f 100644 --- a/tests/src/core/testqgscoordinatetransform.cpp +++ b/tests/src/core/testqgscoordinatetransform.cpp @@ -19,6 +19,7 @@ #include "qgsrectangle.h" #include #include "qgstest.h" +#include "qgstestutils.h" class TestQgsCoordinateTransform: public QObject { @@ -200,10 +201,10 @@ void TestQgsCoordinateTransform::transformBoundingBox() qDebug( "BBox transform y min: %.17f", resultRect.yMinimum() ); qDebug( "BBox transform y max: %.17f", resultRect.yMaximum() ); - QVERIFY( qgsDoubleNear( resultRect.xMinimum(), expectedRect.xMinimum(), 0.001 ) ); - QVERIFY( qgsDoubleNear( resultRect.yMinimum(), expectedRect.yMinimum(), 0.001 ) ); - QVERIFY( qgsDoubleNear( resultRect.xMaximum(), expectedRect.xMaximum(), 0.001 ) ); - QVERIFY( qgsDoubleNear( resultRect.yMaximum(), expectedRect.yMaximum(), 0.001 ) ); + QGSCOMPARENEAR( resultRect.xMinimum(), expectedRect.xMinimum(), 0.001 ); + QGSCOMPARENEAR( resultRect.yMinimum(), expectedRect.yMinimum(), 0.001 ); + QGSCOMPARENEAR( resultRect.xMaximum(), expectedRect.xMaximum(), 0.001 ); + QGSCOMPARENEAR( resultRect.yMaximum(), expectedRect.yMaximum(), 0.001 ); } QGSTEST_MAIN( TestQgsCoordinateTransform ) diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp index d7dccd81de7..661a8e1b753 100644 --- a/tests/src/core/testqgsdistancearea.cpp +++ b/tests/src/core/testqgsdistancearea.cpp @@ -234,14 +234,14 @@ void TestQgsDistanceArea::collections() double result = myDa.measureLength( lines ); QGSCOMPARENEAR( result, 12006159, 1 ); result = myDa.measureArea( lines ); - QVERIFY( qgsDoubleNear( result, 0 ) ); + QGSCOMPARENEAR( result, 0, 4 * DBL_EPSILON ); //collection of polygons QgsGeometry polys( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) ).release() ); result = myDa.measureArea( polys ); QGSCOMPARENEAR( result, 670434859475LL, 1 ); result = myDa.measureLength( polys ); - QVERIFY( qgsDoubleNear( result, 0 ) ); + QGSCOMPARENEAR( result, 0, 4 * DBL_EPSILON ); //mixed collection QgsGeometry mixed( QgsGeometryFactory::geomFromWkt( QStringLiteral( "GeometryCollection( LineString(0 36.53, 5.76 -48.16), LineString(0 25.54, 24.20 36.70), Polygon((0 36.53, 5.76 -48.16, 0 25.54, 0 36.53)), Polygon((10 20, 15 20, 15 10, 10 20)) )" ) ).release() ); diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index e1e274c0f20..bdc5f39ba2f 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -33,6 +33,7 @@ #include "qgsrasterlayer.h" #include "qgsproject.h" #include "qgsexpressionnodeimpl.h" +#include "qgstestutils.h" static void _parseAndEvalExpr( int arg ) { @@ -921,7 +922,7 @@ class TestQgsExpression: public QObject QTest::newRow( "wordwrap with negative length" ) << "wordwrap('university of qgis',-3)" << false << QVariant( "university\nof qgis" ); QTest::newRow( "wordwrap with negative length, custom delimiter" ) << "wordwrap('university of qgis',-3,' ')" << false << QVariant( "university\nof qgis" ); QTest::newRow( "wordwrap on multi line" ) << "wordwrap('university of qgis\nsupports many multiline',-5,' ')" << false << QVariant( "university\nof qgis\nsupports\nmany multiline" ); - QTest::newRow( "wordwrap on zero-space width" ) << QStringLiteral( "wordwrap('test%1zero-width space',4)" ).arg( QChar(8203) ) << false << QVariant( "test\nzero-width\nspace" ); + QTest::newRow( "wordwrap on zero-space width" ) << QStringLiteral( "wordwrap('test%1zero-width space',4)" ).arg( QChar( 8203 ) ) << false << QVariant( "test\nzero-width\nspace" ); QTest::newRow( "format" ) << "format('%1 %2 %3 %1', 'One', 'Two', 'Three')" << false << QVariant( "One Two Three One" ); QTest::newRow( "concat" ) << "concat('a', 'b', 'c', 'd')" << false << QVariant( "abcd" ); QTest::newRow( "concat function single" ) << "concat('a')" << false << QVariant( "a" ); @@ -1638,7 +1639,7 @@ class TestQgsExpression: public QObject qDebug() << exp.evalErrorString(); QCOMPARE( exp.hasEvalError(), false ); - QVERIFY( qgsDoubleNear( res.toDouble(), -85.65217, 0.00001 ) ); + QGSCOMPARENEAR( res.toDouble(), -85.65217, 0.00001 ); } void eval_rand() @@ -1940,63 +1941,63 @@ class TestQgsExpression: public QObject QgsExpression expArea( QStringLiteral( "$area" ) ); QVariant vArea = expArea.evaluate( &context ); double expected = 1005640568.0; - QVERIFY( qgsDoubleNear( vArea.toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( vArea.toDouble(), expected, 1.0 ); // units should not be converted if no geometry calculator set expArea.setAreaUnits( QgsUnitTypes::AreaSquareFeet ); vArea = expArea.evaluate( &context ); - QVERIFY( qgsDoubleNear( vArea.toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( vArea.toDouble(), expected, 1.0 ); expArea.setAreaUnits( QgsUnitTypes::AreaSquareNauticalMiles ); vArea = expArea.evaluate( &context ); - QVERIFY( qgsDoubleNear( vArea.toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( vArea.toDouble(), expected, 1.0 ); // test area with geomCalculator QgsExpression expArea2( QStringLiteral( "$area" ) ); expArea2.setGeomCalculator( &da ); vArea = expArea2.evaluate( &context ); expected = 1009089817.0; - QVERIFY( qgsDoubleNear( vArea.toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( vArea.toDouble(), expected, 1.0 ); // test unit conversion expArea2.setAreaUnits( QgsUnitTypes::AreaSquareMeters ); //default units should be square meters vArea = expArea2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vArea.toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( vArea.toDouble(), expected, 1.0 ); expArea2.setAreaUnits( QgsUnitTypes::AreaUnknownUnit ); //unknown units should not be converted vArea = expArea2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vArea.toDouble(), expected, 1.0 ) ); + QGSCOMPARENEAR( vArea.toDouble(), expected, 1.0 ); expArea2.setAreaUnits( QgsUnitTypes::AreaSquareMiles ); expected = 389.6117565069; vArea = expArea2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vArea.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vArea.toDouble(), expected, 0.001 ); // test perimeter without geomCalculator QgsExpression expPerimeter( QStringLiteral( "$perimeter" ) ); QVariant vPerimeter = expPerimeter.evaluate( &context ); expected = 128282.086; - QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vPerimeter.toDouble(), expected, 0.001 ); // units should not be converted if no geometry calculator set expPerimeter.setDistanceUnits( QgsUnitTypes::DistanceFeet ); vPerimeter = expPerimeter.evaluate( &context ); - QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vPerimeter.toDouble(), expected, 0.001 ); expPerimeter.setDistanceUnits( QgsUnitTypes::DistanceNauticalMiles ); vPerimeter = expPerimeter.evaluate( &context ); - QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vPerimeter.toDouble(), expected, 0.001 ); // test perimeter with geomCalculator QgsExpression expPerimeter2( QStringLiteral( "$perimeter" ) ); expPerimeter2.setGeomCalculator( &da ); vPerimeter = expPerimeter2.evaluate( &context ); expected = 128289.074; - QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vPerimeter.toDouble(), expected, 0.001 ); // test unit conversion expPerimeter2.setDistanceUnits( QgsUnitTypes::DistanceMeters ); //default units should be meters vPerimeter = expPerimeter2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vPerimeter.toDouble(), expected, 0.001 ); expPerimeter2.setDistanceUnits( QgsUnitTypes::DistanceUnknownUnit ); //unknown units should not be converted vPerimeter = expPerimeter2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vPerimeter.toDouble(), expected, 0.001 ); expPerimeter2.setDistanceUnits( QgsUnitTypes::DistanceFeet ); expected = 420895.9120735; vPerimeter = expPerimeter2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vPerimeter.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vPerimeter.toDouble(), expected, 0.001 ); // test length without geomCalculator QgsPolyline line3111; @@ -2008,32 +2009,32 @@ class TestQgsExpression: public QObject QgsExpression expLength( QStringLiteral( "$length" ) ); QVariant vLength = expLength.evaluate( &context ); expected = 26930.637; - QVERIFY( qgsDoubleNear( vLength.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vLength.toDouble(), expected, 0.001 ); // units should not be converted if no geometry calculator set expLength.setDistanceUnits( QgsUnitTypes::DistanceFeet ); vLength = expLength.evaluate( &context ); - QVERIFY( qgsDoubleNear( vLength.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vLength.toDouble(), expected, 0.001 ); expLength.setDistanceUnits( QgsUnitTypes::DistanceNauticalMiles ); vLength = expLength.evaluate( &context ); - QVERIFY( qgsDoubleNear( vLength.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vLength.toDouble(), expected, 0.001 ); // test length with geomCalculator QgsExpression expLength2( QStringLiteral( "$length" ) ); expLength2.setGeomCalculator( &da ); vLength = expLength2.evaluate( &context ); expected = 26932.156; - QVERIFY( qgsDoubleNear( vLength.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vLength.toDouble(), expected, 0.001 ); // test unit conversion expLength2.setDistanceUnits( QgsUnitTypes::DistanceMeters ); //default units should be meters vLength = expLength2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vLength.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vLength.toDouble(), expected, 0.001 ); expLength2.setDistanceUnits( QgsUnitTypes::DistanceUnknownUnit ); //unknown units should not be converted vLength = expLength2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vLength.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vLength.toDouble(), expected, 0.001 ); expLength2.setDistanceUnits( QgsUnitTypes::DistanceFeet ); expected = 88360.0918635; vLength = expLength2.evaluate( &context ); - QVERIFY( qgsDoubleNear( vLength.toDouble(), expected, 0.001 ) ); + QGSCOMPARENEAR( vLength.toDouble(), expected, 0.001 ); } void eval_geometry_wkt() diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 6ed5feec635..18c5972d3ed 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -577,8 +577,8 @@ void TestQgsGeometry::point() //toQPointF QgsPoint p11a( 5.0, 9.0 ); QPointF result = p11a.toQPointF(); - QVERIFY( qgsDoubleNear( result.x(), 5.0 ) ); - QVERIFY( qgsDoubleNear( result.y(), 9.0 ) ); + QGSCOMPARENEAR( result.x(), 5.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( result.y(), 9.0, 4 * DBL_EPSILON ); //to/from WKB QgsPoint p12( QgsWkbTypes::PointZM, 1.0, 2.0, 3.0, -4.0 ); @@ -2360,26 +2360,26 @@ void TestQgsGeometry::lineString() l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); QVERIFY( l35.closestSegment( QgsPoint( 5, 10 ), p, v, 0, 0 ) < 0 ); l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 10 ) ); - QVERIFY( qgsDoubleNear( l35.closestSegment( QgsPoint( 4, 11 ), p, v, &leftOf, 0 ), 2.0 ) ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 4, 11 ), p, v, &leftOf, 0 ), 2.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 5, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, true ); - QVERIFY( qgsDoubleNear( l35.closestSegment( QgsPoint( 8, 11 ), p, v, &leftOf, 0 ), 1.0 ) ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 11 ), p, v, &leftOf, 0 ), 1.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 8, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, true ); - QVERIFY( qgsDoubleNear( l35.closestSegment( QgsPoint( 8, 9 ), p, v, &leftOf, 0 ), 1.0 ) ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 9 ), p, v, &leftOf, 0 ), 1.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 8, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, false ); - QVERIFY( qgsDoubleNear( l35.closestSegment( QgsPoint( 11, 9 ), p, v, &leftOf, 0 ), 2.0 ) ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 11, 9 ), p, v, &leftOf, 0 ), 2.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 10, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, false ); l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 10 ) << QgsPoint( 10, 15 ) ); - QVERIFY( qgsDoubleNear( l35.closestSegment( QgsPoint( 11, 12 ), p, v, &leftOf, 0 ), 1.0 ) ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 11, 12 ), p, v, &leftOf, 0 ), 1.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 10, 12 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); QCOMPARE( leftOf, false ); @@ -2394,13 +2394,13 @@ void TestQgsGeometry::lineString() QCOMPARE( area, 1.0 ); l36.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 10 ) ); l36.sumUpArea( area ); - QVERIFY( qgsDoubleNear( area, -24 ) ); + QGSCOMPARENEAR( area, -24, 4 * DBL_EPSILON ); l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 2, 0 ) << QgsPoint( 2, 2 ) ); l36.sumUpArea( area ); - QVERIFY( qgsDoubleNear( area, -22 ) ); + QGSCOMPARENEAR( area, -22, 4 * DBL_EPSILON ); l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 2, 0 ) << QgsPoint( 2, 2 ) << QgsPoint( 0, 2 ) ); l36.sumUpArea( area ); - QVERIFY( qgsDoubleNear( area, -18 ) ); + QGSCOMPARENEAR( area, -18, 4 * DBL_EPSILON ); //boundingBox - test that bounding box is updated after every modification to the line string QgsLineString l37; @@ -2445,36 +2445,36 @@ void TestQgsGeometry::lineString() l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) ); ( void )l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash, any answer is meaningless l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ); ( void )l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ); //no crash l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 1 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 0.0 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0.0 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0.0, 4 * DBL_EPSILON ); l38.setPoints( QgsPointSequence() << QgsPoint( 1, 0 ) << QgsPoint( 0, 0 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 4.71239, 0.0001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 4.71239, 0.0001 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 4.71239, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 4.71239, 0.0001 ); l38.setPoints( QgsPointSequence() << QgsPoint( 0, 1 ) << QgsPoint( 0, 0 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 3.1416, 0.0001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.1416, 0.0001 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 3.1416, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.1416, 0.0001 ); l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0.7854, 0.0001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 0.0, 0.0001 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0.7854, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 0.0, 0.0001 ); l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.5, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 0, 2 ) ); ( void )l38.vertexAngle( QgsVertexId( 0, 0, 20 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.17809, 0.00001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0.0, 0.00001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 5.10509, 0.00001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 4.71239, 0.00001 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.17809, 0.00001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0.0, 0.00001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 5.10509, 0.00001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 4.71239, 0.00001 ); //closed line string l38.close(); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 3.92699, 0.00001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 2.35619, 0.00001 ) ); - QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 6 ) ), 2.35619, 0.00001 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 3.92699, 0.00001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 2.35619, 0.00001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 6 ) ), 2.35619, 0.00001 ); //removing the second to last vertex should remove the whole line QgsLineString l39; @@ -4473,7 +4473,7 @@ void TestQgsGeometry::compoundCurve() << QgsPoint( 1, 1 ) ); double lsArea = 0.0; ls.sumUpArea( lsArea ); - QVERIFY( qgsDoubleNear( ccArea, lsArea ) ); + QGSCOMPARENEAR( ccArea, lsArea, 4 * DBL_EPSILON ); } void TestQgsGeometry::multiPoint() diff --git a/tests/src/core/testqgsgeometryimport.cpp b/tests/src/core/testqgsgeometryimport.cpp index 2a109234641..c48fd0e8fb2 100644 --- a/tests/src/core/testqgsgeometryimport.cpp +++ b/tests/src/core/testqgsgeometryimport.cpp @@ -21,6 +21,8 @@ #include "qgstest.h" +#include "qgstestutils.h" + #include class TestQgsGeometryImport: public QObject @@ -81,8 +83,8 @@ void TestQgsGeometryImport::pointWkt() QCOMPARE( geom.wkbType(), QgsWkbTypes::Point ); QgsPointXY point = geom.asPoint(); - QVERIFY( qgsDoubleNear( point.x(), x ) ); - QVERIFY( qgsDoubleNear( point.y(), y ) ); + QGSCOMPARENEAR( point.x(), x, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( point.y(), y, 4 * DBL_EPSILON ); } void TestQgsGeometryImport::pointWkb_data() @@ -109,8 +111,8 @@ void TestQgsGeometryImport::pointWkb() QgsPointXY point = geom.asPoint(); QCOMPARE( geom.wkbType(), QgsWkbTypes::Point ); - QVERIFY( qgsDoubleNear( point.x(), x ) ); - QVERIFY( qgsDoubleNear( point.y(), y ) ); + QGSCOMPARENEAR( point.x(), x, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( point.y(), y, 4 * DBL_EPSILON ); } void TestQgsGeometryImport::pointGeos_data() @@ -137,8 +139,8 @@ void TestQgsGeometryImport::pointGeos() QgsPointXY geomPt = geom.asPoint(); - QVERIFY( qgsDoubleNear( x, geomPt.x() ) ); - QVERIFY( qgsDoubleNear( y, geomPt.y() ) ); + QGSCOMPARENEAR( x, geomPt.x(), 4 * DBL_EPSILON ); + QGSCOMPARENEAR( y, geomPt.y(), 4 * DBL_EPSILON ); } void TestQgsGeometryImport::linestringWkt_data() diff --git a/tests/src/core/testqgsgeometryutils.cpp b/tests/src/core/testqgsgeometryutils.cpp index e7c14e46dff..1d34e9dc3a2 100644 --- a/tests/src/core/testqgsgeometryutils.cpp +++ b/tests/src/core/testqgsgeometryutils.cpp @@ -190,8 +190,8 @@ void TestQgsGeometryUtils::testSegmentMidPoint() midPoint, radius, left ); QVERIFY( ok ); - QVERIFY( qgsDoubleNear( midPoint.x(), expectedX ) ); - QVERIFY( qgsDoubleNear( midPoint.y(), expectedY ) ); + QGSCOMPARENEAR( midPoint.x(), expectedX, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( midPoint.y(), expectedY, 4 * DBL_EPSILON ); } void TestQgsGeometryUtils::testCircleLength_data() @@ -218,7 +218,7 @@ void TestQgsGeometryUtils::testCircleLength() QFETCH( double, y3 ); QFETCH( double, expected ); - QVERIFY( qgsDoubleNear( expected, QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 ) ) ); + QGSCOMPARENEAR( expected, QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 ), 4 * DBL_EPSILON ); } void TestQgsGeometryUtils::testNormalizedAngle_data() @@ -243,7 +243,7 @@ void TestQgsGeometryUtils::testNormalizedAngle() { QFETCH( double, input ); QFETCH( double, expected ); - QVERIFY( qgsDoubleNear( expected, QgsGeometryUtils::normalizedAngle( input ), 0.0001 ) ); + QGSCOMPARENEAR( expected, QgsGeometryUtils::normalizedAngle( input ), 0.0001 ); } void TestQgsGeometryUtils::testLineAngle_data() @@ -275,7 +275,7 @@ void TestQgsGeometryUtils::testLineAngle() double lineAngle = QgsGeometryUtils::lineAngle( x1, y1, x2, y2 ) * 180 / M_PI; if ( expected > -99999 ) - QVERIFY( qgsDoubleNear( lineAngle, expected ) ); + QGSCOMPARENEAR( lineAngle, expected, 4 * DBL_EPSILON ); } void TestQgsGeometryUtils::testLinePerpendicularAngle_data() @@ -307,7 +307,7 @@ void TestQgsGeometryUtils::testLinePerpendicularAngle() double pAngle = QgsGeometryUtils::linePerpendicularAngle( x1, y1, x2, y2 ) * 180 / M_PI; if ( expected > -99999 ) - QVERIFY( qgsDoubleNear( pAngle, expected, 0.01 ) ); + QGSCOMPARENEAR( pAngle, expected, 0.01 ); } void TestQgsGeometryUtils::testAverageAngle_data() @@ -340,7 +340,7 @@ void TestQgsGeometryUtils::testAverageAngle() QFETCH( double, expected ); double averageAngle = QgsGeometryUtils::averageAngle( angle1 * M_PI / 180.0, angle2 * M_PI / 180.0 ) * 180.0 / M_PI; - QVERIFY( qgsDoubleNear( averageAngle, expected, 0.0000000001 ) ); + QGSCOMPARENEAR( averageAngle, expected, 0.0000000001 ); } void TestQgsGeometryUtils::testAdjacentVertices() @@ -503,9 +503,9 @@ void TestQgsGeometryUtils::testCircleCenterRadius() double radius, centerX, centerY; QgsGeometryUtils::circleCenterRadius( QgsPoint( x1, y1 ), QgsPoint( x2, y2 ), QgsPoint( x3, y3 ), radius, centerX, centerY ); - QVERIFY( qgsDoubleNear( expectedRadius, radius ) ); - QVERIFY( qgsDoubleNear( expectedCenterX, centerX ) ); - QVERIFY( qgsDoubleNear( expectedCenterY, centerY ) ); + QGSCOMPARENEAR( expectedRadius, radius, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( expectedCenterX, centerX, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( expectedCenterY, centerY, 4 * DBL_EPSILON ); } //QgsGeometryUtils::sqrDistToLine diff --git a/tests/src/core/testqgshistogram.cpp b/tests/src/core/testqgshistogram.cpp index d4f8a3fe308..2a5fa25185b 100644 --- a/tests/src/core/testqgshistogram.cpp +++ b/tests/src/core/testqgshistogram.cpp @@ -20,6 +20,7 @@ #include "qgsvectorlayer.h" #include "qgsvectordataprovider.h" #include "qgshistogram.h" +#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for QgsHistogram @@ -70,7 +71,7 @@ void TestQgsHistogram::optimalBinWidth() QgsHistogram h; h.setValues( vals ); - QVERIFY( qgsDoubleNear( h.optimalBinWidth(), 4.641, 0.001 ) ); + QGSCOMPARENEAR( h.optimalBinWidth(), 4.641, 0.001 ); } void TestQgsHistogram::optimalBinCount() diff --git a/tests/src/core/testqgslayoutitem.cpp b/tests/src/core/testqgslayoutitem.cpp index 98523573ffc..fcaf136ed24 100644 --- a/tests/src/core/testqgslayoutitem.cpp +++ b/tests/src/core/testqgslayoutitem.cpp @@ -22,6 +22,7 @@ #include "qgstest.h" #include "qgsproject.h" #include "qgsreadwritecontext.h" +#include "qgstestutils.h" #include #include #include @@ -1009,13 +1010,13 @@ void TestQgsLayoutItem::fixedSize() item->setRect( 0, 0, 5.0, 6.0 ); //temporarily set rect to random size item->attemptResize( QgsLayoutSize( 7.0, 8.0, QgsUnitTypes::LayoutPoints ) ); //check size matches fixed item size converted to mm - QVERIFY( qgsDoubleNear( item->rect().width(), 2.0 * 25.4 ) ); - QVERIFY( qgsDoubleNear( item->rect().height(), 4.0 * 25.4 ) ); + QGSCOMPARENEAR( item->rect().width(), 2.0 * 25.4, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( item->rect().height(), 4.0 * 25.4, 4 * DBL_EPSILON ); //check that setting a fixed size applies this size immediately item->updateFixedSize( QgsLayoutSize( 150, 250, QgsUnitTypes::LayoutMillimeters ) ); - QVERIFY( qgsDoubleNear( item->rect().width(), 150.0 ) ); - QVERIFY( qgsDoubleNear( item->rect().height(), 250.0 ) ); + QGSCOMPARENEAR( item->rect().width(), 150.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( item->rect().height(), 250.0, 4 * DBL_EPSILON ); } void TestQgsLayoutItem::minSize() @@ -1033,18 +1034,18 @@ void TestQgsLayoutItem::minSize() //try to resize to less than minimum size item->attemptResize( QgsLayoutSize( 1.0, 0.5, QgsUnitTypes::LayoutPoints ) ); //check size matches min item size converted to mm - QVERIFY( qgsDoubleNear( item->rect().width(), 50.0 ) ); - QVERIFY( qgsDoubleNear( item->rect().height(), 100.0 ) ); + QGSCOMPARENEAR( item->rect().width(), 50.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( item->rect().height(), 100.0, 4 * DBL_EPSILON ); //check that resize to larger than min size works item->attemptResize( QgsLayoutSize( 0.1, 0.2, QgsUnitTypes::LayoutMeters ) ); - QVERIFY( qgsDoubleNear( item->rect().width(), 100.0 ) ); - QVERIFY( qgsDoubleNear( item->rect().height(), 200.0 ) ); + QGSCOMPARENEAR( item->rect().width(), 100.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( item->rect().height(), 200.0, 4 * DBL_EPSILON ); //check that setting a minimum size applies this size immediately item->updateMinSize( QgsLayoutSize( 150, 250, QgsUnitTypes::LayoutMillimeters ) ); - QVERIFY( qgsDoubleNear( item->rect().width(), 150.0 ) ); - QVERIFY( qgsDoubleNear( item->rect().height(), 250.0 ) ); + QGSCOMPARENEAR( item->rect().width(), 150.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( item->rect().height(), 250.0, 4 * DBL_EPSILON ); //also need check that fixed size trumps min size FixedMinSizedItem *fixedMinItem = new FixedMinSizedItem( &l ); @@ -1057,8 +1058,8 @@ void TestQgsLayoutItem::minSize() //try to resize to less than minimum size fixedMinItem->attemptResize( QgsLayoutSize( 1.0, 0.5, QgsUnitTypes::LayoutPoints ) ); //check size matches fixed item size, not minimum size (converted to mm) - QVERIFY( qgsDoubleNear( fixedMinItem->rect().width(), 50.0 ) ); - QVERIFY( qgsDoubleNear( fixedMinItem->rect().height(), 90.0 ) ); + QGSCOMPARENEAR( fixedMinItem->rect().width(), 50.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( fixedMinItem->rect().height(), 90.0, 4 * DBL_EPSILON ); } void TestQgsLayoutItem::move() diff --git a/tests/src/core/testqgslayoutunits.cpp b/tests/src/core/testqgslayoutunits.cpp index 43e38822d98..1f4c2ae3efd 100644 --- a/tests/src/core/testqgslayoutunits.cpp +++ b/tests/src/core/testqgslayoutunits.cpp @@ -23,6 +23,7 @@ #include "qgslayoutsize.h" #include "qgslayoutmeasurementconverter.h" #include "qgis.h" +#include "qgstestutils.h" class TestQgsLayoutUnits : public QObject { @@ -670,13 +671,13 @@ void TestQgsLayoutUnits::conversionToInches() QCOMPARE( convertedFromPoints.units(), QgsUnitTypes::LayoutInches ); QCOMPARE( convertedFromPicas.units(), QgsUnitTypes::LayoutInches ); - QVERIFY( qgsDoubleNear( convertedFromMillimeters.length(), 0.0393701, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromCentimeters.length(), 0.3937008, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromMeters.length(), 39.3700787, 0.000001 ) ); + QGSCOMPARENEAR( convertedFromMillimeters.length(), 0.0393701, 0.000001 ); + QGSCOMPARENEAR( convertedFromCentimeters.length(), 0.3937008, 0.000001 ); + QGSCOMPARENEAR( convertedFromMeters.length(), 39.3700787, 0.000001 ); QCOMPARE( convertedFromInches.length(), 1.0 ); QCOMPARE( convertedFromFeet.length(), 12.0 ); - QVERIFY( qgsDoubleNear( convertedFromPoints.length(), 0.0138888889, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromPicas.length(), 0.166666667, 0.000001 ) ); + QGSCOMPARENEAR( convertedFromPoints.length(), 0.0138888889, 0.000001 ); + QGSCOMPARENEAR( convertedFromPicas.length(), 0.166666667, 0.000001 ); } void TestQgsLayoutUnits::conversionToFeet() @@ -706,13 +707,13 @@ void TestQgsLayoutUnits::conversionToFeet() QCOMPARE( convertedFromPoints.units(), QgsUnitTypes::LayoutFeet ); QCOMPARE( convertedFromPicas.units(), QgsUnitTypes::LayoutFeet ); - QVERIFY( qgsDoubleNear( convertedFromMillimeters.length(), 0.0032808399, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromCentimeters.length(), 0.032808399, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromMeters.length(), 3.2808399, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromInches.length(), 0.0833333, 0.000001 ) ); + QGSCOMPARENEAR( convertedFromMillimeters.length(), 0.0032808399, 0.000001 ); + QGSCOMPARENEAR( convertedFromCentimeters.length(), 0.032808399, 0.000001 ); + QGSCOMPARENEAR( convertedFromMeters.length(), 3.2808399, 0.000001 ); + QGSCOMPARENEAR( convertedFromInches.length(), 0.0833333, 0.000001 ); QCOMPARE( convertedFromFeet.length(), 1.0 ); - QVERIFY( qgsDoubleNear( convertedFromPoints.length(), 0.00115740741, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromPicas.length(), 0.0138888889, 0.000001 ) ); + QGSCOMPARENEAR( convertedFromPoints.length(), 0.00115740741, 0.000001 ); + QGSCOMPARENEAR( convertedFromPicas.length(), 0.0138888889, 0.000001 ); } void TestQgsLayoutUnits::conversionToPoints() @@ -742,13 +743,13 @@ void TestQgsLayoutUnits::conversionToPoints() QCOMPARE( convertedFromPoints.units(), QgsUnitTypes::LayoutPoints ); QCOMPARE( convertedFromPicas.units(), QgsUnitTypes::LayoutPoints ); - QVERIFY( qgsDoubleNear( convertedFromMillimeters.length(), 2.83464567, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromCentimeters.length(), 28.3464567, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromMeters.length(), 2834.64567, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromInches.length(), 72.0, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromFeet.length(), 864.0, 0.000001 ) ); + QGSCOMPARENEAR( convertedFromMillimeters.length(), 2.83464567, 0.000001 ); + QGSCOMPARENEAR( convertedFromCentimeters.length(), 28.3464567, 0.000001 ); + QGSCOMPARENEAR( convertedFromMeters.length(), 2834.64567, 0.000001 ); + QGSCOMPARENEAR( convertedFromInches.length(), 72.0, 0.000001 ); + QGSCOMPARENEAR( convertedFromFeet.length(), 864.0, 0.000001 ); QCOMPARE( convertedFromPoints.length(), 1.0 ); - QVERIFY( qgsDoubleNear( convertedFromPicas.length(), 12.0, 0.000001 ) ); + QGSCOMPARENEAR( convertedFromPicas.length(), 12.0, 0.000001 ); } void TestQgsLayoutUnits::conversionToPicas() @@ -778,12 +779,12 @@ void TestQgsLayoutUnits::conversionToPicas() QCOMPARE( convertedFromPoints.units(), QgsUnitTypes::LayoutPicas ); QCOMPARE( convertedFromPicas.units(), QgsUnitTypes::LayoutPicas ); - QVERIFY( qgsDoubleNear( convertedFromMillimeters.length(), 0.236220472, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromCentimeters.length(), 2.36220472, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromMeters.length(), 236.220472, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromInches.length(), 6.0, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromFeet.length(), 72.0, 0.000001 ) ); - QVERIFY( qgsDoubleNear( convertedFromPoints.length(), 0.0833333333, 0.000001 ) ); + QGSCOMPARENEAR( convertedFromMillimeters.length(), 0.236220472, 0.000001 ); + QGSCOMPARENEAR( convertedFromCentimeters.length(), 2.36220472, 0.000001 ); + QGSCOMPARENEAR( convertedFromMeters.length(), 236.220472, 0.000001 ); + QGSCOMPARENEAR( convertedFromInches.length(), 6.0, 0.000001 ); + QGSCOMPARENEAR( convertedFromFeet.length(), 72.0, 0.000001 ); + QGSCOMPARENEAR( convertedFromPoints.length(), 0.0833333333, 0.000001 ); QCOMPARE( convertedFromPicas.length(), 1.0 ); } @@ -803,19 +804,19 @@ void TestQgsLayoutUnits::conversionFromPixels() converter.setDpi( 300.0 ); QgsLayoutMeasurement convertedToInches = converter.convert( measurementInPixels, QgsUnitTypes::LayoutInches ); QCOMPARE( convertedToInches.units(), QgsUnitTypes::LayoutInches ); - QVERIFY( qgsDoubleNear( convertedToInches.length(), 1.0, 0.000001 ) ); + QGSCOMPARENEAR( convertedToInches.length(), 1.0, 0.000001 ); QgsLayoutMeasurement convertedToMM = converter.convert( measurementInPixels, QgsUnitTypes::LayoutMillimeters ); QCOMPARE( convertedToMM.units(), QgsUnitTypes::LayoutMillimeters ); - QVERIFY( qgsDoubleNear( convertedToMM.length(), 25.4, 0.000001 ) ); + QGSCOMPARENEAR( convertedToMM.length(), 25.4, 0.000001 ); //try with 96 dpi converter.setDpi( 96.0 ); convertedToInches = converter.convert( measurementInPixels, QgsUnitTypes::LayoutInches ); QCOMPARE( convertedToInches.units(), QgsUnitTypes::LayoutInches ); - QVERIFY( qgsDoubleNear( convertedToInches.length(), 3.125, 0.000001 ) ); + QGSCOMPARENEAR( convertedToInches.length(), 3.125, 0.000001 ); convertedToMM = converter.convert( measurementInPixels, QgsUnitTypes::LayoutMillimeters ); QCOMPARE( convertedToMM.units(), QgsUnitTypes::LayoutMillimeters ); - QVERIFY( qgsDoubleNear( convertedToMM.length(), 79.375, 0.000001 ) ); + QGSCOMPARENEAR( convertedToMM.length(), 79.375, 0.000001 ); } void TestQgsLayoutUnits::conversionToPixels() @@ -828,19 +829,19 @@ void TestQgsLayoutUnits::conversionToPixels() converter.setDpi( 300.0 ); QgsLayoutMeasurement convertedToPixels = converter.convert( measurementInInches, QgsUnitTypes::LayoutPixels ); QCOMPARE( convertedToPixels.units(), QgsUnitTypes::LayoutPixels ); - QVERIFY( qgsDoubleNear( convertedToPixels.length(), 300.0, 0.000001 ) ); + QGSCOMPARENEAR( convertedToPixels.length(), 300.0, 0.000001 ); convertedToPixels = converter.convert( measurementInMM, QgsUnitTypes::LayoutPixels ); QCOMPARE( convertedToPixels.units(), QgsUnitTypes::LayoutPixels ); - QVERIFY( qgsDoubleNear( convertedToPixels.length(), 11.811023622, 0.000001 ) ); + QGSCOMPARENEAR( convertedToPixels.length(), 11.811023622, 0.000001 ); //try with 96 dpi converter.setDpi( 96.0 ); convertedToPixels = converter.convert( measurementInInches, QgsUnitTypes::LayoutPixels ); QCOMPARE( convertedToPixels.units(), QgsUnitTypes::LayoutPixels ); - QVERIFY( qgsDoubleNear( convertedToPixels.length(), 96.0, 0.000001 ) ); + QGSCOMPARENEAR( convertedToPixels.length(), 96.0, 0.000001 ); convertedToPixels = converter.convert( measurementInMM, QgsUnitTypes::LayoutPixels ); QCOMPARE( convertedToPixels.units(), QgsUnitTypes::LayoutPixels ); - QVERIFY( qgsDoubleNear( convertedToPixels.length(), 3.77952755906, 0.000001 ) ); + QGSCOMPARENEAR( convertedToPixels.length(), 3.77952755906, 0.000001 ); } void TestQgsLayoutUnits::sizeConversion() diff --git a/tests/src/core/testqgslayoututils.cpp b/tests/src/core/testqgslayoututils.cpp index 0ed5745b8a3..da5a66dc93a 100644 --- a/tests/src/core/testqgslayoututils.cpp +++ b/tests/src/core/testqgslayoututils.cpp @@ -90,7 +90,7 @@ void TestQgsLayoutUtils::normalizedAngle() { double result = QgsLayoutUtils::normalizedAngle( ( *it ).first ); qDebug() << QString( "actual: %1 expected: %2" ).arg( result ).arg( ( *it ).second ); - QVERIFY( qgsDoubleNear( result, ( *it ).second ) ); + QGSCOMPARENEAR( result, ( *it ).second, 4 * DBL_EPSILON ); } @@ -111,7 +111,7 @@ void TestQgsLayoutUtils::normalizedAngle() { double result = QgsLayoutUtils::normalizedAngle( ( *it ).first, true ); qDebug() << QString( "actual: %1 expected: %2" ).arg( result ).arg( ( *it ).second ); - QVERIFY( qgsDoubleNear( result, ( *it ).second ) ); + QGSCOMPARENEAR( result, ( *it ).second, 4 * DBL_EPSILON ); } } diff --git a/tests/src/core/testqgspoint.cpp b/tests/src/core/testqgspoint.cpp index ecd4df82957..651cdd20c95 100644 --- a/tests/src/core/testqgspoint.cpp +++ b/tests/src/core/testqgspoint.cpp @@ -25,6 +25,7 @@ #include //header for class being tested #include +#include "qgstestutils.h" class TestQgsPointXY: public QObject { @@ -711,30 +712,30 @@ void TestQgsPointXY::vector() // length QCOMPARE( v1.length(), 0.0 ); - QVERIFY( qgsDoubleNear( v2.length(), std::sqrt( 5.0 ), 0.000000001 ) ); + QGSCOMPARENEAR( v2.length(), std::sqrt( 5.0 ), 0.000000001 ); // perpVector QCOMPARE( QgsVector( 2, 3 ).perpVector().x(), -3.0 ); QCOMPARE( QgsVector( 2, 3 ).perpVector().y(), 2.0 ); // angle - QVERIFY( qgsDoubleNear( QgsVector( 0, 1 ).angle(), M_PI_2, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 1, 0 ).angle(), 0, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( -1, 0 ).angle(), M_PI, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 0, -1 ).angle(), 3 * M_PI_2, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 0, 0 ).angle(), 0, 0.0000001 ) ); + QGSCOMPARENEAR( QgsVector( 0, 1 ).angle(), M_PI_2, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 1, 0 ).angle(), 0, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( -1, 0 ).angle(), M_PI, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 0, -1 ).angle(), 3 * M_PI_2, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 0, 0 ).angle(), 0, 0.0000001 ); - QVERIFY( qgsDoubleNear( QgsVector( 0, 1 ).angle( QgsVector( 0, 1 ) ), 0, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 1, 0 ).angle( QgsVector( 0, 1 ) ), M_PI_2, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 0, 1 ).angle( QgsVector( -1, 0 ) ), M_PI_2, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 1, 0 ).angle( QgsVector( -1, 0 ) ), M_PI, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( -1, 0 ).angle( QgsVector( 0, 0 ) ), -M_PI, 0.0000001 ) ); + QGSCOMPARENEAR( QgsVector( 0, 1 ).angle( QgsVector( 0, 1 ) ), 0, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 1, 0 ).angle( QgsVector( 0, 1 ) ), M_PI_2, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 0, 1 ).angle( QgsVector( -1, 0 ) ), M_PI_2, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 1, 0 ).angle( QgsVector( -1, 0 ) ), M_PI, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( -1, 0 ).angle( QgsVector( 0, 0 ) ), -M_PI, 0.0000001 ); // rotateBy - QVERIFY( qgsDoubleNear( QgsVector( 0, 1 ).rotateBy( M_PI_2 ).x(), -1.0, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 0, 1 ).rotateBy( M_PI_2 ).y(), 0.0, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 0, 1 ).rotateBy( M_PI ).x(), 0.0, 0.0000001 ) ); - QVERIFY( qgsDoubleNear( QgsVector( 0, 1 ).rotateBy( M_PI ).y(), -1.0, 0.0000001 ) ); + QGSCOMPARENEAR( QgsVector( 0, 1 ).rotateBy( M_PI_2 ).x(), -1.0, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 0, 1 ).rotateBy( M_PI_2 ).y(), 0.0, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 0, 1 ).rotateBy( M_PI ).x(), 0.0, 0.0000001 ); + QGSCOMPARENEAR( QgsVector( 0, 1 ).rotateBy( M_PI ).y(), -1.0, 0.0000001 ); // normalized QCOMPARE( QgsVector( 0, 2 ).normalized().x(), 0.0 ); diff --git a/tests/src/core/testqgsproperty.cpp b/tests/src/core/testqgsproperty.cpp index 905bfa36aed..f72aebcb376 100644 --- a/tests/src/core/testqgsproperty.cpp +++ b/tests/src/core/testqgsproperty.cpp @@ -804,7 +804,7 @@ void TestQgsProperty::genericNumericTransformer() //test exponential scaling t.setExponent( 1.5 ); QCOMPARE( t.value( 100 ), 10.0 ); - QVERIFY( qgsDoubleNear( t.value( 150 ), 13.5355, 0.001 ) ); + QGSCOMPARENEAR( t.value( 150 ), 13.5355, 0.001 ); QCOMPARE( t.value( 200 ), 20.0 ); //as expression @@ -1010,18 +1010,18 @@ void TestQgsProperty::sizeScaleTransformer() //test area scaling t.setType( QgsSizeScaleTransformer::Area ); QCOMPARE( t.size( 100 ), 10.0 ); - QVERIFY( qgsDoubleNear( t.size( 150 ), 17.0711, 0.001 ) ); + QGSCOMPARENEAR( t.size( 150 ), 17.0711, 0.001 ); QCOMPARE( t.size( 200 ), 20.0 ); //test flannery scaling t.setType( QgsSizeScaleTransformer::Flannery ); QCOMPARE( t.size( 100 ), 10.0 ); - QVERIFY( qgsDoubleNear( t.size( 150 ), 16.7362, 0.001 ) ); + QGSCOMPARENEAR( t.size( 150 ), 16.7362, 0.001 ); QCOMPARE( t.size( 200 ), 20.0 ); //test exponential scaling t.setType( QgsSizeScaleTransformer::Exponential ); t.setExponent( 1.5 ); QCOMPARE( t.size( 100 ), 10.0 ); - QVERIFY( qgsDoubleNear( t.size( 150 ), 13.5355, 0.001 ) ); + QGSCOMPARENEAR( t.size( 150 ), 13.5355, 0.001 ); QCOMPARE( t.size( 200 ), 20.0 ); //as expression diff --git a/tests/src/core/testqgsrasterlayer.cpp b/tests/src/core/testqgsrasterlayer.cpp index e93c7607f2f..208871193c7 100644 --- a/tests/src/core/testqgsrasterlayer.cpp +++ b/tests/src/core/testqgsrasterlayer.cpp @@ -398,11 +398,11 @@ void TestQgsRasterLayer::checkStats() //QVERIFY( myStatistics.elementCount == 100 ); QVERIFY( myStatistics.minimumValue == 0 ); QVERIFY( myStatistics.maximumValue == 9 ); - QVERIFY( qgsDoubleNear( myStatistics.mean, 4.5 ) ); + QGSCOMPARENEAR( myStatistics.mean, 4.5, 4 * DBL_EPSILON ); double stdDev = 2.87228132326901431; // TODO: verify why GDAL stdDev is so different from generic (2.88675) mReport += QStringLiteral( "stdDev = %1 expected = %2
    \n" ).arg( myStatistics.stdDev ).arg( stdDev ); - QVERIFY( qgsDoubleNear( myStatistics.stdDev, stdDev, 0.00000000000001 ) ); + QGSCOMPARENEAR( myStatistics.stdDev, stdDev, 0.00000000000001 ); mReport += QLatin1String( "

    Passed

    " ); } diff --git a/tests/src/core/testqgsrastersublayer.cpp b/tests/src/core/testqgsrastersublayer.cpp index 02a6b1ee0dd..eeb85335d6c 100644 --- a/tests/src/core/testqgsrastersublayer.cpp +++ b/tests/src/core/testqgsrastersublayer.cpp @@ -39,6 +39,7 @@ //qgis unit test includes #include +#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for raster sublayers @@ -175,8 +176,8 @@ void TestQgsRasterSubLayer::checkStats() QVERIFY( sublayer->width() == width ); QVERIFY( sublayer->height() == height ); - QVERIFY( qgsDoubleNear( myStatistics.minimumValue, min ) ); - QVERIFY( qgsDoubleNear( myStatistics.maximumValue, max ) ); + QGSCOMPARENEAR( myStatistics.minimumValue, min, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( myStatistics.maximumValue, max, 4 * DBL_EPSILON ); mReport += QLatin1String( "

    Passed

    " ); delete sublayer; } diff --git a/tests/src/core/testqgsstatisticalsummary.cpp b/tests/src/core/testqgsstatisticalsummary.cpp index b7db0080ebe..2f262ac2455 100644 --- a/tests/src/core/testqgsstatisticalsummary.cpp +++ b/tests/src/core/testqgsstatisticalsummary.cpp @@ -20,6 +20,7 @@ #include "qgsstatisticalsummary.h" #include "qgis.h" +#include "qgstestutils.h" class TestQgsStatisticSummary: public QObject { @@ -85,10 +86,10 @@ void TestQgsStatisticSummary::stats() QCOMPARE( s2.sum(), 24.0 ); QCOMPARE( s.mean(), 4.0 ); QCOMPARE( s2.mean(), 4.0 ); - QVERIFY( qgsDoubleNear( s.stDev(), 2.0816, 0.0001 ) ); - QVERIFY( qgsDoubleNear( s2.stDev(), 2.0816, 0.0001 ) ); - QVERIFY( qgsDoubleNear( s.sampleStDev(), 2.2803, 0.0001 ) ); - QVERIFY( qgsDoubleNear( s2.sampleStDev(), 2.2803, 0.0001 ) ); + QGSCOMPARENEAR( s.stDev(), 2.0816, 0.0001 ); + QGSCOMPARENEAR( s2.stDev(), 2.0816, 0.0001 ); + QGSCOMPARENEAR( s.sampleStDev(), 2.2803, 0.0001 ); + QGSCOMPARENEAR( s2.sampleStDev(), 2.2803, 0.0001 ); QCOMPARE( s.min(), 2.0 ); QCOMPARE( s2.min(), 2.0 ); @@ -248,7 +249,7 @@ void TestQgsStatisticSummary::individualStatCalculations() QCOMPARE( s.statistics(), stat ); s.calculate( values ); - QVERIFY( qgsDoubleNear( s.statistic( stat ), expected, 0.00001 ) ); + QGSCOMPARENEAR( s.statistic( stat ), expected, 0.00001 ); //also test using values added one-at-a-time QgsStatisticalSummary s2( QgsStatisticalSummary::Statistics( 0 ) ); diff --git a/tests/src/providers/testqgsgdalprovider.cpp b/tests/src/providers/testqgsgdalprovider.cpp index e2900817a12..d26f00dc7a6 100644 --- a/tests/src/providers/testqgsgdalprovider.cpp +++ b/tests/src/providers/testqgsgdalprovider.cpp @@ -29,6 +29,7 @@ #include #include #include +#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the gdal provider @@ -104,10 +105,10 @@ void TestQgsGdalProvider::warpedVrt() qDebug() << "y min: " << rp->extent().yMinimum(); qDebug() << "y max: " << rp->extent().yMaximum(); - QVERIFY( qgsDoubleNear( rp->extent().xMinimum(), 2058589, 1 ) ); - QVERIFY( qgsDoubleNear( rp->extent().xMaximum(), 3118999, 1 ) ); - QVERIFY( qgsDoubleNear( rp->extent().yMinimum(), 2281355, 1 ) ); - QVERIFY( qgsDoubleNear( rp->extent().yMaximum(), 3129683, 1 ) ); + QGSCOMPARENEAR( rp->extent().xMinimum(), 2058589, 1 ); + QGSCOMPARENEAR( rp->extent().xMaximum(), 3118999, 1 ); + QGSCOMPARENEAR( rp->extent().yMinimum(), 2281355, 1 ); + QGSCOMPARENEAR( rp->extent().yMaximum(), 3129683, 1 ); delete provider; } From 9b2e60ee1b6f975c4788250000bed8af3beffbfe Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 13:36:54 +1000 Subject: [PATCH 192/364] Remove use of old CMP0005 CMake policy Since it's deprecated on newer CMake versions --- CMakeLists.txt | 1 - cmake/FindGSL.cmake | 2 +- python/ext-libs/pyspatialite/CMakeLists.txt | 2 +- src/core/CMakeLists.txt | 6 +++--- src/plugins/globe/CMakeLists.txt | 4 ++-- src/plugins/grass/CMakeLists.txt | 8 ++++---- src/providers/grass/CMakeLists.txt | 2 +- tests/src/CMakeLists.txt | 6 +++--- tests/src/native/CMakeLists.txt | 4 ++-- tests/src/providers/CMakeLists.txt | 2 +- 10 files changed, 18 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b9ec14c13..705259f616e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -581,7 +581,6 @@ IF (WITH_CORE) #hoops to escape compiler directives then IF(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) - cmake_policy(SET CMP0005 OLD) IF(NOT "${CMAKE_VERSION}" VERSION_LESS "3.3") cmake_policy(SET CMP0063 NEW) ENDIF(NOT "${CMAKE_VERSION}" VERSION_LESS "3.3") diff --git a/cmake/FindGSL.cmake b/cmake/FindGSL.cmake index c05209d8377..c9c9de047da 100644 --- a/cmake/FindGSL.cmake +++ b/cmake/FindGSL.cmake @@ -107,7 +107,7 @@ ELSE(WIN32) # MESSAGE("DBG GSL_LINK_DIRECTORIES=${GSL_LINK_DIRECTORIES}") # MESSAGE("DBG GSL_EXE_LINKER_FLAGS=${GSL_EXE_LINKER_FLAGS}") - # ADD_DEFINITIONS("-DHAVE_GSL") + # ADD_DEFINITIONS(-DHAVE_GSL) # SET(GSL_DEFINITIONS "-DHAVE_GSL") MARK_AS_ADVANCED( GSL_CXX_FLAGS diff --git a/python/ext-libs/pyspatialite/CMakeLists.txt b/python/ext-libs/pyspatialite/CMakeLists.txt index 83fde875397..c00d30905b7 100644 --- a/python/ext-libs/pyspatialite/CMakeLists.txt +++ b/python/ext-libs/pyspatialite/CMakeLists.txt @@ -26,7 +26,7 @@ ELSE(MSVC) SET_SOURCE_FILES_PROPERTIES(${PYSPATIALITE_SRC} PROPERTIES COMPILE_FLAGS -w) ENDIF(MSVC) -ADD_DEFINITIONS(-DMODULE_NAME=\\\"spatialite.dbapi2\\\") +ADD_DEFINITIONS(-DMODULE_NAME="spatialite.dbapi2") IF (CYGWIN OR APPLE) ADD_LIBRARY(pyspatialite MODULE ${PYSPATIALITE_SRC}) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 07bac53a9f7..2be4e4354d1 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1126,13 +1126,13 @@ INCLUDE_DIRECTORIES(SYSTEM #for PAL classes IF (WIN32) - ADD_DEFINITIONS("-D_HAVE_WINDOWS_H_") + ADD_DEFINITIONS(-D_HAVE_WINDOWS_H_) ELSE (WIN32) - ADD_DEFINITIONS("-D_HAVE_PTHREAD_") + ADD_DEFINITIONS(-D_HAVE_PTHREAD_) ENDIF (WIN32) # Test data dir for QgsRenderChecker -ADD_DEFINITIONS(-DTEST_DATA_DIR="\\"${TEST_DATA_DIR}\\"") +ADD_DEFINITIONS(-DTEST_DATA_DIR="${TEST_DATA_DIR}") ############################################################# # qgis_core library diff --git a/src/plugins/globe/CMakeLists.txt b/src/plugins/globe/CMakeLists.txt index 692a45b2f35..0d3efd65b88 100644 --- a/src/plugins/globe/CMakeLists.txt +++ b/src/plugins/globe/CMakeLists.txt @@ -43,9 +43,9 @@ SET (GLOBE_PLUGIN_RCCS globe_plugin.qrc) # Build IF(WIN32) - ADD_DEFINITIONS("\"-DGLOBE_EXPORT=${DLLEXPORT}\"") + ADD_DEFINITIONS(-DGLOBE_EXPORT=${DLLEXPORT}) ELSE(WIN32) - ADD_DEFINITIONS("-DGLOBE_EXPORT=") + ADD_DEFINITIONS(-DGLOBE_EXPORT=) ENDIF(WIN32) QT5_WRAP_UI (GLOBE_PLUGIN_UIS_H ${GLOBE_PLUGIN_UIS}) diff --git a/src/plugins/grass/CMakeLists.txt b/src/plugins/grass/CMakeLists.txt index 77f3d877628..81760d15bab 100644 --- a/src/plugins/grass/CMakeLists.txt +++ b/src/plugins/grass/CMakeLists.txt @@ -1,7 +1,7 @@ ADD_SUBDIRECTORY(modules) ADD_SUBDIRECTORY(scripts) -#ADD_DEFINITIONS(-DGRASS_BASE=\\\"${GRASS_PREFIX}\\\") +#ADD_DEFINITIONS(-DGRASS_BASE="${GRASS_PREFIX}") if (HAVE_OPENPTY) ADD_DEFINITIONS(-DHAVE_OPENPTY) ENDIF (HAVE_OPENPTY) @@ -13,7 +13,7 @@ ELSE (WIN32) ENDIF (WIN32) # GRASS Direct disabled in 2.0 -#ADD_DEFINITIONS("-DGRASS_DIRECT") +#ADD_DEFINITIONS(-DGRASS_DIRECT) ######################################################## # Files @@ -133,10 +133,10 @@ IF(NOT WIN32) SET(KB_LAYOUT_DIR "${QGIS_DATA_DIR}/grass/qtermwidget/kb-layouts") - ADD_DEFINITIONS(-DKB_LAYOUT_DIR=\\\"${CMAKE_INSTALL_PREFIX}/${KB_LAYOUT_DIR}\\\") + ADD_DEFINITIONS(-DKB_LAYOUT_DIR="${CMAKE_INSTALL_PREFIX}/${KB_LAYOUT_DIR}") SET(COLORSCHEMES_DIR "${QGIS_DATA_DIR}/grass/qtermwidget/color-schemes") - ADD_DEFINITIONS(-DCOLORSCHEMES_DIR=\\\"${CMAKE_INSTALL_PREFIX}/${COLORSCHEMES_DIR}\\\") + ADD_DEFINITIONS(-DCOLORSCHEMES_DIR="${CMAKE_INSTALL_PREFIX}/${COLORSCHEMES_DIR}") ENDIF(NOT WIN32) diff --git a/src/providers/grass/CMakeLists.txt b/src/providers/grass/CMakeLists.txt index 85ef1da7ce8..e7deea4ccb2 100644 --- a/src/providers/grass/CMakeLists.txt +++ b/src/providers/grass/CMakeLists.txt @@ -1,4 +1,4 @@ -#ADD_DEFINITIONS(-DGRASS_BASE=\\\"${GRASS_PREFIX}\\\") +#ADD_DEFINITIONS(-DGRASS_BASE="${GRASS_PREFIX}") ######################################################## # Build diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index 64bab569409..4b485ec88df 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -5,15 +5,15 @@ IF (ENABLE_TESTS) # This define is used for tests that need to locate the test # data under tests/testdata in the qgis source tree. # the TEST_DATA_DIR variable is set in the top level CMakeLists.txt - ADD_DEFINITIONS(-DTEST_DATA_DIR="\\"${TEST_DATA_DIR}\\"") + ADD_DEFINITIONS(-DTEST_DATA_DIR="${TEST_DATA_DIR}") - ADD_DEFINITIONS(-DINSTALL_PREFIX="\\"${CMAKE_INSTALL_PREFIX}\\"") + ADD_DEFINITIONS(-DINSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}") # libraries # enable postgresql tests SET (ENABLE_PGTEST FALSE CACHE BOOL "Enable PostgreSQL provider tests") IF ( ENABLE_PGTEST ) - ADD_DEFINITIONS( "-DENABLE_PGTEST" ) + ADD_DEFINITIONS(-DENABLE_PGTEST) ENDIF () # because of htonl diff --git a/tests/src/native/CMakeLists.txt b/tests/src/native/CMakeLists.txt index d09955e70ce..7cd63fe7d65 100644 --- a/tests/src/native/CMakeLists.txt +++ b/tests/src/native/CMakeLists.txt @@ -28,9 +28,9 @@ ENDIF(APPLE) # This define is used for tests that need to locate the test # data under tests/testdata in the qgis source tree. # the TEST_DATA_DIR variable is set in the top level CMakeLists.txt -ADD_DEFINITIONS(-DTEST_DATA_DIR="\\"${TEST_DATA_DIR}\\"") +ADD_DEFINITIONS(-DTEST_DATA_DIR="${TEST_DATA_DIR}") -ADD_DEFINITIONS(-DINSTALL_PREFIX="\\"${CMAKE_INSTALL_PREFIX}\\"") +ADD_DEFINITIONS(-DINSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}") ############################################################# # libraries diff --git a/tests/src/providers/CMakeLists.txt b/tests/src/providers/CMakeLists.txt index 304c692fa2e..263f0af3153 100644 --- a/tests/src/providers/CMakeLists.txt +++ b/tests/src/providers/CMakeLists.txt @@ -59,7 +59,7 @@ ADD_QGIS_TEST(wcsprovidertest testqgswcsprovider.cpp) # Temporarily set to old version until server is reconfigured #SET(TEST_SERVER_URL "http://wcs.qgis.org/${COMPLETE_VERSION}") SET(TEST_SERVER_URL "http://wcs.qgis.org/1.9.0") -#ADD_DEFINITIONS(-DTEST_SERVER_URL="\\"${TEST_SERVER_URL}\\"") +#ADD_DEFINITIONS(-DTEST_SERVER_URL="${TEST_SERVER_URL}") SET_TARGET_PROPERTIES(qgis_wcsprovidertest PROPERTIES COMPILE_FLAGS "-DTEST_SERVER_URL=\\\"${TEST_SERVER_URL}\\\"" ) From 0cb52f6deae08b1cc3f7071c3e9f9e38eec97095 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 14:05:00 +1000 Subject: [PATCH 193/364] Fix UI build warning --- src/ui/qgsjoindialogbase.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/qgsjoindialogbase.ui b/src/ui/qgsjoindialogbase.ui index 0a2bd99858b..1d8256fd977 100644 --- a/src/ui/qgsjoindialogbase.ui +++ b/src/ui/qgsjoindialogbase.ui @@ -58,7 +58,7 @@ true - + From 9ac511dc8375c8893695d6d7b531713f5cf87029 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 14:43:59 +1000 Subject: [PATCH 194/364] Flip a couple of Q_FOREACHs to c++11 for loops ... just to check how bad the Q_FOREACH deprecation will be. And yep, it's horrendous. Each one takes around 10 seconds or so to port, and we've got some 2500+ remaining uses. --- src/core/annotations/qgsannotationmanager.cpp | 4 +-- src/core/auth/qgsauthcertutils.cpp | 22 ++++++------ src/core/auth/qgsauthconfig.cpp | 17 +++++----- src/core/auth/qgsauthmanager.cpp | 34 +++++++++++-------- 4 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/core/annotations/qgsannotationmanager.cpp b/src/core/annotations/qgsannotationmanager.cpp index 7659dedaaaa..19cc4b56380 100644 --- a/src/core/annotations/qgsannotationmanager.cpp +++ b/src/core/annotations/qgsannotationmanager.cpp @@ -62,7 +62,7 @@ bool QgsAnnotationManager::removeAnnotation( QgsAnnotation *annotation ) void QgsAnnotationManager::clear() { - Q_FOREACH ( QgsAnnotation *a, mAnnotations ) + for ( auto *a : qgsAsConst( mAnnotations ) ) { removeAnnotation( a ); } @@ -76,7 +76,7 @@ QList QgsAnnotationManager::annotations() const QList QgsAnnotationManager::cloneAnnotations() const { QList results; - Q_FOREACH ( const QgsAnnotation *a, mAnnotations ) + for ( const auto *a : qgsAsConst( mAnnotations ) ) { results << a->clone(); } diff --git a/src/core/auth/qgsauthcertutils.cpp b/src/core/auth/qgsauthcertutils.cpp index a9d302854e9..d9ec3168e07 100644 --- a/src/core/auth/qgsauthcertutils.cpp +++ b/src/core/auth/qgsauthcertutils.cpp @@ -48,7 +48,7 @@ QString QgsAuthCertUtils::getSslProtocolName( QSsl::SslProtocol protocol ) QMap QgsAuthCertUtils::mapDigestToCerts( const QList &certs ) { QMap digestmap; - Q_FOREACH ( const QSslCertificate &cert, certs ) + for ( const auto &cert : certs ) { digestmap.insert( shaHexForCert( cert ), cert ); } @@ -58,7 +58,7 @@ QMap QgsAuthCertUtils::mapDigestToCerts( const QList > QgsAuthCertUtils::certsGroupedByOrg( const QList &certs ) { QMap< QString, QList > orgcerts; - Q_FOREACH ( const QSslCertificate &cert, certs ) + for ( const auto &cert : certs ) { QString org( SSL_SUBJECT_INFO( cert, QSslCertificate::Organization ) ); if ( org.isEmpty() ) @@ -72,7 +72,7 @@ QMap > QgsAuthCertUtils::certsGroupedByOrg( cons QMap QgsAuthCertUtils::mapDigestToSslConfigs( const QList &configs ) { QMap digestmap; - Q_FOREACH ( const QgsAuthConfigSslServer &config, configs ) + for ( const auto &config : configs ) { digestmap.insert( shaHexForCert( config.sslCertificate() ), config ); } @@ -82,7 +82,7 @@ QMap QgsAuthCertUtils::mapDigestToSslConfigs( c QMap > QgsAuthCertUtils::sslConfigsGroupedByOrg( const QList &configs ) { QMap< QString, QList > orgconfigs; - Q_FOREACH ( const QgsAuthConfigSslServer &config, configs ) + for ( const auto &config : configs ) { QString org( SSL_SUBJECT_INFO( config.sslCertificate(), QSslCertificate::Organization ) ); @@ -439,7 +439,7 @@ QCA::CertificateCollection QgsAuthCertUtils::qtCertsToQcaCollection( const QList if ( QgsAuthManager::instance()->isDisabled() ) return qcacoll; - Q_FOREACH ( const QSslCertificate &cert, certs ) + for ( const auto &cert : certs ) { QCA::Certificate qcacert( qtCertToQcaCert( cert ) ); if ( !qcacert.isNull() ) @@ -622,8 +622,8 @@ QList QgsAuthCertUtils::certificateUsageTypes( usages << QgsAuthCertUtils::CertAuthorityUsage; } - QList certconsts = qcacert.constraints(); - Q_FOREACH ( const QCA::ConstraintType &certconst, certconsts ) + const QList certconsts = qcacert.constraints(); + for ( const auto &certconst : certconsts ) { if ( certconst.known() == QCA::KeyCertificateSign ) { @@ -722,8 +722,8 @@ bool QgsAuthCertUtils::certificateIsSslServer( const QSslCertificate &cert ) return false; } - QList certconsts = qcacert.constraints(); - Q_FOREACH ( QCA::ConstraintType certconst, certconsts ) + const QList certconsts = qcacert.constraints(); + for ( const auto & certconst, certconsts ) { if ( certconst.known() == QCA::KeyCertificateSign ) { @@ -737,7 +737,7 @@ bool QgsAuthCertUtils::certificateIsSslServer( const QSslCertificate &cert ) bool serverauth = false; bool dsignature = false; bool keyencrypt = false; - Q_FOREACH ( QCA::ConstraintType certconst, certconsts ) + for ( const auto &certconst : certconsts ) { if ( certconst.known() == QCA::DigitalSignature ) { @@ -791,7 +791,7 @@ bool QgsAuthCertUtils::certificateIsSslServer( const QSslCertificate &cert ) { return false; } - Q_FOREACH ( QCA::ConstraintType certconst, certconsts ) + for ( const auto &certconst : certconsts ) { if ( certconst.known() == QCA::EncipherOnly ) { diff --git a/src/core/auth/qgsauthconfig.cpp b/src/core/auth/qgsauthconfig.cpp index b07bc20f0cb..128f6ae6854 100644 --- a/src/core/auth/qgsauthconfig.cpp +++ b/src/core/auth/qgsauthconfig.cpp @@ -91,9 +91,9 @@ void QgsAuthMethodConfig::loadConfigString( const QString &configstr ) return; } - QStringList confs( configstr.split( CONFIG_SEP ) ); + const QStringList confs( configstr.split( CONFIG_SEP ) ); - Q_FOREACH ( const QString &conf, confs ) + for ( const auto &conf : confs ) { if ( conf.contains( CONFIG_KEY_SEP ) ) { @@ -255,7 +255,7 @@ const QgsPkiBundle QgsPkiBundle::fromPkcs12Paths( const QString &bundlepath, QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( bundlepath, passarray, &res, QStringLiteral( "qca-ossl" ) ) ); if ( res == QCA::ConvertGood && !bundle.isNull() ) { - QCA::CertificateChain cert_chain( bundle.certificateChain() ); + const QCA::CertificateChain cert_chain( bundle.certificateChain() ); QSslCertificate cert( cert_chain.primary().toPEM().toLatin1() ); if ( !cert.isNull() ) { @@ -270,7 +270,7 @@ const QgsPkiBundle QgsPkiBundle::fromPkcs12Paths( const QString &bundlepath, if ( cert_chain.size() > 1 ) { QList ca_chain; - Q_FOREACH ( const QCA::Certificate &ca_cert, cert_chain ) + for ( const auto &ca_cert : cert_chain ) { if ( ca_cert != cert_chain.primary() ) { @@ -366,7 +366,8 @@ QgsAuthConfigSslServer::QgsAuthConfigSslServer() const QList QgsAuthConfigSslServer::sslIgnoredErrors() const { QList errors; - Q_FOREACH ( QSslError::SslError errenum, sslIgnoredErrorEnums() ) + const QList ignoredErrors = sslIgnoredErrorEnums(); + for ( QSslError::SslError errenum : ignoredErrors ) { errors << QSslError( errenum ); } @@ -381,7 +382,7 @@ const QString QgsAuthConfigSslServer::configString() const configlist << QString::number( static_cast< int >( mSslProtocol ) ); QStringList errs; - Q_FOREACH ( const QSslError::SslError &err, mSslIgnoredErrors ) + for ( auto err : mSslIgnoredErrors ) { errs << QString::number( static_cast< int >( err ) ); } @@ -408,8 +409,8 @@ void QgsAuthConfigSslServer::loadConfigString( const QString &config ) mSslProtocol = static_cast< QSsl::SslProtocol >( configlist.at( 2 ).toInt() ); mSslIgnoredErrors.clear(); - QStringList errs( configlist.at( 3 ).split( QStringLiteral( "~~" ) ) ); - Q_FOREACH ( const QString &err, errs ) + const QStringList errs( configlist.at( 3 ).split( QStringLiteral( "~~" ) ) ); + for ( const auto &err : errs ) { mSslIgnoredErrors.append( static_cast< QSslError::SslError >( err.toInt() ) ); } diff --git a/src/core/auth/qgsauthmanager.cpp b/src/core/auth/qgsauthmanager.cpp index e918b5dd083..8659461c914 100644 --- a/src/core/auth/qgsauthmanager.cpp +++ b/src/core/auth/qgsauthmanager.cpp @@ -148,9 +148,9 @@ bool QgsAuthManager::init( const QString &pluginPath ) } QgsDebugMsg( "Prioritizing qca-ossl over all other QCA providers..." ); - QCA::ProviderList provds = QCA::providers(); + const QCA::ProviderList provds = QCA::providers(); QStringList prlist; - Q_FOREACH ( QCA::Provider *p, provds ) + for ( QCA::Provider *p : provds ) { QString pn = p->name(); int pr = 0; @@ -780,7 +780,8 @@ bool QgsAuthManager::registerCoreAuthMethods() qDeleteAll( mAuthMethods ); mAuthMethods.clear(); - Q_FOREACH ( const QString &authMethodKey, QgsAuthMethodRegistry::instance()->authMethodList() ) + const QStringList methods = QgsAuthMethodRegistry::instance()->authMethodList(); + for ( const auto &authMethodKey : methods ) { mAuthMethods.insert( authMethodKey, QgsAuthMethodRegistry::instance()->authMethod( authMethodKey ).release() ); } @@ -2086,7 +2087,7 @@ void QgsAuthManager::dumpIgnoredSslErrorsCache_() while ( i != mIgnoredSslErrorsCache.constEnd() ) { QStringList errs; - Q_FOREACH ( QSslError::SslError err, i.value() ) + for ( auto err : i.value() ) { errs << QgsAuthCertUtils::sslErrorEnumString( err ); } @@ -2150,7 +2151,7 @@ bool QgsAuthManager::updateIgnoredSslErrorsCache( const QString &shahostport, co } QSet errs; - Q_FOREACH ( const QSslError &error, errors ) + for ( const auto &error : errors ) { if ( error.error() == QSslError::NoError ) continue; @@ -2240,7 +2241,7 @@ bool QgsAuthManager::storeCertAuthorities( const QList &certs ) return false; } - Q_FOREACH ( const QSslCertificate &cert, certs ) + for ( const auto &cert : certs ) { if ( !storeCertAuthority( cert ) ) return false; @@ -2410,7 +2411,7 @@ const QList QgsAuthManager::getExtraFileCAs() filecerts = QgsAuthCertUtils::certsFromFile( cafile ); } // only CAs or certs capable of signing other certs are allowed - Q_FOREACH ( const QSslCertificate &cert, filecerts ) + for ( const auto &cert : qgsAsConst( filecerts ) ) { if ( !allowinvalid.toBool() && !cert.isValid() ) { @@ -2547,7 +2548,7 @@ bool QgsAuthManager::removeCertTrustPolicies( const QList &cert return false; } - Q_FOREACH ( const QSslCertificate &cert, certs ) + for ( const auto &cert : certs ) { if ( !removeCertTrustPolicy( cert ) ) return false; @@ -2730,11 +2731,11 @@ bool QgsAuthManager::rebuildTrustedCaCertsCache() const QByteArray QgsAuthManager::getTrustedCaCertsPemText() { QByteArray capem; - QList certs( getTrustedCaCertsCache() ); + const QList certs( getTrustedCaCertsCache() ); if ( !certs.isEmpty() ) { QStringList certslist; - Q_FOREACH ( const QSslCertificate &cert, certs ) + for ( const auto &cert : certs ) { certslist << cert.toPem(); } @@ -2762,7 +2763,8 @@ void QgsAuthManager::clearAllCachedConfigs() if ( isDisabled() ) return; - Q_FOREACH ( QString authcfg, configIds() ) + const QStringList ids = configIds(); + for ( const auto &authcfg : ids ) { clearCachedConfig( authcfg ); } @@ -3307,7 +3309,8 @@ bool QgsAuthManager::reencryptAllAuthenticationConfigs( const QString &prevpass, return false; bool res = true; - Q_FOREACH ( QString configid, configIds() ) + const QStringList ids = configIds(); + for ( const auto &configid : ids ) { res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv ); } @@ -3395,7 +3398,7 @@ bool QgsAuthManager::reencryptAllAuthenticationSettings( const QString &prevpass QStringList encryptedsettings; encryptedsettings << ""; - Q_FOREACH ( const QString &sett, encryptedsettings ) + for ( const auto & sett, qgsAsConst( encryptedsettings ) ) { if ( sett.isEmpty() || !existsAuthSetting( sett ) ) continue; @@ -3468,7 +3471,8 @@ bool QgsAuthManager::reencryptAllAuthenticationIdentities( const QString &prevpa return false; bool res = true; - Q_FOREACH ( const QString &identid, getCertIdentityIds() ) + const QStringList ids = getCertIdentityIds(); + for ( const auto &identid : ids ) { res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv ); } @@ -3649,7 +3653,7 @@ bool QgsAuthManager::authDbTransactionQuery( QSqlQuery *query ) const void QgsAuthManager::insertCaCertInCache( QgsAuthCertUtils::CaCertSource source, const QList &certs ) { - Q_FOREACH ( const QSslCertificate &cert, certs ) + for ( const auto &cert : certs ) { mCaCertsCache.insert( QgsAuthCertUtils::shaHexForCert( cert ), QPair( source, cert ) ); From 51170aec0ce0a2acfbb6805dc962dbf691bba356 Mon Sep 17 00:00:00 2001 From: nirvn Date: Tue, 29 Aug 2017 14:40:50 +0700 Subject: [PATCH 195/364] Revert "highlight fix" due to regression (fixes #16824) This reverts commit 46596914e11dae13cd4d986941246c12f30aa0d9. --- src/gui/qgshighlight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgshighlight.cpp b/src/gui/qgshighlight.cpp index 17ce2e60778..7819bcea4f1 100644 --- a/src/gui/qgshighlight.cpp +++ b/src/gui/qgshighlight.cpp @@ -257,7 +257,7 @@ void QgsHighlight::paintPolygon( QPainter *p, QgsPolygon polygon ) void QgsHighlight::updatePosition() { - QgsMapCanvasItem::updatePosition(); + // nothing to do here... } void QgsHighlight::paint( QPainter *p ) From 1fddfcf62206e409186a3baf222965e22f306bf6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:22:20 +1000 Subject: [PATCH 196/364] Flip Q_ENUMS to Q_ENUM See https://woboq.com/blog/q_enum.html for rationale --- python/core/qgsdataitem.sip | 1 + python/core/qgsdataprovider.sip | 1 - python/gui/attributetable/qgsdualview.sip | 1 + python/gui/qgsfontbutton.sip | 2 +- scripts/sipify.pl | 2 +- src/core/auth/qgsauthmanager.h | 2 +- src/core/qgsapplication.cpp | 1 - src/core/qgsdataitem.h | 10 ++++++---- src/core/qgsdataprovider.h | 3 +-- src/core/qgsmessagelog.cpp | 1 - src/core/qgsmessagelog.h | 2 +- src/core/qgsunittypes.h | 5 +++++ src/gui/attributetable/qgsdualview.h | 3 ++- src/gui/qgscolorbutton.h | 2 +- src/gui/qgsfilterlineedit.h | 2 +- src/gui/qgsfloatingwidget.h | 2 +- src/gui/qgsfontbutton.h | 4 ++-- tests/src/analysis/testqgsrastercalculator.cpp | 1 - 18 files changed, 25 insertions(+), 20 deletions(-) diff --git a/python/core/qgsdataitem.sip b/python/core/qgsdataitem.sip index c77dd090b3b..a5d06be8890 100644 --- a/python/core/qgsdataitem.sip +++ b/python/core/qgsdataitem.sip @@ -52,6 +52,7 @@ class QgsDataItem : QObject Project }; + QgsDataItem( QgsDataItem::Type type, QgsDataItem *parent /TransferThis/, const QString &name, const QString &path ); %Docstring Create new data item. diff --git a/python/core/qgsdataprovider.sip b/python/core/qgsdataprovider.sip index 3119328ef1c..2520a007545 100644 --- a/python/core/qgsdataprovider.sip +++ b/python/core/qgsdataprovider.sip @@ -46,7 +46,6 @@ class QgsDataProvider : QObject %End public: - enum DataCapability { NoDataCapabilities, diff --git a/python/gui/attributetable/qgsdualview.sip b/python/gui/attributetable/qgsdualview.sip index e3d1ef20e80..5699eed4a08 100644 --- a/python/gui/attributetable/qgsdualview.sip +++ b/python/gui/attributetable/qgsdualview.sip @@ -34,6 +34,7 @@ class QgsDualView : QStackedWidget AttributeEditor }; + explicit QgsDualView( QWidget *parent /TransferThis/ = 0 ); %Docstring Constructor diff --git a/python/gui/qgsfontbutton.sip b/python/gui/qgsfontbutton.sip index a8f70c22c3b..c4accbb8f23 100644 --- a/python/gui/qgsfontbutton.sip +++ b/python/gui/qgsfontbutton.sip @@ -31,13 +31,13 @@ class QgsFontButton : QToolButton %End public: - enum Mode { ModeTextRenderer, ModeQFont, }; + QgsFontButton( QWidget *parent /TransferThis/ = 0, const QString &dialogTitle = QString() ); %Docstring Construct a new font button. diff --git a/scripts/sipify.pl b/scripts/sipify.pl index 0aa88fbf408..3e6ffeb78b7 100755 --- a/scripts/sipify.pl +++ b/scripts/sipify.pl @@ -464,7 +464,7 @@ while ($LINE_IDX < $LINE_COUNT){ next; } # Skip Q_OBJECT, Q_PROPERTY, Q_ENUM, Q_GADGET etc. - if ($LINE =~ m/^\s*Q_(OBJECT|ENUMS|PROPERTY|GADGET|DECLARE_METATYPE|DECLARE_TYPEINFO|DECL_DEPRECATED|NOWARN_DEPRECATED_(PUSH|POP)).*?$/){ + if ($LINE =~ m/^\s*Q_(OBJECT|ENUMS|ENUM|PROPERTY|GADGET|DECLARE_METATYPE|DECLARE_TYPEINFO|DECL_DEPRECATED|NOWARN_DEPRECATED_(PUSH|POP)).*?$/){ next; } diff --git a/src/core/auth/qgsauthmanager.h b/src/core/auth/qgsauthmanager.h index d5c513cb313..815aacc942f 100644 --- a/src/core/auth/qgsauthmanager.h +++ b/src/core/auth/qgsauthmanager.h @@ -60,7 +60,6 @@ class QTimer; class CORE_EXPORT QgsAuthManager : public QObject { Q_OBJECT - Q_ENUMS( MessageLevel ) public: @@ -71,6 +70,7 @@ class CORE_EXPORT QgsAuthManager : public QObject WARNING = 1, CRITICAL = 2 }; + Q_ENUM( MessageLevel ); /** Enforce singleton pattern * \note To set up the manager instance and initialize everything use QgsAuthManager::instance()->init() diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 455ff6adfe3..8946ec2a9a1 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -146,7 +146,6 @@ void QgsApplication::init( QString profileFolder ) qRegisterMetaType( "QgsGeometry::Error" ); qRegisterMetaType( "QgsProcessingFeatureSourceDefinition" ); qRegisterMetaType( "QgsProcessingOutputLayerDefinition" ); - qRegisterMetaType( "QgsUnitTypes::LayoutUnit" ); qRegisterMetaType( "QgsFeatureIds" ); QString prefixPath( getenv( "QGIS_PREFIX_PATH" ) ? getenv( "QGIS_PREFIX_PATH" ) : applicationDirPath() ); diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index ab79ac3ed7e..8d0c873f945 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -69,8 +69,7 @@ class CORE_EXPORT QgsDataItem : public QObject #endif Q_OBJECT - Q_ENUMS( Type ) - Q_ENUMS( State ) + public: enum Type { @@ -82,6 +81,8 @@ class CORE_EXPORT QgsDataItem : public QObject Project //!< Represents a QGIS project }; + Q_ENUM( Type ); + //! Create new data item. QgsDataItem( QgsDataItem::Type type, QgsDataItem *parent SIP_TRANSFERTHIS, const QString &name, const QString &path ); virtual ~QgsDataItem(); @@ -100,6 +101,7 @@ class CORE_EXPORT QgsDataItem : public QObject Populating, //!< Creating children in separate thread (populating or refreshing) Populated //!< Children created }; + Q_ENUM( State ); //! \since QGIS 2.8 State state() const; @@ -328,7 +330,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( QgsDataItem::Capabilities ) class CORE_EXPORT QgsLayerItem : public QgsDataItem { Q_OBJECT - Q_ENUMS( LayerType ) + public: enum LayerType { @@ -344,7 +346,7 @@ class CORE_EXPORT QgsLayerItem : public QgsDataItem Plugin //!< Added in 2.10 }; - Q_ENUMS( LayerType ) + Q_ENUM( LayerType ); QgsLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, const QString &providerKey ); diff --git a/src/core/qgsdataprovider.h b/src/core/qgsdataprovider.h index d48ab5a0f50..a0327cb1f85 100644 --- a/src/core/qgsdataprovider.h +++ b/src/core/qgsdataprovider.h @@ -67,8 +67,6 @@ class CORE_EXPORT QgsDataProvider : public QObject public: - Q_ENUMS( DataCapability ) - enum DataCapability { NoDataCapabilities = 0, @@ -77,6 +75,7 @@ class CORE_EXPORT QgsDataProvider : public QObject Database = 1 << 2, Net = 1 << 3 // Internet source }; + Q_ENUM( DataCapability ); /** * Properties are used to pass custom configuration options into data providers. diff --git a/src/core/qgsmessagelog.cpp b/src/core/qgsmessagelog.cpp index 093c53efed3..feca690963f 100644 --- a/src/core/qgsmessagelog.cpp +++ b/src/core/qgsmessagelog.cpp @@ -25,7 +25,6 @@ class QgsMessageLogConsole; QgsMessageLog::QgsMessageLog() : QObject() { - qRegisterMetaType< QgsMessageLog::MessageLevel >( "QgsMessageLog::MessageLevel" ); } void QgsMessageLog::logMessage( const QString &message, const QString &tag, QgsMessageLog::MessageLevel level ) diff --git a/src/core/qgsmessagelog.h b/src/core/qgsmessagelog.h index 1233dad7049..8af7aa0fff5 100644 --- a/src/core/qgsmessagelog.h +++ b/src/core/qgsmessagelog.h @@ -36,7 +36,6 @@ class CORE_EXPORT QgsMessageLog : public QObject { Q_OBJECT - Q_ENUMS( MessageLevel ) public: @@ -48,6 +47,7 @@ class CORE_EXPORT QgsMessageLog : public QObject CRITICAL = 2, NONE = 3 }; + Q_ENUM( MessageLevel ); QgsMessageLog(); diff --git a/src/core/qgsunittypes.h b/src/core/qgsunittypes.h index 47b1368d4d1..42c1aa03cf9 100644 --- a/src/core/qgsunittypes.h +++ b/src/core/qgsunittypes.h @@ -52,6 +52,7 @@ class CORE_EXPORT QgsUnitTypes DistanceMillimeters, //!< Millimeters DistanceUnknownUnit, //!< Unknown distance unit }; + Q_ENUM( DistanceUnit ); /** Types of distance units */ @@ -78,6 +79,7 @@ class CORE_EXPORT QgsUnitTypes AreaSquareMillimeters, //! Square millimeters AreaUnknownUnit, //!< Unknown areal unit }; + Q_ENUM( AreaUnit ); //! Units of angles enum AngleUnit @@ -90,6 +92,7 @@ class CORE_EXPORT QgsUnitTypes AngleTurn, //!< Turn/revolutions AngleUnknownUnit, //!< Unknown angle unit }; + Q_ENUM( AngleUnit ); //! Rendering size units enum RenderUnit @@ -103,6 +106,7 @@ class CORE_EXPORT QgsUnitTypes RenderUnknownUnit, //!< Mixed or unknown units RenderMetersInMapUnits, //!< Meters value as Map units }; + Q_ENUM( RenderUnit ); //! Layout measurement units enum LayoutUnit @@ -116,6 +120,7 @@ class CORE_EXPORT QgsUnitTypes LayoutPicas, //!< Typographic picas LayoutPixels //!< Pixels }; + Q_ENUM( LayoutUnit ); //! Types of layout units enum LayoutUnitType diff --git a/src/gui/attributetable/qgsdualview.h b/src/gui/attributetable/qgsdualview.h index fd3575ce752..9b8b16d8c82 100644 --- a/src/gui/attributetable/qgsdualview.h +++ b/src/gui/attributetable/qgsdualview.h @@ -42,7 +42,6 @@ class QgsScrollArea; class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBase { Q_OBJECT - Q_ENUMS( ViewMode ) public: @@ -67,6 +66,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas AttributeEditor = 1 }; + Q_ENUM( ViewMode ); + /** * \brief Constructor * \param parent The parent widget diff --git a/src/gui/qgscolorbutton.h b/src/gui/qgscolorbutton.h index 10568a2ddb8..4ee2ee6d879 100644 --- a/src/gui/qgscolorbutton.h +++ b/src/gui/qgscolorbutton.h @@ -46,7 +46,6 @@ class GUI_EXPORT QgsColorButton : public QToolButton Q_OBJECT - Q_ENUMS( Behavior ) Q_PROPERTY( QString colorDialogTitle READ colorDialogTitle WRITE setColorDialogTitle ) Q_PROPERTY( bool acceptLiveUpdates READ acceptLiveUpdates WRITE setAcceptLiveUpdates ) Q_PROPERTY( QColor color READ color WRITE setColor ) @@ -67,6 +66,7 @@ class GUI_EXPORT QgsColorButton : public QToolButton ShowDialog = 0, //!< Show a color picker dialog when clicked SignalOnly //!< Emit colorClicked signal only, no dialog }; + Q_ENUM( Behavior ); /** Construct a new color ramp button. * Use \a parent to attach a parent QWidget to the dialog. diff --git a/src/gui/qgsfilterlineedit.h b/src/gui/qgsfilterlineedit.h index 20c97f69b47..4d4b8a830a3 100644 --- a/src/gui/qgsfilterlineedit.h +++ b/src/gui/qgsfilterlineedit.h @@ -47,7 +47,6 @@ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit #endif Q_OBJECT - Q_ENUMS( ClearMode ) Q_PROPERTY( ClearMode clearMode READ clearMode WRITE setClearMode ) Q_PROPERTY( QString nullValue READ nullValue WRITE setNullValue ) Q_PROPERTY( QString defaultValue READ defaultValue WRITE setDefaultValue ) @@ -63,6 +62,7 @@ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit ClearToNull = 0, //!< Reset value to null ClearToDefault, //!< Reset value to default value (see defaultValue() ) }; + Q_ENUM( ClearMode ); /** Constructor for QgsFilterLineEdit. * \param parent parent widget diff --git a/src/gui/qgsfloatingwidget.h b/src/gui/qgsfloatingwidget.h index 083f511a608..5916b0ba956 100644 --- a/src/gui/qgsfloatingwidget.h +++ b/src/gui/qgsfloatingwidget.h @@ -32,7 +32,6 @@ class QgsFloatingWidgetEventFilter; class GUI_EXPORT QgsFloatingWidget: public QWidget { Q_OBJECT - Q_ENUMS( AnchorPoint ) Q_PROPERTY( QWidget *anchorWidget READ anchorWidget WRITE setAnchorWidget NOTIFY anchorWidgetChanged ) Q_PROPERTY( AnchorPoint anchorPoint READ anchorPoint WRITE setAnchorPoint NOTIFY anchorPointChanged ) Q_PROPERTY( AnchorPoint anchorWidgetPoint READ anchorWidgetPoint WRITE setAnchorWidgetPoint NOTIFY anchorWidgetPointChanged ) @@ -52,6 +51,7 @@ class GUI_EXPORT QgsFloatingWidget: public QWidget BottomMiddle, //!< Bottom center of widget BottomRight, //!< Bottom-right of widget }; + Q_ENUM( AnchorPoint ); /** Constructor for QgsFloatingWidget. * \param parent parent widget diff --git a/src/gui/qgsfontbutton.h b/src/gui/qgsfontbutton.h index 19b45fb322f..6e396e4e4f6 100644 --- a/src/gui/qgsfontbutton.h +++ b/src/gui/qgsfontbutton.h @@ -50,8 +50,6 @@ class GUI_EXPORT QgsFontButton : public QToolButton public: - Q_ENUMS( Mode ) - //! Available button modes. enum Mode { @@ -59,6 +57,8 @@ class GUI_EXPORT QgsFontButton : public QToolButton ModeQFont, //!< Configure font settings for use with QFont objects }; + Q_ENUM( Mode ); + /** * Construct a new font button. * Use \a parent to attach a parent QWidget to the dialog. diff --git a/tests/src/analysis/testqgsrastercalculator.cpp b/tests/src/analysis/testqgsrastercalculator.cpp index a83c634344b..030775af9ee 100644 --- a/tests/src/analysis/testqgsrastercalculator.cpp +++ b/tests/src/analysis/testqgsrastercalculator.cpp @@ -25,7 +25,6 @@ Email : nyall dot dawson at gmail dot com Q_DECLARE_METATYPE( QgsRasterCalcNode::Operator ) - class TestQgsRasterCalculator : public QObject { Q_OBJECT From 972a1fe45916f631807f89f3a02071dfe3242aee Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:25:00 +1000 Subject: [PATCH 197/364] Fix mixed uses of const/non-const iterator methods Thanks for Clazy --- .../interpolation/DualEdgeTriangulation.cc | 6 ++--- .../network/qgsvectorlayerdirector.cpp | 6 ++--- src/app/composer/qgscomposer.cpp | 6 ++--- src/app/composer/qgscompositionwidget.cpp | 2 +- src/app/qgsabout.cpp | 4 ++-- src/app/qgscustomization.cpp | 2 +- src/app/qgsidentifyresultsdialog.cpp | 4 ++-- src/app/qgsmaptoolidentifyaction.cpp | 4 ++-- src/core/geometry/qgsgeometryeditutils.cpp | 2 +- src/core/qgsofflineediting.cpp | 6 ++--- src/core/qgsogcutils.cpp | 18 +++++++-------- src/core/qgsvectordataprovider.cpp | 2 +- src/core/qgsvectorlayer.cpp | 7 +++--- src/core/qgsvectorlayereditbuffer.cpp | 2 +- src/core/raster/qgscolorrampshader.cpp | 6 ++--- .../editorwidgets/qgsvaluemapconfigdlg.cpp | 2 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 2 +- src/gui/qgsowssourceselect.cpp | 4 ++-- src/plugins/gps_importer/qgsgpsplugin.cpp | 2 +- src/plugins/topology/checkDock.cpp | 2 +- src/plugins/topology/topolTest.cpp | 22 +++++++++---------- src/providers/arcgisrest/qgsafsshareddata.cpp | 4 ++-- .../qgsarcgisservicesourceselect.cpp | 4 ++-- .../qgsdelimitedtextfeatureiterator.cpp | 2 +- src/providers/gdal/qgsgdalprovider.cpp | 20 ++++++++--------- src/providers/gpx/gpsdata.cpp | 12 +++++----- src/providers/ogr/qgsogrconnpool.h | 4 ++-- src/providers/ogr/qgsogrfeatureiterator.cpp | 2 +- src/providers/ogr/qgsogrprovider.cpp | 2 +- src/providers/wfs/qgswfssourceselect.cpp | 4 ++-- src/providers/wms/qgswmssourceselect.cpp | 4 ++-- 31 files changed, 85 insertions(+), 84 deletions(-) diff --git a/src/analysis/interpolation/DualEdgeTriangulation.cc b/src/analysis/interpolation/DualEdgeTriangulation.cc index 627480b13a5..0c1ac63021f 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.cc +++ b/src/analysis/interpolation/DualEdgeTriangulation.cc @@ -1545,10 +1545,10 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) //setNext and setPoint for the forced edge because this would disturb the building of 'leftpoly' and 'rightpoly' otherwise - mHalfEdge[leftPolygon.first()]->setNext( ( *( ++( leftiter = leftPolygon.begin() ) ) ) ); + mHalfEdge[leftPolygon.first()]->setNext( ( *( ++( leftiter = leftPolygon.constBegin() ) ) ) ); mHalfEdge[leftPolygon.first()]->setPoint( p2 ); mHalfEdge[leftPolygon.last()]->setNext( firstedge ); - mHalfEdge[rightPolygon.first()]->setNext( ( *( ++( rightiter = rightPolygon.begin() ) ) ) ); + mHalfEdge[rightPolygon.first()]->setNext( ( *( ++( rightiter = rightPolygon.constBegin() ) ) ) ); mHalfEdge[rightPolygon.first()]->setPoint( p1 ); mHalfEdge[rightPolygon.last()]->setNext( dualfirstedge ); @@ -1556,7 +1556,7 @@ int DualEdgeTriangulation::insertForcedSegment( int p1, int p2, bool breakline ) triangulatePolygon( &rightPolygon, &freelist, dualfirstedge ); //optimisation of the new edges - for ( iter = crossedEdges.begin(); iter != crossedEdges.end(); ++iter ) + for ( iter = crossedEdges.constBegin(); iter != crossedEdges.constEnd(); ++iter ) { checkSwap( ( *( iter ) ), 0 ); } diff --git a/src/analysis/network/qgsvectorlayerdirector.cpp b/src/analysis/network/qgsvectorlayerdirector.cpp index 3a5d57c1813..a10b10f9007 100644 --- a/src/analysis/network/qgsvectorlayerdirector.cpp +++ b/src/analysis/network/qgsvectorlayerdirector.cpp @@ -255,10 +255,10 @@ void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const QList< QgsNetworkStrategy * >::const_iterator it; QgsAttributeList::const_iterator it2; - for ( it = mStrategies.begin(); it != mStrategies.end(); ++it ) + for ( it = mStrategies.constBegin(); it != mStrategies.constEnd(); ++it ) { QgsAttributeList tmp = ( *it )->requiredAttributes(); - for ( it2 = tmp.begin(); it2 != tmp.end(); ++it2 ) + for ( it2 = tmp.constBegin(); it2 != tmp.constEnd(); ++it2 ) { tmpAttr.push_back( *it2 ); } @@ -266,7 +266,7 @@ void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const std::sort( tmpAttr.begin(), tmpAttr.end() ); int lastAttrId = -1; - for ( it2 = tmpAttr.begin(); it2 != tmpAttr.end(); ++it2 ) + for ( it2 = tmpAttr.constBegin(); it2 != tmpAttr.constEnd(); ++it2 ) { if ( *it2 == lastAttrId ) { diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp index 007599d8755..31386670776 100644 --- a/src/app/composer/qgscomposer.cpp +++ b/src/app/composer/qgscomposer.cpp @@ -2696,8 +2696,8 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode ) && !mComposition->gridVisible() ) items.pop_back(); QgsItemTempHider itemsHider( items ); int composerItemLayerIdx = 0; - QList::const_iterator it = items.begin(); - for ( unsigned svgLayerId = 1; it != items.end(); ++svgLayerId ) + QList::const_iterator it = items.constBegin(); + for ( unsigned svgLayerId = 1; it != items.constEnd(); ++svgLayerId ) { itemsHider.hideAll(); QgsComposerItem *composerItem = dynamic_cast( *it ); @@ -2711,7 +2711,7 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode ) else { // show all items until the next item that renders on a separate layer - for ( ; it != items.end(); ++it ) + for ( ; it != items.constEnd(); ++it ) { composerItem = dynamic_cast( *it ); if ( composerItem && composerItem->numberExportLayers() ) diff --git a/src/app/composer/qgscompositionwidget.cpp b/src/app/composer/qgscompositionwidget.cpp index 478a27468d2..0aaaf083842 100644 --- a/src/app/composer/qgscompositionwidget.cpp +++ b/src/app/composer/qgscompositionwidget.cpp @@ -269,7 +269,7 @@ void QgsCompositionWidget::createPaperEntries() ; mPaperSizeComboBox->addItem( tr( "Custom" ) ); - for ( QList::const_iterator it = formats.begin(); it != formats.end(); ++it ) + for ( QList::const_iterator it = formats.constBegin(); it != formats.constEnd(); ++it ) { mPaperSizeComboBox->addItem( it->mName ); mPaperMap.insert( it->mName, *it ); diff --git a/src/app/qgsabout.cpp b/src/app/qgsabout.cpp index 33d2652fbf6..23d0c080361 100644 --- a/src/app/qgsabout.cpp +++ b/src/app/qgsabout.cpp @@ -255,8 +255,8 @@ void QgsAbout::setPluginInfo() myString += QApplication::libraryPaths().join( QStringLiteral( "
    " ) ); myString += QLatin1String( "
      \n
    1. \n" ); QList myImageFormats = QImageReader::supportedImageFormats(); - QList::const_iterator myIterator = myImageFormats.begin(); - while ( myIterator != myImageFormats.end() ) + QList::const_iterator myIterator = myImageFormats.constBegin(); + while ( myIterator != myImageFormats.constEnd() ) { QString myFormat = ( *myIterator ).data(); myString += myFormat + "
    2. \n
    3. "; diff --git a/src/app/qgscustomization.cpp b/src/app/qgscustomization.cpp index 37bd5f3bbad..38d3005cb70 100644 --- a/src/app/qgscustomization.cpp +++ b/src/app/qgscustomization.cpp @@ -966,7 +966,7 @@ void QgsCustomization::loadDefault() QStringList keys = fileSettings.allKeys(); QgsDebugMsg( QString( "size = %1" ).arg( keys.size() ) ); QStringList::const_iterator i; - for ( i = keys.begin(); i != keys.end(); ++i ) + for ( i = keys.constBegin(); i != keys.constEnd(); ++i ) { QString p( *i ); diff --git a/src/app/qgsidentifyresultsdialog.cpp b/src/app/qgsidentifyresultsdialog.cpp index a62d32a641c..d7be19d0cc2 100644 --- a/src/app/qgsidentifyresultsdialog.cpp +++ b/src/app/qgsidentifyresultsdialog.cpp @@ -1780,7 +1780,7 @@ void QgsIdentifyResultsDialog::copyFeatureAttributes() const QgsFields &fields = vlayer->fields(); - for ( QgsAttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); ++it ) + for ( QgsAttributeMap::const_iterator it = attributes.constBegin(); it != attributes.constEnd(); ++it ) { int attrIdx = it.key(); if ( attrIdx < 0 || attrIdx >= fields.count() ) @@ -1972,4 +1972,4 @@ void QgsIdentifyResultsDialogMapLayerAction::execute() void QgsIdentifyResultsDialog::showHelp() { QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#identify" ) ); -} \ No newline at end of file +} diff --git a/src/app/qgsmaptoolidentifyaction.cpp b/src/app/qgsmaptoolidentifyaction.cpp index 26855c51626..92ff9d43e20 100644 --- a/src/app/qgsmaptoolidentifyaction.cpp +++ b/src/app/qgsmaptoolidentifyaction.cpp @@ -146,7 +146,7 @@ void QgsMapToolIdentifyAction::canvasReleaseEvent( QgsMapMouseEvent *e ) resultsDialog()->QDialog::show(); QList::const_iterator result; - for ( result = results.begin(); result != results.end(); ++result ) + for ( result = results.constBegin(); result != results.constEnd(); ++result ) { resultsDialog()->addFeature( *result ); } @@ -164,7 +164,7 @@ void QgsMapToolIdentifyAction::handleChangedRasterResults( QList // Add new result after raster format change QgsDebugMsg( QString( "%1 raster results" ).arg( results.size() ) ); QList::const_iterator rresult; - for ( rresult = results.begin(); rresult != results.end(); ++rresult ) + for ( rresult = results.constBegin(); rresult != results.constEnd(); ++rresult ) { if ( rresult->mLayer->type() == QgsMapLayer::RasterLayer ) { diff --git a/src/core/geometry/qgsgeometryeditutils.cpp b/src/core/geometry/qgsgeometryeditutils.cpp index e7aedc1e1b7..e035a62d85c 100644 --- a/src/core/geometry/qgsgeometryeditutils.cpp +++ b/src/core/geometry/qgsgeometryeditutils.cpp @@ -248,7 +248,7 @@ std::unique_ptr QgsGeometryEditUtils::avoidIntersections( c Q_FOREACH ( QgsVectorLayer *currentLayer, avoidIntersectionsLayers ) { QgsFeatureIds ignoreIds; - QHash >::const_iterator ignoreIt = ignoreFeatures.find( currentLayer ); + QHash >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer ); if ( ignoreIt != ignoreFeatures.constEnd() ) ignoreIds = ignoreIt.value(); diff --git a/src/core/qgsofflineediting.cpp b/src/core/qgsofflineediting.cpp index 3e1296508ec..4a016e2d2d1 100644 --- a/src/core/qgsofflineediting.cpp +++ b/src/core/qgsofflineediting.cpp @@ -800,7 +800,7 @@ void QgsOfflineEditing::applyFeaturesRemoved( QgsVectorLayer *remoteLayer, sqlit emit progressModeSet( QgsOfflineEditing::RemoveFeatures, values.size() ); int i = 1; - for ( QgsFeatureIds::const_iterator it = values.begin(); it != values.end(); ++it ) + for ( QgsFeatureIds::const_iterator it = values.constBegin(); it != values.constEnd(); ++it ) { QgsFeatureId fid = remoteFid( db, layerId, *it ); remoteLayer->deleteFeature( fid ); @@ -883,7 +883,7 @@ void QgsOfflineEditing::updateFidLookup( QgsVectorLayer *remoteLayer, sqlite3 *d // add new fid lookups i = 0; sqlExec( db, QStringLiteral( "BEGIN" ) ); - for ( QMap::const_iterator it = newRemoteFids.begin(); it != newRemoteFids.end(); ++it ) + for ( QMap::const_iterator it = newRemoteFids.constBegin(); it != newRemoteFids.constEnd(); ++it ) { addFidLookup( db, layerId, newOfflineFids.at( i++ ), it.key() ); } @@ -1346,7 +1346,7 @@ void QgsOfflineEditing::committedAttributeValuesChanges( const QString &qgisLaye continue; } QgsAttributeMap attrMap = cit.value(); - for ( QgsAttributeMap::const_iterator it = attrMap.begin(); it != attrMap.end(); ++it ) + for ( QgsAttributeMap::const_iterator it = attrMap.constBegin(); it != attrMap.constEnd(); ++it ) { QString sql = QStringLiteral( "INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" ) .arg( layerId ) diff --git a/src/core/qgsogcutils.cpp b/src/core/qgsogcutils.cpp index ec7a041a500..822b95c5b4b 100644 --- a/src/core/qgsogcutils.cpp +++ b/src/core/qgsogcutils.cpp @@ -175,7 +175,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLPoint( const QDomElement &geometryElemen return QgsGeometry(); } - QgsPolyline::const_iterator point_it = pointCoordinate.begin(); + QgsPolyline::const_iterator point_it = pointCoordinate.constBegin(); char e = htonl( 1 ) != 1; double x = point_it->x(); double y = point_it->y(); @@ -244,7 +244,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLLineString( const QDomElement &geometryE wkbPosition += sizeof( int ); QgsPolyline::const_iterator iter; - for ( iter = lineCoordinates.begin(); iter != lineCoordinates.end(); ++iter ) + for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter ) { x = iter->x(); y = iter->y(); @@ -340,7 +340,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLPolygon( const QDomElement &geometryElem return QgsGeometry(); int npoints = 0;//total number of points - for ( QgsMultiPolyline::const_iterator it = ringCoordinates.begin(); it != ringCoordinates.end(); ++it ) + for ( QgsMultiPolyline::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it ) { npoints += it->size(); } @@ -362,7 +362,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLPolygon( const QDomElement &geometryElem wkbPosition += sizeof( int ); memcpy( &( wkb )[wkbPosition], &nrings, sizeof( int ) ); wkbPosition += sizeof( int ); - for ( QgsMultiPolyline::const_iterator it = ringCoordinates.begin(); it != ringCoordinates.end(); ++it ) + for ( QgsMultiPolyline::const_iterator it = ringCoordinates.constBegin(); it != ringCoordinates.constEnd(); ++it ) { nPointsInRing = it->size(); memcpy( &( wkb )[wkbPosition], &nPointsInRing, sizeof( int ) ); @@ -465,7 +465,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLMultiPoint( const QDomElement &geometryE memcpy( &( wkb )[wkbPosition], &nPoints, sizeof( int ) ); wkbPosition += sizeof( int ); type = QgsWkbTypes::Point; - for ( QgsPolyline::const_iterator it = pointList.begin(); it != pointList.end(); ++it ) + for ( QgsPolyline::const_iterator it = pointList.constBegin(); it != pointList.constEnd(); ++it ) { memcpy( &( wkb )[wkbPosition], &e, 1 ); wkbPosition += 1; @@ -584,7 +584,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLMultiLineString( const QDomElement &geom //calculate the required wkb size int size = ( lineCoordinates.size() + 1 ) * ( 1 + 2 * sizeof( int ) ); - for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.begin(); it != lineCoordinates.end(); ++it ) + for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it ) { size += it->size() * 2 * sizeof( double ); } @@ -604,7 +604,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLMultiLineString( const QDomElement &geom memcpy( &( wkb )[wkbPosition], &nLines, sizeof( int ) ); wkbPosition += sizeof( int ); type = QgsWkbTypes::LineString; - for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.begin(); it != lineCoordinates.end(); ++it ) + for ( QList< QgsPolyline >::const_iterator it = lineCoordinates.constBegin(); it != lineCoordinates.constEnd(); ++it ) { memcpy( &( wkb )[wkbPosition], &e, 1 ); wkbPosition += 1; @@ -777,7 +777,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLMultiPolygon( const QDomElement &geometr int size = 1 + 2 * sizeof( int ); //calculate the wkb size - for ( QgsMultiPolygon::const_iterator it = multiPolygonPoints.begin(); it != multiPolygonPoints.end(); ++it ) + for ( QgsMultiPolygon::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it ) { size += 1 + 2 * sizeof( int ); for ( QgsPolygon::const_iterator iter = it->begin(); iter != it->end(); ++iter ) @@ -805,7 +805,7 @@ QgsGeometry QgsOgcUtils::geometryFromGMLMultiPolygon( const QDomElement &geometr type = QgsWkbTypes::Polygon; - for ( QgsMultiPolygon::const_iterator it = multiPolygonPoints.begin(); it != multiPolygonPoints.end(); ++it ) + for ( QgsMultiPolygon::const_iterator it = multiPolygonPoints.constBegin(); it != multiPolygonPoints.constEnd(); ++it ) { memcpy( &( wkb )[wkbPosition], &e, 1 ); wkbPosition += 1; diff --git a/src/core/qgsvectordataprovider.cpp b/src/core/qgsvectordataprovider.cpp index 1f11973495f..4f0e9fdcbe0 100644 --- a/src/core/qgsvectordataprovider.cpp +++ b/src/core/qgsvectordataprovider.cpp @@ -527,7 +527,7 @@ void QgsVectorDataProvider::fillMinMaxCache() const while ( fi.nextFeature( f ) ) { QgsAttributes attrs = f.attributes(); - for ( QgsAttributeList::const_iterator it = keys.begin(); it != keys.end(); ++it ) + for ( QgsAttributeList::const_iterator it = keys.constBegin(); it != keys.constEnd(); ++it ) { const QVariant &varValue = attrs.at( *it ); diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 80ed6d6af9b..fab196aa9b3 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -4283,11 +4283,12 @@ QMap< QgsFieldConstraints::Constraint, QgsFieldConstraints::ConstraintStrength> QString name = mFields.at( fieldIndex ).name(); - for ( QPair< QString, QgsFieldConstraints::Constraint > p : mFieldConstraintStrength.keys() ) + QMap< QPair< QString, QgsFieldConstraints::Constraint >, QgsFieldConstraints::ConstraintStrength >::const_iterator conIt = mFieldConstraintStrength.constBegin(); + for ( ; conIt != mFieldConstraintStrength.constEnd(); ++conIt ) { - if ( p.first == name ) + if ( conIt.key().first == name ) { - m[ p.second ] = mFieldConstraintStrength.value( p ); + m[ conIt.key().second ] = mFieldConstraintStrength.value( conIt.key() ); } } diff --git a/src/core/qgsvectorlayereditbuffer.cpp b/src/core/qgsvectorlayereditbuffer.cpp index d1d65692519..989dcb9a374 100644 --- a/src/core/qgsvectorlayereditbuffer.cpp +++ b/src/core/qgsvectorlayereditbuffer.cpp @@ -770,7 +770,7 @@ void QgsVectorLayerEditBuffer::handleAttributeDeleted( int index ) void QgsVectorLayerEditBuffer::updateAttributeMapIndex( QgsAttributeMap &map, int index, int offset ) const { QgsAttributeMap updatedMap; - for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it ) + for ( QgsAttributeMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) { int attrIndex = it.key(); updatedMap.insert( attrIndex < index ? attrIndex : attrIndex + offset, it.value() ); diff --git a/src/core/raster/qgscolorrampshader.cpp b/src/core/raster/qgscolorrampshader.cpp index 76cf8c91ae0..838efb1a0cd 100644 --- a/src/core/raster/qgscolorrampshader.cpp +++ b/src/core/raster/qgscolorrampshader.cpp @@ -281,15 +281,15 @@ void QgsColorRampShader::classifyColorRamp( const int classes, const int band, c } } - QList::const_iterator value_it = entryValues.begin(); - QVector::const_iterator color_it = entryColors.begin(); + QList::const_iterator value_it = entryValues.constBegin(); + QVector::const_iterator color_it = entryColors.constBegin(); // calculate a reasonable number of decimals to display double maxabs = std::log10( std::max( std::fabs( max ), std::fabs( min ) ) ); int nDecimals = std::round( std::max( 3.0 + maxabs - std::log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) ); QList colorRampItems; - for ( ; value_it != entryValues.end(); ++value_it, ++color_it ) + for ( ; value_it != entryValues.constEnd(); ++value_it, ++color_it ) { QgsColorRampShader::ColorRampItem newColorRampItem; newColorRampItem.value = *value_it; diff --git a/src/gui/editorwidgets/qgsvaluemapconfigdlg.cpp b/src/gui/editorwidgets/qgsvaluemapconfigdlg.cpp index ca0c1203043..acf9236724f 100644 --- a/src/gui/editorwidgets/qgsvaluemapconfigdlg.cpp +++ b/src/gui/editorwidgets/qgsvaluemapconfigdlg.cpp @@ -81,7 +81,7 @@ void QgsValueMapConfigDlg::setConfig( const QVariantMap &config ) int row = 0; QVariantMap values = config.value( QStringLiteral( "map" ) ).toMap(); - for ( QVariantMap::ConstIterator mit = values.begin(); mit != values.end(); mit++, row++ ) + for ( QVariantMap::ConstIterator mit = values.constBegin(); mit != values.constEnd(); mit++, row++ ) { if ( mit.value().isNull() ) setRow( row, mit.key(), QString() ); diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 25c432f1b84..23e1862bef8 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -167,7 +167,7 @@ QgsAdvancedDigitizingDockWidget::QgsAdvancedDigitizingDockWidget( QgsMapCanvas * commonAngles << QPair( 30, trUtf8( "Snap to 30° angles" ) ); commonAngles << QPair( 45, trUtf8( "Snap to 45° angles" ) ); commonAngles << QPair( 90, trUtf8( "Snap to 90° angles" ) ); - for ( QList< QPair< int, QString > >::const_iterator it = commonAngles.begin(); it != commonAngles.end(); ++it ) + for ( QList< QPair< int, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it ) { QAction *action = new QAction( it->second, menu ); action->setCheckable( true ); diff --git a/src/gui/qgsowssourceselect.cpp b/src/gui/qgsowssourceselect.cpp index 3d37843786d..c9306708670 100644 --- a/src/gui/qgsowssourceselect.cpp +++ b/src/gui/qgsowssourceselect.cpp @@ -413,7 +413,7 @@ void QgsOWSSourceSelect::populateCrs() break; // save first CRS in case the current CRS is not available - if ( it == mSelectedLayersCRSs.begin() ) + if ( it == mSelectedLayersCRSs.constBegin() ) defaultCRS = *it; // prefer value of DEFAULT_GEO_EPSG_CRS_ID if available @@ -421,7 +421,7 @@ void QgsOWSSourceSelect::populateCrs() defaultCRS = *it; } - if ( it == mSelectedLayersCRSs.end() ) + if ( it == mSelectedLayersCRSs.constEnd() ) { // not found mSelectedCRS = defaultCRS; diff --git a/src/plugins/gps_importer/qgsgpsplugin.cpp b/src/plugins/gps_importer/qgsgpsplugin.cpp index ad1afa730ed..8f17464ce8b 100644 --- a/src/plugins/gps_importer/qgsgpsplugin.cpp +++ b/src/plugins/gps_importer/qgsgpsplugin.cpp @@ -642,7 +642,7 @@ void QgsGPSPlugin::setupBabel() QStringList::const_iterator iter; - for ( iter = deviceNames.begin(); iter != deviceNames.end(); ++iter ) + for ( iter = deviceNames.constBegin(); iter != deviceNames.constEnd(); ++iter ) { QString wptDownload = settings. value( QStringLiteral( "/Plugin-GPS/devices/%1/wptdownload" ). diff --git a/src/plugins/topology/checkDock.cpp b/src/plugins/topology/checkDock.cpp index c4625f83fb1..cdccc1b543d 100644 --- a/src/plugins/topology/checkDock.cpp +++ b/src/plugins/topology/checkDock.cpp @@ -418,7 +418,7 @@ void checkDock::validateSelected() void checkDock::toggleErrorMarker() { QList::const_iterator it; - for ( it = mRbErrorMarkers.begin(); it != mRbErrorMarkers.end(); ++it ) + for ( it = mRbErrorMarkers.constBegin(); it != mRbErrorMarkers.constEnd(); ++it ) { QgsRubberBand *rb = *it; if ( mToggleRubberband->isChecked() ) diff --git a/src/plugins/topology/topolTest.cpp b/src/plugins/topology/topolTest.cpp index 05b12016841..557d3aebd69 100644 --- a/src/plugins/topology/topolTest.cpp +++ b/src/plugins/topology/topolTest.cpp @@ -387,8 +387,8 @@ ErrorList topolTest::checkDuplicates( double tolerance, QgsVectorLayer *layer1, QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); - QList::ConstIterator crossingIdsEnd = crossingIds.end(); + QList::ConstIterator cit = crossingIds.constBegin(); + QList::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool duplicate = false; @@ -504,7 +504,7 @@ ErrorList topolTest::checkOverlaps( double tolerance, QgsVectorLayer *layer1, Qg QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); + QList::ConstIterator cit = crossingIds.begin(); QList::ConstIterator crossingIdsEnd = crossingIds.end(); bool duplicate = false; @@ -919,7 +919,7 @@ ErrorList topolTest::checkPointCoveredBySegment( double tolerance, QgsVectorLaye QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); + QList::ConstIterator cit = crossingIds.begin(); QList::ConstIterator crossingIdsEnd = crossingIds.end(); bool touched = false; @@ -1148,7 +1148,7 @@ ErrorList topolTest::checkOverlapWithLayer( double tolerance, QgsVectorLayer *la QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); + QList::ConstIterator cit = crossingIds.begin(); QList::ConstIterator crossingIdsEnd = crossingIds.end(); for ( ; cit != crossingIdsEnd; ++cit ) { @@ -1240,8 +1240,8 @@ ErrorList topolTest::checkPointCoveredByLineEnds( double tolerance, QgsVectorLay QgsRectangle bb = g1.boundingBox(); QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); - QList::ConstIterator crossingIdsEnd = crossingIds.end(); + QList::ConstIterator cit = crossingIds.constBegin(); + QList::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { @@ -1322,7 +1322,7 @@ ErrorList topolTest::checkyLineEndsCoveredByPoints( double tolerance, QgsVectorL QgsRectangle bb = g1.boundingBox(); QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); + QList::ConstIterator cit = crossingIds.begin(); QList::ConstIterator crossingIdsEnd = crossingIds.end(); bool touched = false; @@ -1416,7 +1416,7 @@ ErrorList topolTest::checkPointInPolygon( double tolerance, QgsVectorLayer *laye QgsRectangle bb = g1.boundingBox(); QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); + QList::ConstIterator cit = crossingIds.begin(); QList::ConstIterator crossingIdsEnd = crossingIds.end(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) @@ -1490,8 +1490,8 @@ ErrorList topolTest::checkPolygonContainsPoint( double tolerance, QgsVectorLayer QgsRectangle bb = g1.boundingBox(); QList crossingIds; crossingIds = index->intersects( bb ); - QList::Iterator cit = crossingIds.begin(); - QList::ConstIterator crossingIdsEnd = crossingIds.end(); + QList::ConstIterator cit = crossingIds.begin(); + QList::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { diff --git a/src/providers/arcgisrest/qgsafsshareddata.cpp b/src/providers/arcgisrest/qgsafsshareddata.cpp index 6544dded612..a418f61da7a 100644 --- a/src/providers/arcgisrest/qgsafsshareddata.cpp +++ b/src/providers/arcgisrest/qgsafsshareddata.cpp @@ -26,8 +26,8 @@ QgsAfsSharedData::QgsAfsSharedData() bool QgsAfsSharedData::getFeature( QgsFeatureId id, QgsFeature &f, bool fetchGeometry, const QList & /*fetchAttributes*/, const QgsRectangle &filterRect ) { // If cached, return cached feature - QMap::const_iterator it = mCache.find( id ); - if ( it != mCache.end() ) + QMap::const_iterator it = mCache.constFind( id ); + if ( it != mCache.constEnd() ) { f = it.value(); return filterRect.isNull() || f.geometry().intersects( filterRect ); diff --git a/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp b/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp index 86a1622c737..e2d90605215 100644 --- a/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp +++ b/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp @@ -391,8 +391,8 @@ void QgsArcGisServiceSourceSelect::changeCrsFilter() QString currentTypename = currentIndex.sibling( currentIndex.row(), 1 ).data().toString(); QgsDebugMsg( QString( "the current typename is: %1" ).arg( currentTypename ) ); - QMap::const_iterator crsIterator = mAvailableCRS.find( currentTypename ); - if ( crsIterator != mAvailableCRS.end() ) + QMap::const_iterator crsIterator = mAvailableCRS.constFind( currentTypename ); + if ( crsIterator != mAvailableCRS.constEnd() ) { QSet crsNames; foreach ( const QString &crsName, crsIterator.value() ) diff --git a/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp b/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp index 35843bc0c75..818081a8232 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp @@ -360,7 +360,7 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature &feature ) if ( ! mTestSubset && ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ) { QgsAttributeList attrs = mRequest.subsetOfAttributes(); - for ( QgsAttributeList::const_iterator i = attrs.begin(); i != attrs.end(); ++i ) + for ( QgsAttributeList::const_iterator i = attrs.constBegin(); i != attrs.constEnd(); ++i ) { int fieldIdx = *i; fetchAttribute( feature, fieldIdx, tokens ); diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 5c906c35818..99770ecf33c 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -910,8 +910,8 @@ QString QgsGdalProvider::generateBandName( int bandNumber ) const QStringList metadata = cStringList2Q_( GDALmetadata ); QStringList dimExtraValues; QMap< QString, QString > unitsMap; - for ( QStringList::const_iterator i = metadata.begin(); - i != metadata.end(); ++i ) + for ( QStringList::const_iterator i = metadata.constBegin(); + i != metadata.constEnd(); ++i ) { QString val( *i ); if ( !val.startsWith( QLatin1String( "NETCDF_DIM_EXTRA" ) ) && !val.contains( QLatin1String( "#units=" ) ) ) @@ -937,15 +937,15 @@ QString QgsGdalProvider::generateBandName( int bandNumber ) const if ( GDALmetadata ) { metadata = cStringList2Q_( GDALmetadata ); - for ( QStringList::const_iterator i = metadata.begin(); - i != metadata.end(); ++i ) + for ( QStringList::const_iterator i = metadata.constBegin(); + i != metadata.constEnd(); ++i ) { QString val( *i ); if ( !val.startsWith( QLatin1String( "NETCDF_DIM_" ) ) ) continue; QStringList values = val.split( '=' ); - for ( QStringList::const_iterator j = dimExtraValues.begin(); - j != dimExtraValues.end(); ++j ) + for ( QStringList::const_iterator j = dimExtraValues.constBegin(); + j != dimExtraValues.constEnd(); ++j ) { QString dim = ( *j ); if ( values.at( 0 ) != "NETCDF_DIM_" + dim ) @@ -1628,8 +1628,8 @@ QString QgsGdalProvider::buildPyramids( const QList &rasterPyr mGdalDataset = mGdalBaseDataset; // restore former configOptions - for ( QgsStringMap::const_iterator it = myConfigOptionsOld.begin(); - it != myConfigOptionsOld.end(); ++it ) + for ( QgsStringMap::const_iterator it = myConfigOptionsOld.constBegin(); + it != myConfigOptionsOld.constEnd(); ++it ) { QByteArray key = it.key().toLocal8Bit(); QByteArray value = it.value().toLocal8Bit(); @@ -1655,8 +1655,8 @@ QString QgsGdalProvider::buildPyramids( const QList &rasterPyr } // restore former configOptions - for ( QgsStringMap::const_iterator it = myConfigOptionsOld.begin(); - it != myConfigOptionsOld.end(); ++it ) + for ( QgsStringMap::const_iterator it = myConfigOptionsOld.constBegin(); + it != myConfigOptionsOld.constEnd(); ++it ) { QByteArray key = it.key().toLocal8Bit(); QByteArray value = it.value().toLocal8Bit(); diff --git a/src/providers/gpx/gpsdata.cpp b/src/providers/gpx/gpsdata.cpp index 82abea5386a..2682b6fc1af 100644 --- a/src/providers/gpx/gpsdata.cpp +++ b/src/providers/gpx/gpsdata.cpp @@ -292,10 +292,10 @@ void QgsGPSData::removeWaypoints( const QgsFeatureIds &ids ) { QList ids2 = ids.toList(); std::sort( ids2.begin(), ids2.end() ); - QList::const_iterator iter = ids2.begin(); + QList::const_iterator iter = ids2.constBegin(); WaypointIterator wIter; for ( wIter = waypoints.begin(); - wIter != waypoints.end() && iter != ids2.end(); ) + wIter != waypoints.end() && iter != ids2.constEnd(); ) { WaypointIterator tmpIter = wIter; ++tmpIter; @@ -313,9 +313,9 @@ void QgsGPSData::removeRoutes( const QgsFeatureIds &ids ) { QList ids2 = ids.toList(); std::sort( ids2.begin(), ids2.end() ); - QList::const_iterator iter = ids2.begin(); + QList::const_iterator iter = ids2.constBegin(); RouteIterator rIter; - for ( rIter = routes.begin(); rIter != routes.end() && iter != ids2.end(); ) + for ( rIter = routes.begin(); rIter != routes.end() && iter != ids2.constEnd(); ) { RouteIterator tmpIter = rIter; ++tmpIter; @@ -333,9 +333,9 @@ void QgsGPSData::removeTracks( const QgsFeatureIds &ids ) { QList ids2 = ids.toList(); std::sort( ids2.begin(), ids2.end() ); - QList::const_iterator iter = ids2.begin(); + QList::const_iterator iter = ids2.constBegin(); TrackIterator tIter; - for ( tIter = tracks.begin(); tIter != tracks.end() && iter != ids2.end(); ) + for ( tIter = tracks.begin(); tIter != tracks.end() && iter != ids2.constEnd(); ) { TrackIterator tmpIter = tIter; ++tmpIter; diff --git a/src/providers/ogr/qgsogrconnpool.h b/src/providers/ogr/qgsogrconnpool.h index bb503d540d8..919d5de1650 100644 --- a/src/providers/ogr/qgsogrconnpool.h +++ b/src/providers/ogr/qgsogrconnpool.h @@ -122,8 +122,8 @@ class QgsOgrConnPool : public QgsConnectionPoolref(); mMutex.unlock(); diff --git a/src/providers/ogr/qgsogrfeatureiterator.cpp b/src/providers/ogr/qgsogrfeatureiterator.cpp index b293c140a0b..60d82e0fa5d 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.cpp +++ b/src/providers/ogr/qgsogrfeatureiterator.cpp @@ -370,7 +370,7 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature &feature ) if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { QgsAttributeList attrs = mRequest.subsetOfAttributes(); - for ( QgsAttributeList::const_iterator it = attrs.begin(); it != attrs.end(); ++it ) + for ( QgsAttributeList::const_iterator it = attrs.constBegin(); it != attrs.constEnd(); ++it ) { getFeatureAttribute( fet, feature, *it ); } diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index d61842c5855..3c55c8e5f8d 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -316,7 +316,7 @@ QgsVectorLayerExporter::ExportError QgsOgrProvider::createEmptyLayer( const QStr } } - for ( QMap::const_iterator attrIt = attrIdxMap.begin(); attrIt != attrIdxMap.end(); ++attrIt ) + for ( QMap::const_iterator attrIt = attrIdxMap.constBegin(); attrIt != attrIdxMap.constEnd(); ++attrIt ) { oldToNewAttrIdxMap->insert( attrIt.key(), *attrIt + ( firstFieldIsFid ? 1 : 0 ) ); } diff --git a/src/providers/wfs/qgswfssourceselect.cpp b/src/providers/wfs/qgswfssourceselect.cpp index 43986870eae..11560917296 100644 --- a/src/providers/wfs/qgswfssourceselect.cpp +++ b/src/providers/wfs/qgswfssourceselect.cpp @@ -694,8 +694,8 @@ void QgsWFSSourceSelect::changeCRSFilter() QString currentTypename = currentIndex.sibling( currentIndex.row(), MODEL_IDX_NAME ).data().toString(); QgsDebugMsg( QString( "the current typename is: %1" ).arg( currentTypename ) ); - QMap::const_iterator crsIterator = mAvailableCRS.find( currentTypename ); - if ( crsIterator != mAvailableCRS.end() ) + QMap::const_iterator crsIterator = mAvailableCRS.constFind( currentTypename ); + if ( crsIterator != mAvailableCRS.constEnd() ) { QSet crsNames( crsIterator->toSet() ); diff --git a/src/providers/wms/qgswmssourceselect.cpp b/src/providers/wms/qgswmssourceselect.cpp index 2f8549bf857..0e2fe94719d 100644 --- a/src/providers/wms/qgswmssourceselect.cpp +++ b/src/providers/wms/qgswmssourceselect.cpp @@ -830,7 +830,7 @@ void QgsWMSSourceSelect::on_lstLayers_itemSelectionChanged() break; // save first CRS in case the current CRS is not available - if ( it == mCRSs.begin() ) + if ( it == mCRSs.constBegin() ) defaultCRS = *it; // prefer value of DEFAULT_GEO_EPSG_CRS_ID if available @@ -838,7 +838,7 @@ void QgsWMSSourceSelect::on_lstLayers_itemSelectionChanged() defaultCRS = *it; } - if ( it == mCRSs.end() ) + if ( it == mCRSs.constEnd() ) { // not found mCRS = defaultCRS; From 78b834c5b04000bec94e5339fd05edab329bb2ab Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:27:19 +1000 Subject: [PATCH 198/364] Fix missing Q_OBJECT macros Thanks to Clazy --- python/core/layertree/qgslayertreemodel.sip | 2 + python/gui/qgscodeeditorsql.sip | 1 + .../qgsdatadefinedsizelegendwidget.sip | 1 + src/app/qgsdiagramproperties.h | 2 + src/core/CMakeLists.txt | 5 +- src/core/layertree/qgslayertreemodel.cpp | 30 --- src/core/layertree/qgslayertreemodel.h | 36 +++- .../layertree/qgslayertreemodellegendnode.h | 6 +- src/core/layout/qgslayoutitemregistry.h | 2 + src/core/processing/qgsnativealgorithms.h | 2 + src/core/qgsnetworkdiskcache.cpp | 4 +- src/core/qgsnetworkdiskcache.h | 21 +- src/core/raster/qgsrasterinterface.h | 2 + src/core/raster/qgsrasterlayerrenderer.cpp | 71 ++++--- src/core/raster/qgsrasterlayerrenderer.h | 52 ++--- src/gui/locator/qgslocatorwidget.h | 4 + src/gui/qgscodeeditorsql.cpp | 17 -- src/gui/qgscodeeditorsql.h | 25 +++ .../qgsdatadefinedsizelegendwidget.cpp | 23 --- .../qgsdatadefinedsizelegendwidget.h | 27 +++ src/plugins/grass/qgsgrassmoduleinput.h | 6 +- src/plugins/grass/qgsgrasstools.cpp | 190 ++++++++---------- src/plugins/grass/qgsgrasstools.h | 37 ++++ src/providers/arcgisrest/qgsafsdataitems.h | 2 + src/providers/arcgisrest/qgsamsdataitems.h | 2 + .../qgsarcgisservicesourceselect.cpp | 13 -- .../arcgisrest/qgsarcgisservicesourceselect.h | 14 ++ src/providers/grass/CMakeLists.txt | 1 + src/providers/grass/qgsgrassdatafile.h | 2 + src/providers/grass/qgsgrassprovidermodule.h | 2 + src/providers/wfs/CMakeLists.txt | 1 + src/providers/wfs/qgswfsconnection.h | 2 + src/providers/wfs/qgswfssourceselect.cpp | 30 +-- src/providers/wfs/qgswfssourceselect.h | 33 ++- src/providers/wms/qgswmssourceselect.cpp | 2 +- tests/src/core/testqgslayoutitem.cpp | 154 +++++++------- 36 files changed, 465 insertions(+), 359 deletions(-) diff --git a/python/core/layertree/qgslayertreemodel.sip b/python/core/layertree/qgslayertreemodel.sip index 56f88034922..1b089ffdb7e 100644 --- a/python/core/layertree/qgslayertreemodel.sip +++ b/python/core/layertree/qgslayertreemodel.sip @@ -385,6 +385,8 @@ Filter nodes from QgsMapLayerLegend according to the current filtering rules QFlags operator|(QgsLayerTreeModel::Flag f1, QFlags f2); + + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/gui/qgscodeeditorsql.sip b/python/gui/qgscodeeditorsql.sip index 88d78338cdf..4843596c37f 100644 --- a/python/gui/qgscodeeditorsql.sip +++ b/python/gui/qgscodeeditorsql.sip @@ -28,6 +28,7 @@ class QgsCodeEditorSQL : QgsCodeEditor }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/gui/symbology/qgsdatadefinedsizelegendwidget.sip b/python/gui/symbology/qgsdatadefinedsizelegendwidget.sip index 3dceef75760..b83e237f8fa 100644 --- a/python/gui/symbology/qgsdatadefinedsizelegendwidget.sip +++ b/python/gui/symbology/qgsdatadefinedsizelegendwidget.sip @@ -40,6 +40,7 @@ Returns configuration as set up in the dialog (may be null). Ownership is passed }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/src/app/qgsdiagramproperties.h b/src/app/qgsdiagramproperties.h index a1b479894f9..466f810b174 100644 --- a/src/app/qgsdiagramproperties.h +++ b/src/app/qgsdiagramproperties.h @@ -94,6 +94,8 @@ class APP_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr class EditBlockerDelegate: public QStyledItemDelegate { + Q_OBJECT + public: EditBlockerDelegate( QObject *parent = nullptr ) : QStyledItemDelegate( parent ) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2be4e4354d1..c1c3e45863d 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -659,6 +659,7 @@ SET(QGIS_CORE_MOC_HDRS composer/qgslayoutmanager.h composer/qgspaperitem.h + processing/qgsnativealgorithms.h processing/qgsprocessingalgrunnertask.h processing/qgsprocessingfeedback.h processing/qgsprocessingprovider.h @@ -669,6 +670,8 @@ SET(QGIS_CORE_MOC_HDRS raster/qgsrasterfilewritertask.h raster/qgsrasterlayer.h raster/qgsrasterdataprovider.h + raster/qgsrasterinterface.h + raster/qgsrasterlayerrenderer.h geometry/qgspoint.h @@ -943,7 +946,6 @@ SET(QGIS_CORE_HDRS metadata/qgslayermetadata.h metadata/qgslayermetadatavalidator.h - processing/qgsnativealgorithms.h processing/qgsprocessing.h processing/qgsprocessingalgorithm.h processing/qgsprocessingcontext.h @@ -980,7 +982,6 @@ SET(QGIS_CORE_HDRS raster/qgsrasterfilewriter.h raster/qgsrasterhistogram.h raster/qgsrasteridentifyresult.h - raster/qgsrasterinterface.h raster/qgsrasteriterator.h raster/qgsrasterminmaxorigin.h raster/qgsrasternuller.h diff --git a/src/core/layertree/qgslayertreemodel.cpp b/src/core/layertree/qgslayertreemodel.cpp index 70d385ed819..09fb4f8fe4c 100644 --- a/src/core/layertree/qgslayertreemodel.cpp +++ b/src/core/layertree/qgslayertreemodel.cpp @@ -33,36 +33,6 @@ #include "qgssymbollayerutils.h" #include "qgsvectorlayer.h" -///@cond PRIVATE - -/** In order to support embedded widgets in layer tree view, the model - * generates one placeholder legend node for each embedded widget. - * The placeholder will be replaced by an embedded widget in QgsLayerTreeView - */ -class EmbeddedWidgetLegendNode : public QgsLayerTreeModelLegendNode -{ - public: - EmbeddedWidgetLegendNode( QgsLayerTreeLayer *nodeL ) - : QgsLayerTreeModelLegendNode( nodeL ) - { - // we need a valid rule key to allow the model to build a tree out of legend nodes - // if that's possible (if there is a node without a rule key, building of tree is canceled) - mRuleKey = QStringLiteral( "embedded-widget-" ) + QUuid::createUuid().toString(); - } - - QVariant data( int role ) const override - { - if ( role == RuleKeyRole ) - return mRuleKey; - return QVariant(); - } - - private: - QString mRuleKey; -}; - -///@endcond - QgsLayerTreeModel::QgsLayerTreeModel( QgsLayerTree *rootNode, QObject *parent ) : QAbstractItemModel( parent ) , mRootNode( rootNode ) diff --git a/src/core/layertree/qgslayertreemodel.h b/src/core/layertree/qgslayertreemodel.h index bb4d85791ee..2863a1ed5eb 100644 --- a/src/core/layertree/qgslayertreemodel.h +++ b/src/core/layertree/qgslayertreemodel.h @@ -25,11 +25,11 @@ #include "qgsgeometry.h" #include "qgsmaplayer.h" +#include "qgslayertreemodellegendnode.h" class QgsLayerTreeNode; class QgsLayerTreeGroup; class QgsLayerTreeLayer; -class QgsLayerTreeModelLegendNode; class QgsMapHitTest; class QgsMapLayer; class QgsMapSettings; @@ -385,4 +385,38 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayerTreeModel::Flags ) +///@cond PRIVATE +#ifndef SIP_RUN + +/** In order to support embedded widgets in layer tree view, the model + * generates one placeholder legend node for each embedded widget. + * The placeholder will be replaced by an embedded widget in QgsLayerTreeView + */ +class EmbeddedWidgetLegendNode : public QgsLayerTreeModelLegendNode +{ + Q_OBJECT + + public: + EmbeddedWidgetLegendNode( QgsLayerTreeLayer *nodeL ) + : QgsLayerTreeModelLegendNode( nodeL ) + { + // we need a valid rule key to allow the model to build a tree out of legend nodes + // if that's possible (if there is a node without a rule key, building of tree is canceled) + mRuleKey = QStringLiteral( "embedded-widget-" ) + QUuid::createUuid().toString(); + } + + QVariant data( int role ) const override + { + if ( role == RuleKeyRole ) + return mRuleKey; + return QVariant(); + } + + private: + QString mRuleKey; +}; +#endif + +///@endcond + #endif // QGSLAYERTREEMODEL_H diff --git a/src/core/layertree/qgslayertreemodellegendnode.h b/src/core/layertree/qgslayertreemodellegendnode.h index cfd4cf3a337..00843bbd2b7 100644 --- a/src/core/layertree/qgslayertreemodellegendnode.h +++ b/src/core/layertree/qgslayertreemodellegendnode.h @@ -26,7 +26,7 @@ #include "qgis_core.h" #include "qgis.h" -#include "qgsrasterdataprovider.h" // for QgsImageFetcher dtor visibility +#include "raster/qgsrasterdataprovider.h" // for QgsImageFetcher dtor visibility class QgsLayerTreeLayer; class QgsLayerTreeModel; @@ -140,7 +140,7 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject QString mUserLabel; }; -#include "qgslegendsymbolitem.h" +#include "symbology/qgslegendsymbolitem.h" /** \ingroup core * Implementation of legend node interface for displaying preview of vector symbols and their labels @@ -388,6 +388,8 @@ class CORE_EXPORT QgsWmsLegendNode : public QgsLayerTreeModelLegendNode */ class CORE_EXPORT QgsDataDefinedSizeLegendNode : public QgsLayerTreeModelLegendNode { + Q_OBJECT + public: //! Construct the node using QgsDataDefinedSizeLegend as definition of the node's appearance QgsDataDefinedSizeLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsDataDefinedSizeLegend &settings, QObject *parent SIP_TRANSFERTHIS = nullptr ); diff --git a/src/core/layout/qgslayoutitemregistry.h b/src/core/layout/qgslayoutitemregistry.h index 788c837159c..1ae53579bfc 100644 --- a/src/core/layout/qgslayoutitemregistry.h +++ b/src/core/layout/qgslayoutitemregistry.h @@ -265,6 +265,8 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject //simple item for testing class TestLayoutItem : public QgsLayoutItem { + Q_OBJECT + public: TestLayoutItem( QgsLayout *layout ); diff --git a/src/core/processing/qgsnativealgorithms.h b/src/core/processing/qgsnativealgorithms.h index 7c599f7a57c..c23fbeb5140 100644 --- a/src/core/processing/qgsnativealgorithms.h +++ b/src/core/processing/qgsnativealgorithms.h @@ -29,6 +29,8 @@ class QgsNativeAlgorithms: public QgsProcessingProvider { + Q_OBJECT + public: QgsNativeAlgorithms( QObject *parent = nullptr ); diff --git a/src/core/qgsnetworkdiskcache.cpp b/src/core/qgsnetworkdiskcache.cpp index af1b5328bdd..c5f7a6ffc33 100644 --- a/src/core/qgsnetworkdiskcache.cpp +++ b/src/core/qgsnetworkdiskcache.cpp @@ -18,7 +18,9 @@ #include "qgsnetworkdiskcache.h" -QgsNetworkDiskCache::ExpirableNetworkDiskCache QgsNetworkDiskCache::sDiskCache; +///@cond PRIVATE +ExpirableNetworkDiskCache QgsNetworkDiskCache::sDiskCache; +///@endcond QMutex QgsNetworkDiskCache::sDiskCacheMutex; QgsNetworkDiskCache::QgsNetworkDiskCache( QObject *parent ) diff --git a/src/core/qgsnetworkdiskcache.h b/src/core/qgsnetworkdiskcache.h index ac74b9a48f8..3f55398c79c 100644 --- a/src/core/qgsnetworkdiskcache.h +++ b/src/core/qgsnetworkdiskcache.h @@ -25,6 +25,19 @@ class QNetworkDiskCache; +///@cond PRIVATE + +class ExpirableNetworkDiskCache : public QNetworkDiskCache +{ + Q_OBJECT + + public: + explicit ExpirableNetworkDiskCache( QObject *parent = 0 ) : QNetworkDiskCache( parent ) {} + qint64 runExpire() { return QNetworkDiskCache::expire(); } +}; + +///@endcond + /** \ingroup core * Wrapper implementation of QNetworkDiskCache with all methods guarded by a * mutex soly for internal use of QgsNetworkAccessManagers @@ -83,14 +96,6 @@ class QgsNetworkDiskCache : public QNetworkDiskCache private: explicit QgsNetworkDiskCache( QObject *parent ); - Q_DISABLE_COPY( QgsNetworkDiskCache ) - - class ExpirableNetworkDiskCache : public QNetworkDiskCache - { - public: - explicit ExpirableNetworkDiskCache( QObject *parent = 0 ) : QNetworkDiskCache( parent ) {} - qint64 runExpire() { return QNetworkDiskCache::expire(); } - }; static ExpirableNetworkDiskCache sDiskCache; static QMutex sDiskCacheMutex; diff --git a/src/core/raster/qgsrasterinterface.h b/src/core/raster/qgsrasterinterface.h index 99842f27eaf..1b07f519d88 100644 --- a/src/core/raster/qgsrasterinterface.h +++ b/src/core/raster/qgsrasterinterface.h @@ -39,6 +39,8 @@ */ class CORE_EXPORT QgsRasterBlockFeedback : public QgsFeedback { + Q_OBJECT + public: //! Construct a new raster block feedback object QgsRasterBlockFeedback( QObject *parent = nullptr ) : QgsFeedback( parent ), mPreviewOnly( false ), mRenderPartialOutput( false ) {} diff --git a/src/core/raster/qgsrasterlayerrenderer.cpp b/src/core/raster/qgsrasterlayerrenderer.cpp index 837a6a55633..ac2f41ebc09 100644 --- a/src/core/raster/qgsrasterlayerrenderer.cpp +++ b/src/core/raster/qgsrasterlayerrenderer.cpp @@ -25,12 +25,50 @@ #include "qgsproject.h" #include "qgsexception.h" + +///@cond PRIVATE + +QgsRasterLayerRendererFeedback::QgsRasterLayerRendererFeedback( QgsRasterLayerRenderer *r ) + : QgsRasterBlockFeedback() + , mR( r ) + , mMinimalPreviewInterval( 250 ) +{ + setRenderPartialOutput( r->mContext.testFlag( QgsRenderContext::RenderPartialOutput ) ); +} + +void QgsRasterLayerRendererFeedback::onNewData() +{ + if ( !renderPartialOutput() ) + return; // we were not asked for partial renders and we may not have a temporary image for overwriting... + + // update only once upon a time + // (preview itself takes some time) + if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval ) + return; + + // TODO: update only the area that got new data + + QgsDebugMsg( QString( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ) ); + QTime t; + t.start(); + QgsRasterBlockFeedback feedback; + feedback.setPreviewOnly( true ); + feedback.setRenderPartialOutput( true ); + QgsRasterIterator iterator( mR->mPipe->last() ); + QgsRasterDrawer drawer( &iterator ); + drawer.draw( mR->mPainter, mR->mRasterViewPort, mR->mMapToPixel, &feedback ); + QgsDebugMsg( QString( "total raster preview time: %1 ms" ).arg( t.elapsed() ) ); + mLastPreview = QTime::currentTime(); +} + +///@endcond +/// QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer *layer, QgsRenderContext &rendererContext ) : QgsMapLayerRenderer( layer->id() ) , mRasterViewPort( nullptr ) , mPipe( nullptr ) , mContext( rendererContext ) - , mFeedback( new Feedback( this ) ) + , mFeedback( new QgsRasterLayerRendererFeedback( this ) ) { mPainter = rendererContext.painter(); const QgsMapToPixel &qgsMapToPixel = rendererContext.mapToPixel(); @@ -231,34 +269,3 @@ QgsFeedback *QgsRasterLayerRenderer::feedback() const return mFeedback; } -QgsRasterLayerRenderer::Feedback::Feedback( QgsRasterLayerRenderer *r ) - : mR( r ) - , mMinimalPreviewInterval( 250 ) -{ - setRenderPartialOutput( r->mContext.testFlag( QgsRenderContext::RenderPartialOutput ) ); -} - -void QgsRasterLayerRenderer::Feedback::onNewData() -{ - if ( !renderPartialOutput() ) - return; // we were not asked for partial renders and we may not have a temporary image for overwriting... - - // update only once upon a time - // (preview itself takes some time) - if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval ) - return; - - // TODO: update only the area that got new data - - QgsDebugMsg( QString( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ) ); - QTime t; - t.start(); - QgsRasterBlockFeedback feedback; - feedback.setPreviewOnly( true ); - feedback.setRenderPartialOutput( true ); - QgsRasterIterator iterator( mR->mPipe->last() ); - QgsRasterDrawer drawer( &iterator ); - drawer.draw( mR->mPainter, mR->mRasterViewPort, mR->mMapToPixel, &feedback ); - QgsDebugMsg( QString( "total raster preview time: %1 ms" ).arg( t.elapsed() ) ); - mLastPreview = QTime::currentTime(); -} diff --git a/src/core/raster/qgsrasterlayerrenderer.h b/src/core/raster/qgsrasterlayerrenderer.h index 7346791a626..8329904d750 100644 --- a/src/core/raster/qgsrasterlayerrenderer.h +++ b/src/core/raster/qgsrasterlayerrenderer.h @@ -23,7 +23,6 @@ class QPainter; class QgsMapToPixel; -class QgsRasterBlockFeedback; class QgsRasterLayer; class QgsRasterPipe; struct QgsRasterViewPort; @@ -33,6 +32,30 @@ class QgsRasterLayerRenderer; #include "qgsrasterinterface.h" +///@cond PRIVATE + +/** \ingroup core + * Specific internal feedback class to provide preview of raster layer rendering. + * \since QGIS 3.0 + * \note not available in Python bindings + */ +class CORE_EXPORT QgsRasterLayerRendererFeedback : public QgsRasterBlockFeedback +{ + Q_OBJECT + + public: + //! Create feedback object based on our layer renderer + explicit QgsRasterLayerRendererFeedback( QgsRasterLayerRenderer *r ); + + //! when notified of new data in data provider it launches a preview draw of the raster + virtual void onNewData() override; + private: + QgsRasterLayerRenderer *mR = nullptr; //!< Parent renderer instance + int mMinimalPreviewInterval; //!< In milliseconds + QTime mLastPreview; //!< When last preview has been generated +}; + +///@endcond /** \ingroup core * Implementation of threaded rendering for raster layers. @@ -40,7 +63,7 @@ class QgsRasterLayerRenderer; * \since QGIS 2.4 * \note not available in Python bindings */ -class QgsRasterLayerRenderer : public QgsMapLayerRenderer +class CORE_EXPORT QgsRasterLayerRenderer : public QgsMapLayerRenderer { public: QgsRasterLayerRenderer( QgsRasterLayer *layer, QgsRenderContext &rendererContext ); @@ -50,7 +73,7 @@ class QgsRasterLayerRenderer : public QgsMapLayerRenderer virtual QgsFeedback *feedback() const override; - protected: + private: QPainter *mPainter = nullptr; const QgsMapToPixel *mMapToPixel = nullptr; @@ -59,27 +82,10 @@ class QgsRasterLayerRenderer : public QgsMapLayerRenderer QgsRasterPipe *mPipe = nullptr; QgsRenderContext &mContext; - /** \ingroup core - * Specific internal feedback class to provide preview of raster layer rendering. - * \since QGIS 3.0 - * \note not available in Python bindings - */ - class Feedback : public QgsRasterBlockFeedback - { - public: - //! Create feedback object based on our layer renderer - explicit Feedback( QgsRasterLayerRenderer *r ); - - //! when notified of new data in data provider it launches a preview draw of the raster - virtual void onNewData() override; - private: - QgsRasterLayerRenderer *mR; //!< Parent renderer instance - int mMinimalPreviewInterval; //!< In milliseconds - QTime mLastPreview; //!< When last preview has been generated - }; - //! feedback class for cancelation and preview generation - Feedback *mFeedback = nullptr; + QgsRasterLayerRendererFeedback *mFeedback = nullptr; + + friend class QgsRasterLayerRendererFeedback; }; diff --git a/src/gui/locator/qgslocatorwidget.h b/src/gui/locator/qgslocatorwidget.h index 2aae612c450..6bb2931d1c2 100644 --- a/src/gui/locator/qgslocatorwidget.h +++ b/src/gui/locator/qgslocatorwidget.h @@ -130,6 +130,8 @@ class GUI_EXPORT QgsLocatorWidget : public QWidget class QgsLocatorFilterFilter : public QgsLocatorFilter { + Q_OBJECT + public: QgsLocatorFilterFilter( QgsLocatorWidget *widget, QObject *parent = nullptr ); @@ -221,6 +223,8 @@ class QgsLocatorModel : public QAbstractTableModel class QgsLocatorProxyModel : public QSortFilterProxyModel { + Q_OBJECT + public: explicit QgsLocatorProxyModel( QObject *parent = nullptr ); diff --git a/src/gui/qgscodeeditorsql.cpp b/src/gui/qgscodeeditorsql.cpp index 66b2e0e1a9e..8131e8cc785 100644 --- a/src/gui/qgscodeeditorsql.cpp +++ b/src/gui/qgscodeeditorsql.cpp @@ -20,7 +20,6 @@ #include #include #include -#include QgsCodeEditorSQL::QgsCodeEditorSQL( QWidget *parent ) @@ -36,22 +35,6 @@ QgsCodeEditorSQL::QgsCodeEditorSQL( QWidget *parent ) setSciLexerSQL(); } -/** Internal use. - - setAutoCompletionCaseSensitivity( false ) is not sufficient when installing - a lexer, since its caseSensitive() method is actually used, and defaults - to true. - @note not available in Python bindings - @ingroup gui -*/ -class QgsCaseInsensitiveLexerSQL: public QsciLexerSQL -{ - public: - //! constructor - explicit QgsCaseInsensitiveLexerSQL( QObject *parent = 0 ) : QsciLexerSQL( parent ) {} - - bool caseSensitive() const override { return false; } -}; void QgsCodeEditorSQL::setSciLexerSQL() { diff --git a/src/gui/qgscodeeditorsql.h b/src/gui/qgscodeeditorsql.h index 8008669dffe..88de794aed0 100644 --- a/src/gui/qgscodeeditorsql.h +++ b/src/gui/qgscodeeditorsql.h @@ -19,6 +19,7 @@ #include "qgscodeeditor.h" #include "qgis_sip.h" #include "qgis_gui.h" +#include SIP_IF_MODULE( HAVE_QSCI_SIP ) @@ -41,4 +42,28 @@ class GUI_EXPORT QgsCodeEditorSQL : public QgsCodeEditor void setSciLexerSQL(); }; +#ifndef SIP_RUN +///@cond PRIVATE + +/** Internal use. + + setAutoCompletionCaseSensitivity( false ) is not sufficient when installing + a lexer, since its caseSensitive() method is actually used, and defaults + to true. + @note not available in Python bindings + @ingroup gui +*/ +class QgsCaseInsensitiveLexerSQL: public QsciLexerSQL +{ + Q_OBJECT + + public: + //! constructor + explicit QgsCaseInsensitiveLexerSQL( QObject *parent = 0 ) : QsciLexerSQL( parent ) {} + + bool caseSensitive() const override { return false; } +}; +///@endcond +#endif + #endif diff --git a/src/gui/symbology/qgsdatadefinedsizelegendwidget.cpp b/src/gui/symbology/qgsdatadefinedsizelegendwidget.cpp index 3a452447d80..d3843398b88 100644 --- a/src/gui/symbology/qgsdatadefinedsizelegendwidget.cpp +++ b/src/gui/symbology/qgsdatadefinedsizelegendwidget.cpp @@ -30,29 +30,6 @@ #include "qgsvectorlayer.h" -///@cond PRIVATE - -//! Simple delegate to allow only numeric values -class SizeClassDelegate : public QStyledItemDelegate -{ - public: - SizeClassDelegate( QObject *parent ) - : QStyledItemDelegate( parent ) - { - } - - QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const - { - QLineEdit *lineEdit = new QLineEdit( parent ); - QDoubleValidator *validator = new QDoubleValidator( 0, 1e6, 1, lineEdit ); - lineEdit->setValidator( validator ); - return lineEdit; - } -}; - -///@endcond - - QgsDataDefinedSizeLegendWidget::QgsDataDefinedSizeLegendWidget( const QgsDataDefinedSizeLegend *ddsLegend, const QgsProperty &ddSize, QgsMarkerSymbol *overrideSymbol, QgsMapCanvas *canvas, QWidget *parent ) : QgsPanelWidget( parent ) , mSizeProperty( ddSize ) diff --git a/src/gui/symbology/qgsdatadefinedsizelegendwidget.h b/src/gui/symbology/qgsdatadefinedsizelegendwidget.h index 5680a4daa93..a2fe6be93b2 100644 --- a/src/gui/symbology/qgsdatadefinedsizelegendwidget.h +++ b/src/gui/symbology/qgsdatadefinedsizelegendwidget.h @@ -24,6 +24,7 @@ #include "qgspanelwidget.h" #include "qgsproperty.h" +#include class QStandardItemModel; @@ -76,4 +77,30 @@ class GUI_EXPORT QgsDataDefinedSizeLegendWidget : public QgsPanelWidget, private QStandardItemModel *mSizeClassesModel; }; +#ifndef SIP_RUN +///@cond PRIVATE + +//! Simple delegate to allow only numeric values +class SizeClassDelegate : public QStyledItemDelegate +{ + Q_OBJECT + + public: + SizeClassDelegate( QObject *parent ) + : QStyledItemDelegate( parent ) + { + } + + QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const + { + QLineEdit *lineEdit = new QLineEdit( parent ); + QDoubleValidator *validator = new QDoubleValidator( 0, 1e6, 1, lineEdit ); + lineEdit->setValidator( validator ); + return lineEdit; + } +}; + +///@endcond +#endif + #endif // QGSDATADEFINEDSIZELEGENDWIDGET_H diff --git a/src/plugins/grass/qgsgrassmoduleinput.h b/src/plugins/grass/qgsgrassmoduleinput.h index 101e6cd12ce..7ddb000ce7e 100644 --- a/src/plugins/grass/qgsgrassmoduleinput.h +++ b/src/plugins/grass/qgsgrassmoduleinput.h @@ -152,6 +152,8 @@ class QgsGrassModuleInputCompleterProxy : public QAbstractProxyModel class QgsGrassModuleInputCompleter : public QCompleter { + Q_OBJECT + public: explicit QgsGrassModuleInputCompleter( QAbstractItemModel *model, QWidget *parent = 0 ); @@ -160,6 +162,8 @@ class QgsGrassModuleInputCompleter : public QCompleter class QgsGrassModuleInputComboBox : public QComboBox { + Q_OBJECT + public: explicit QgsGrassModuleInputComboBox( QgsGrassObject::Type type, QWidget *parent = 0 ); ~QgsGrassModuleInputComboBox(); @@ -340,7 +344,7 @@ class QgsGrassModuleInput : public QgsGrassModuleGroupBoxItem // List of vector layers matching mGeometryTypes for currently selected vector QList mLayers; - //! The imput map will be updated -> must be from current mapset + //! The input map will be updated -> must be from current mapset // TODO bool mUpdate; diff --git a/src/plugins/grass/qgsgrasstools.cpp b/src/plugins/grass/qgsgrasstools.cpp index 8b18c86e573..cd009010d26 100644 --- a/src/plugins/grass/qgsgrasstools.cpp +++ b/src/plugins/grass/qgsgrasstools.cpp @@ -41,114 +41,11 @@ #include "qgsdetaileditemdata.h" #include "qgsdetaileditemdelegate.h" -#include -#include #ifdef Q_OS_WIN #include "qgsgrassutils.h" #endif -// TODO: searching acros the tree is taken from QgsDockBrowserTreeView -> create common base class -class QgsGrassToolsTreeFilterProxyModel : public QSortFilterProxyModel -{ - public: - explicit QgsGrassToolsTreeFilterProxyModel( QObject *parent ) - : QSortFilterProxyModel( parent ) - , mModel( 0 ) - { - setDynamicSortFilter( true ); - mRegExp.setPatternSyntax( QRegExp::Wildcard ); - mRegExp.setCaseSensitivity( Qt::CaseInsensitive ); - } - - void setSourceModel( QAbstractItemModel *sourceModel ) override - { - mModel = sourceModel; - QSortFilterProxyModel::setSourceModel( sourceModel ); - } - - void setFilter( const QString &filter ) - { - QgsDebugMsg( QString( "filter = %1" ).arg( filter ) ); - if ( mFilter == filter ) - { - return; - } - mFilter = filter; - mRegExp.setPattern( mFilter ); - - invalidateFilter(); - } - - protected: - - QAbstractItemModel *mModel = nullptr; - QString mFilter; // filter string provided - QRegExp mRegExp; // regular expression constructed from filter string - - bool filterAcceptsString( const QString &value ) const - { - return value.contains( mRegExp ); - } - - // It would be better to apply the filer only to expanded (visible) items, but using mapFromSource() + view here was causing strange errors - bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override - { - if ( mFilter.isEmpty() || !mModel ) - { - return true; - } - - QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent ); - return filterAcceptsItem( sourceIndex ) || filterAcceptsAncestor( sourceIndex ) || filterAcceptsDescendant( sourceIndex ); - } - - // returns true if at least one ancestor is accepted by filter - bool filterAcceptsAncestor( const QModelIndex &sourceIndex ) const - { - if ( !mModel ) - { - return true; - } - - QModelIndex sourceParentIndex = mModel->parent( sourceIndex ); - if ( !sourceParentIndex.isValid() ) - return false; - if ( filterAcceptsItem( sourceParentIndex ) ) - return true; - - return filterAcceptsAncestor( sourceParentIndex ); - } - - // returns true if at least one descendant s accepted by filter - bool filterAcceptsDescendant( const QModelIndex &sourceIndex ) const - { - if ( !mModel ) - { - return true; - } - - for ( int i = 0; i < mModel->rowCount( sourceIndex ); i++ ) - { - QModelIndex sourceChildIndex = mModel->index( i, 0, sourceIndex ); - if ( filterAcceptsItem( sourceChildIndex ) ) - return true; - if ( filterAcceptsDescendant( sourceChildIndex ) ) - return true; - } - return false; - } - - // filter accepts item name - bool filterAcceptsItem( const QModelIndex &sourceIndex ) const - { - if ( !mModel ) - { - return true; - } - return filterAcceptsString( mModel->data( sourceIndex, filterRole() ).toString() ); - } -}; QgsGrassTools::QgsGrassTools( QgisInterface *iface, QWidget *parent, const char *name, Qt::WindowFlags f ) : QgsDockWidget( parent, f ) @@ -792,3 +689,90 @@ void QgsGrassTools::on_mViewModeButton_clicked() mViewModeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconTreeView.png" ) ) ); } } + +QgsGrassToolsTreeFilterProxyModel::QgsGrassToolsTreeFilterProxyModel( QObject *parent ) + : QSortFilterProxyModel( parent ) + , mModel( 0 ) +{ + setDynamicSortFilter( true ); + mRegExp.setPatternSyntax( QRegExp::Wildcard ); + mRegExp.setCaseSensitivity( Qt::CaseInsensitive ); +} + +void QgsGrassToolsTreeFilterProxyModel::setSourceModel( QAbstractItemModel *sourceModel ) +{ + mModel = sourceModel; + QSortFilterProxyModel::setSourceModel( sourceModel ); +} + +void QgsGrassToolsTreeFilterProxyModel::setFilter( const QString &filter ) +{ + QgsDebugMsg( QString( "filter = %1" ).arg( filter ) ); + if ( mFilter == filter ) + { + return; + } + mFilter = filter; + mRegExp.setPattern( mFilter ); + + invalidateFilter(); +} + +bool QgsGrassToolsTreeFilterProxyModel::filterAcceptsString( const QString &value ) const +{ + return value.contains( mRegExp ); +} + +bool QgsGrassToolsTreeFilterProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const +{ + if ( mFilter.isEmpty() || !mModel ) + { + return true; + } + + QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent ); + return filterAcceptsItem( sourceIndex ) || filterAcceptsAncestor( sourceIndex ) || filterAcceptsDescendant( sourceIndex ); +} + +bool QgsGrassToolsTreeFilterProxyModel::filterAcceptsAncestor( const QModelIndex &sourceIndex ) const +{ + if ( !mModel ) + { + return true; + } + + QModelIndex sourceParentIndex = mModel->parent( sourceIndex ); + if ( !sourceParentIndex.isValid() ) + return false; + if ( filterAcceptsItem( sourceParentIndex ) ) + return true; + + return filterAcceptsAncestor( sourceParentIndex ); +} + +bool QgsGrassToolsTreeFilterProxyModel::filterAcceptsDescendant( const QModelIndex &sourceIndex ) const +{ + if ( !mModel ) + { + return true; + } + + for ( int i = 0; i < mModel->rowCount( sourceIndex ); i++ ) + { + QModelIndex sourceChildIndex = mModel->index( i, 0, sourceIndex ); + if ( filterAcceptsItem( sourceChildIndex ) ) + return true; + if ( filterAcceptsDescendant( sourceChildIndex ) ) + return true; + } + return false; +} + +bool QgsGrassToolsTreeFilterProxyModel::filterAcceptsItem( const QModelIndex &sourceIndex ) const +{ + if ( !mModel ) + { + return true; + } + return filterAcceptsString( mModel->data( sourceIndex, filterRole() ).toString() ); +} diff --git a/src/plugins/grass/qgsgrasstools.h b/src/plugins/grass/qgsgrasstools.h index de0b43c3c12..777e38723e6 100644 --- a/src/plugins/grass/qgsgrasstools.h +++ b/src/plugins/grass/qgsgrasstools.h @@ -20,6 +20,9 @@ #include "ui_qgsgrasstoolsbase.h" +#include +#include + class QDomElement; class QSortFilterProxyModel; class QStandardItem; @@ -140,4 +143,38 @@ class QgsGrassTools: public QgsDockWidget, public Ui::QgsGrassToolsBase void showTabs(); }; + +// TODO: searching acros the tree is taken from QgsDockBrowserTreeView -> create common base class +class QgsGrassToolsTreeFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + + public: + explicit QgsGrassToolsTreeFilterProxyModel( QObject *parent ); + + void setSourceModel( QAbstractItemModel *sourceModel ) override; + + void setFilter( const QString &filter ); + + protected: + + QAbstractItemModel *mModel = nullptr; + QString mFilter; // filter string provided + QRegExp mRegExp; // regular expression constructed from filter string + + bool filterAcceptsString( const QString &value ) const; + + // It would be better to apply the filer only to expanded (visible) items, but using mapFromSource() + view here was causing strange errors + bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override; + + // returns true if at least one ancestor is accepted by filter + bool filterAcceptsAncestor( const QModelIndex &sourceIndex ) const; + + // returns true if at least one descendant s accepted by filter + bool filterAcceptsDescendant( const QModelIndex &sourceIndex ) const; + + // filter accepts item name + bool filterAcceptsItem( const QModelIndex &sourceIndex ) const; +}; + #endif // QGSGRASSTOOLS_H diff --git a/src/providers/arcgisrest/qgsafsdataitems.h b/src/providers/arcgisrest/qgsafsdataitems.h index e5f21a8f33e..d9c68b374ee 100644 --- a/src/providers/arcgisrest/qgsafsdataitems.h +++ b/src/providers/arcgisrest/qgsafsdataitems.h @@ -62,6 +62,8 @@ class QgsAfsConnectionItem : public QgsDataCollectionItem class QgsAfsLayerItem : public QgsLayerItem { + Q_OBJECT + public: QgsAfsLayerItem( QgsDataItem *parent, const QString &name, const QString &url, const QString &title, const QString &authid ); }; diff --git a/src/providers/arcgisrest/qgsamsdataitems.h b/src/providers/arcgisrest/qgsamsdataitems.h index 98647c8f524..f39c5f8890c 100644 --- a/src/providers/arcgisrest/qgsamsdataitems.h +++ b/src/providers/arcgisrest/qgsamsdataitems.h @@ -64,6 +64,8 @@ class QgsAmsConnectionItem : public QgsDataCollectionItem class QgsAmsLayerItem : public QgsLayerItem { + Q_OBJECT + public: QgsAmsLayerItem( QgsDataItem *parent, const QString &name, const QString &url, const QString &id, const QString &title, const QString &authid, const QString &format ); }; diff --git a/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp b/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp index e2d90605215..1567de730ec 100644 --- a/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp +++ b/src/providers/arcgisrest/qgsarcgisservicesourceselect.cpp @@ -29,7 +29,6 @@ #include "qgssettings.h" #include "qgsmapcanvas.h" -#include #include #include #include @@ -37,18 +36,6 @@ #include #include "qgshelp.h" -/** - * Item delegate with tweaked sizeHint. - */ -class QgsAbstractDataSourceWidgetItemDelegate : public QItemDelegate -{ - public: - //! Constructor - QgsAbstractDataSourceWidgetItemDelegate( QObject *parent = 0 ) : QItemDelegate( parent ) { } - QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override; -}; - - QgsArcGisServiceSourceSelect::QgsArcGisServiceSourceSelect( const QString &serviceName, ServiceType serviceType, QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode widgetMode ): QgsAbstractDataSourceWidget( parent, fl, widgetMode ), mServiceName( serviceName ), diff --git a/src/providers/arcgisrest/qgsarcgisservicesourceselect.h b/src/providers/arcgisrest/qgsarcgisservicesourceselect.h index c9ff04ff0d7..52ae2f092af 100644 --- a/src/providers/arcgisrest/qgsarcgisservicesourceselect.h +++ b/src/providers/arcgisrest/qgsarcgisservicesourceselect.h @@ -23,6 +23,8 @@ #include "qgscoordinatereferencesystem.h" #include "qgsabstractdatasourcewidget.h" +#include + class QStandardItemModel; class QSortFilterProxyModel; class QgsProjectionSelectionDialog; @@ -116,5 +118,17 @@ class QgsArcGisServiceSourceSelect : public QgsAbstractDataSourceWidget, protect void treeWidgetCurrentRowChanged( const QModelIndex ¤t, const QModelIndex &previous ); }; +/** + * Item delegate with tweaked sizeHint. + */ +class QgsAbstractDataSourceWidgetItemDelegate : public QItemDelegate +{ + Q_OBJECT + + public: + //! Constructor + QgsAbstractDataSourceWidgetItemDelegate( QObject *parent = 0 ) : QItemDelegate( parent ) { } + QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override; +}; #endif // QGSARCGISSERVICESOURCESELECT_H diff --git a/src/providers/grass/CMakeLists.txt b/src/providers/grass/CMakeLists.txt index e7deea4ccb2..f25eede9409 100644 --- a/src/providers/grass/CMakeLists.txt +++ b/src/providers/grass/CMakeLists.txt @@ -33,6 +33,7 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION) SET(GRASS_LIBRARY_MOC_HDRS ../qgsgrass.h + ../qgsgrassdatafile.h ../qgsgrassfeatureiterator.h ../qgsgrassimport.h ../qgsgrassprovider.h diff --git a/src/providers/grass/qgsgrassdatafile.h b/src/providers/grass/qgsgrassdatafile.h index d6b33eba815..ead4f0d8227 100644 --- a/src/providers/grass/qgsgrassdatafile.h +++ b/src/providers/grass/qgsgrassdatafile.h @@ -34,6 +34,8 @@ */ class GRASS_LIB_EXPORT QgsGrassDataFile : public QFile { + Q_OBJECT + public: explicit QgsGrassDataFile( QObject *parent = 0 ); // We need FILE* to be able to test feof but QFile::open(FILE *, OpenMode) is not virtual diff --git a/src/providers/grass/qgsgrassprovidermodule.h b/src/providers/grass/qgsgrassprovidermodule.h index 0b8ba7a172a..e14c135e6f8 100644 --- a/src/providers/grass/qgsgrassprovidermodule.h +++ b/src/providers/grass/qgsgrassprovidermodule.h @@ -77,6 +77,8 @@ class QgsGrassObjectItemBase class QgsGrassLocationItem : public QgsDirectoryItem, public QgsGrassObjectItemBase { + Q_OBJECT + public: QgsGrassLocationItem( QgsDataItem *parent, QString dirPath, QString path ); diff --git a/src/providers/wfs/CMakeLists.txt b/src/providers/wfs/CMakeLists.txt index 328a118ffdb..b0807ab3607 100644 --- a/src/providers/wfs/CMakeLists.txt +++ b/src/providers/wfs/CMakeLists.txt @@ -19,6 +19,7 @@ SET(WFS_SRCS SET (WFS_MOC_HDRS qgswfscapabilities.h + qgswfsconnection.h qgswfsdataitems.h qgswfsprovider.h qgswfsfeatureiterator.h diff --git a/src/providers/wfs/qgswfsconnection.h b/src/providers/wfs/qgswfsconnection.h index e95b21d8dee..85e6b4f4428 100644 --- a/src/providers/wfs/qgswfsconnection.h +++ b/src/providers/wfs/qgswfsconnection.h @@ -20,6 +20,8 @@ class QgsWfsConnection : public QgsOwsConnection { + Q_OBJECT + public: /** diff --git a/src/providers/wfs/qgswfssourceselect.cpp b/src/providers/wfs/qgswfssourceselect.cpp index 11560917296..869e3775950 100644 --- a/src/providers/wfs/qgswfssourceselect.cpp +++ b/src/providers/wfs/qgswfssourceselect.cpp @@ -30,7 +30,6 @@ #include "qgslogger.h" #include "qgsmanageconnectionsdialog.h" #include "qgssqlstatement.h" -#include "qgssqlcomposerdialog.h" #include "qgssettings.h" #include @@ -406,19 +405,6 @@ void QgsWFSSourceSelect::addButtonClicked() } } -class QgsWFSValidatorCallback: public QObject, public QgsSQLComposerDialog::SQLValidatorCallback -{ - public: - QgsWFSValidatorCallback( QObject *parent, - const QgsWFSDataSourceURI &uri, const QString &allSql, - const QgsWfsCapabilities::Capabilities &caps ); - bool isValid( const QString &sql, QString &errorReason, QString &warningMsg ) override; - private: - QgsWFSDataSourceURI mURI; - QString mAllSql; - const QgsWfsCapabilities::Capabilities &mCaps; -}; - QgsWFSValidatorCallback::QgsWFSValidatorCallback( QObject *parent, const QgsWFSDataSourceURI &uri, const QString &allSql, @@ -449,20 +435,6 @@ bool QgsWFSValidatorCallback::isValid( const QString &sqlStr, QString &errorReas return true; } -class QgsWFSTableSelectedCallback: public QObject, public QgsSQLComposerDialog::TableSelectedCallback -{ - public: - QgsWFSTableSelectedCallback( QgsSQLComposerDialog *dialog, - const QgsWFSDataSourceURI &uri, - const QgsWfsCapabilities::Capabilities &caps ); - void tableSelected( const QString &name ) override; - - private: - QgsSQLComposerDialog *mDialog = nullptr; - QgsWFSDataSourceURI mURI; - const QgsWfsCapabilities::Capabilities &mCaps; -}; - QgsWFSTableSelectedCallback::QgsWFSTableSelectedCallback( QgsSQLComposerDialog *dialog, const QgsWFSDataSourceURI &uri, const QgsWfsCapabilities::Capabilities &caps ) @@ -796,4 +768,4 @@ QSize QgsWFSItemDelegate::sizeHint( const QStyleOptionViewItem &option, const QM void QgsWFSSourceSelect::showHelp() { QgsHelp::openHelp( QStringLiteral( "working_with_ogc/ogc_client_support.html" ) ); -} \ No newline at end of file +} diff --git a/src/providers/wfs/qgswfssourceselect.h b/src/providers/wfs/qgswfssourceselect.h index aa80b15f0bd..acdfe2d7188 100644 --- a/src/providers/wfs/qgswfssourceselect.h +++ b/src/providers/wfs/qgswfssourceselect.h @@ -23,6 +23,7 @@ #include "qgswfscapabilities.h" #include "qgsproviderregistry.h" #include "qgsabstractdatasourcewidget.h" +#include "qgssqlcomposerdialog.h" #include #include @@ -30,7 +31,6 @@ class QgsProjectionSelectionDialog; class QgsWfsCapabilities; -class QgsSQLComposerDialog; class QgsWFSItemDelegate : public QItemDelegate { @@ -43,6 +43,21 @@ class QgsWFSItemDelegate : public QItemDelegate }; +class QgsWFSValidatorCallback: public QObject, public QgsSQLComposerDialog::SQLValidatorCallback +{ + Q_OBJECT + + public: + QgsWFSValidatorCallback( QObject *parent, + const QgsWFSDataSourceURI &uri, const QString &allSql, + const QgsWfsCapabilities::Capabilities &caps ); + bool isValid( const QString &sql, QString &errorReason, QString &warningMsg ) override; + private: + QgsWFSDataSourceURI mURI; + QString mAllSql; + const QgsWfsCapabilities::Capabilities &mCaps; +}; + class QgsWFSSourceSelect: public QgsAbstractDataSourceWidget, private Ui::QgsWFSSourceSelectBase { Q_OBJECT @@ -108,4 +123,20 @@ class QgsWFSSourceSelect: public QgsAbstractDataSourceWidget, private Ui::QgsWFS }; +class QgsWFSTableSelectedCallback: public QObject, public QgsSQLComposerDialog::TableSelectedCallback +{ + Q_OBJECT + + public: + QgsWFSTableSelectedCallback( QgsSQLComposerDialog *dialog, + const QgsWFSDataSourceURI &uri, + const QgsWfsCapabilities::Capabilities &caps ); + void tableSelected( const QString &name ) override; + + private: + QgsSQLComposerDialog *mDialog = nullptr; + QgsWFSDataSourceURI mURI; + const QgsWfsCapabilities::Capabilities &mCaps; +}; + #endif diff --git a/src/providers/wms/qgswmssourceselect.cpp b/src/providers/wms/qgswmssourceselect.cpp index 0e2fe94719d..5508a82e732 100644 --- a/src/providers/wms/qgswmssourceselect.cpp +++ b/src/providers/wms/qgswmssourceselect.cpp @@ -1348,4 +1348,4 @@ void QgsWMSSourceSelect::updateLayerOrderTab( const QStringList &newLayerList, c void QgsWMSSourceSelect::showHelp() { QgsHelp::openHelp( QStringLiteral( "working_with_ogc/ogc_client_support.html" ) ); -} \ No newline at end of file +} diff --git a/tests/src/core/testqgslayoutitem.cpp b/tests/src/core/testqgslayoutitem.cpp index fcaf136ed24..8caa697ba91 100644 --- a/tests/src/core/testqgslayoutitem.cpp +++ b/tests/src/core/testqgslayoutitem.cpp @@ -28,6 +28,88 @@ #include #include + +//simple item for testing, since some methods in QgsLayoutItem are pure virtual +class TestItem : public QgsLayoutItem +{ + Q_OBJECT + + public: + + TestItem( QgsLayout *layout ) : QgsLayoutItem( layout ) {} + ~TestItem() {} + + //implement pure virtual methods + int type() const override { return QgsLayoutItemRegistry::LayoutItem + 101; } + QString stringType() const override { return QStringLiteral( "TestItemType" ); } + + protected: + void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem * = nullptr ) override + { + QPainter *painter = context.painter(); + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, false ); + painter->setPen( Qt::NoPen ); + painter->setBrush( QColor( 255, 100, 100, 200 ) ); + painter->drawRect( rect() ); + painter->restore(); + } +}; + +//item with minimum size +class MinSizedItem : public TestItem +{ + Q_OBJECT + + public: + MinSizedItem( QgsLayout *layout ) : TestItem( layout ) + { + setMinimumSize( QgsLayoutSize( 5.0, 10.0, QgsUnitTypes::LayoutCentimeters ) ); + } + + void updateMinSize( QgsLayoutSize size ) + { + setMinimumSize( size ); + } + + ~MinSizedItem() {} +}; + +//item with fixed size +class FixedSizedItem : public TestItem +{ + Q_OBJECT + + public: + + FixedSizedItem( QgsLayout *layout ) : TestItem( layout ) + { + setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutInches ) ); + } + + void updateFixedSize( QgsLayoutSize size ) + { + setFixedSize( size ); + } + ~FixedSizedItem() {} +}; + +//item with both conflicting fixed and minimum size +class FixedMinSizedItem : public TestItem +{ + Q_OBJECT + + public: + + FixedMinSizedItem( QgsLayout *layout ) : TestItem( layout ) + { + setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) ); + setMinimumSize( QgsLayoutSize( 5.0, 9.0, QgsUnitTypes::LayoutCentimeters ) ); + } + ~FixedMinSizedItem() {} +}; + + class TestQgsLayoutItem: public QObject { Q_OBJECT @@ -66,78 +148,6 @@ class TestQgsLayoutItem: public QObject private: - //simple item for testing, since some methods in QgsLayoutItem are pure virtual - class TestItem : public QgsLayoutItem - { - public: - - TestItem( QgsLayout *layout ) : QgsLayoutItem( layout ) {} - ~TestItem() {} - - //implement pure virtual methods - int type() const override { return QgsLayoutItemRegistry::LayoutItem + 101; } - QString stringType() const override { return QStringLiteral( "TestItemType" ); } - - protected: - void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem * = nullptr ) override - { - QPainter *painter = context.painter(); - painter->save(); - painter->setRenderHint( QPainter::Antialiasing, false ); - painter->setPen( Qt::NoPen ); - painter->setBrush( QColor( 255, 100, 100, 200 ) ); - painter->drawRect( rect() ); - painter->restore(); - } - }; - - //item with minimum size - class MinSizedItem : public TestItem - { - public: - MinSizedItem( QgsLayout *layout ) : TestItem( layout ) - { - setMinimumSize( QgsLayoutSize( 5.0, 10.0, QgsUnitTypes::LayoutCentimeters ) ); - } - - void updateMinSize( QgsLayoutSize size ) - { - setMinimumSize( size ); - } - - ~MinSizedItem() {} - }; - - //item with fixed size - class FixedSizedItem : public TestItem - { - public: - - FixedSizedItem( QgsLayout *layout ) : TestItem( layout ) - { - setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutInches ) ); - } - - void updateFixedSize( QgsLayoutSize size ) - { - setFixedSize( size ); - } - ~FixedSizedItem() {} - }; - - //item with both conflicting fixed and minimum size - class FixedMinSizedItem : public TestItem - { - public: - - FixedMinSizedItem( QgsLayout *layout ) : TestItem( layout ) - { - setFixedSize( QgsLayoutSize( 2.0, 4.0, QgsUnitTypes::LayoutCentimeters ) ); - setMinimumSize( QgsLayoutSize( 5.0, 9.0, QgsUnitTypes::LayoutCentimeters ) ); - } - ~FixedMinSizedItem() {} - }; - QString mReport; bool renderCheck( QString testName, QImage &image, int mismatchCount ); From 137dc76d3c3ba5ae4ae72f73265eaafeda992ba4 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:27:49 +1000 Subject: [PATCH 199/364] Remove trivial assignment operator Violates rule of 2. Thanks to Coverity! --- src/app/dwg/libdxfrw/drw_base.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/app/dwg/libdxfrw/drw_base.h b/src/app/dwg/libdxfrw/drw_base.h index a92d59ebfa2..a3490b3062c 100644 --- a/src/app/dwg/libdxfrw/drw_base.h +++ b/src/app/dwg/libdxfrw/drw_base.h @@ -158,13 +158,6 @@ class DRW_Coord DRW_Coord(): x( 0 ), y( 0 ), z( 0 ) {} DRW_Coord( double ix, double iy, double iz ): x( ix ), y( iy ), z( iz ) {} - DRW_Coord &operator = ( const DRW_Coord &data ) - { - x = data.x; - y = data.y; - z = data.z; - return *this; - } //! < convert to unitary vector void unitize() { From 82a772fd217784847f2e3b493a328d216ebf9dcf Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:29:25 +1000 Subject: [PATCH 200/364] Use faster QString.arg multistring overload Thanks to Clazy --- src/app/dwg/qgsdwgimportdialog.cpp | 2 +- src/app/dwg/qgsdwgimporter.cpp | 28 ++++++++----------- src/core/auth/qgsauthmanager.cpp | 3 +- src/core/qgsdistancearea.cpp | 2 +- src/core/qgsproject.cpp | 2 +- src/core/qgsruntimeprofiler.cpp | 3 +- src/core/qgsvectorlayerlabeling.cpp | 2 +- src/gui/qgshelp.cpp | 2 +- src/gui/qgsmessagebar.cpp | 2 +- src/gui/qgsmessagelogviewer.cpp | 3 +- .../symbology/qgsstyleexportimportdialog.cpp | 2 +- src/gui/symbology/qgsstylemanagerdialog.cpp | 2 +- src/gui/symbology/qgssymbolslistwidget.cpp | 2 +- src/providers/ogr/qgsogrprovider.cpp | 2 +- .../spatialite/qgsspatialitedataitems.cpp | 2 +- 15 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/app/dwg/qgsdwgimportdialog.cpp b/src/app/dwg/qgsdwgimportdialog.cpp index 6430c685655..ab7033f7851 100644 --- a/src/app/dwg/qgsdwgimportdialog.cpp +++ b/src/app/dwg/qgsdwgimportdialog.cpp @@ -269,7 +269,7 @@ void QgsDwgImportDialog::on_pbImportDrawing_clicked() QgsVectorLayer *QgsDwgImportDialog::layer( QgsLayerTreeGroup *layerGroup, QString layerFilter, QString table ) { - QgsVectorLayer *l = new QgsVectorLayer( QString( "%1|layername=%2" ).arg( leDatabase->text() ).arg( table ), table, "ogr", false ); + QgsVectorLayer *l = new QgsVectorLayer( QString( "%1|layername=%2" ).arg( leDatabase->text(), table ), table, "ogr", false ); l->setSubsetString( QString( "%1space=0 AND block=-1" ).arg( layerFilter ) ); if ( l->featureCount() == 0 ) diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index 0c0fcc59fc3..02b7382620a 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -99,9 +99,7 @@ bool QgsDwgImporter::exec( QString sql, bool logError ) if ( logError ) { LOG( QObject::tr( "SQL statement failed\nDatabase:%1\nSQL:%2\nError:%3" ) - .arg( mDatabase ) - .arg( sql ) - .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); + .arg( mDatabase, sql, QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); } return false; } @@ -127,9 +125,7 @@ OGRLayerH QgsDwgImporter::query( QString sql ) return layer; LOG( QObject::tr( "SQL statement failed\nDatabase:%1\nSQL:%2\nError:%3" ) - .arg( mDatabase ) - .arg( sql ) - .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); + .arg( mDatabase, sql, QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); OGR_DS_ReleaseResultSet( mDs, layer ); @@ -145,8 +141,7 @@ void QgsDwgImporter::startTransaction() if ( !mInTransaction ) { LOG( QObject::tr( "Could not start transaction\nDatabase:%1\nError:%2" ) - .arg( mDatabase ) - .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); + .arg( mDatabase, QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); } } @@ -157,8 +152,7 @@ void QgsDwgImporter::commitTransaction() if ( mInTransaction && GDALDatasetCommitTransaction( mDs ) != OGRERR_NONE ) { LOG( QObject::tr( "Could not commit transaction\nDatabase:%1\nError:%2" ) - .arg( mDatabase ) - .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); + .arg( mDatabase, QString::fromUtf8( CPLGetLastErrorMsg() ) ) ); } mInTransaction = false; } @@ -608,7 +602,7 @@ bool QgsDwgImporter::import( const QString &drawing, QString &error, bool doExpa OGR_F_Destroy( f ); - LOG( QObject::tr( "Updating database from %1 [%2]." ).arg( drawing ).arg( fi.lastModified().toString() ) ); + LOG( QObject::tr( "Updating database from %1 [%2]." ).arg( drawing, fi.lastModified().toString() ) ); DRW::error result( DRW::BAD_NONE ); @@ -752,9 +746,9 @@ void QgsDwgImporter::addHeader( const DRW_Header *data ) case DRW_Variant::COORD: v = QString( "%1,%2,%3" ) - .arg( qgsDoubleToString( it->second->content.v->x ) ) - .arg( qgsDoubleToString( it->second->content.v->y ) ) - .arg( qgsDoubleToString( it->second->content.v->z ) ); + .arg( qgsDoubleToString( it->second->content.v->x ), + qgsDoubleToString( it->second->content.v->y ), + qgsDoubleToString( it->second->content.v->z ) ); break; case DRW_Variant::INVALID: @@ -767,9 +761,9 @@ void QgsDwgImporter::addHeader( const DRW_Header *data ) if ( OGR_L_CreateFeature( layer, f ) != OGRERR_NONE ) { LOG( QObject::tr( "Could not add %3 %1 [%2]" ) - .arg( k ) - .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) - .arg( QObject::tr( "header record" ) ) + .arg( k, + QString::fromUtf8( CPLGetLastErrorMsg() ), + QObject::tr( "header record" ) ) ); } diff --git a/src/core/auth/qgsauthmanager.cpp b/src/core/auth/qgsauthmanager.cpp index 8659461c914..053790bf397 100644 --- a/src/core/auth/qgsauthmanager.cpp +++ b/src/core/auth/qgsauthmanager.cpp @@ -3060,8 +3060,7 @@ void QgsAuthManager::passwordHelperProcessError() mPasswordHelperErrorMessage = tr( "There was an error and integration with your %1 system has been disabled. " "You can re-enable it at any time through the \"Utilities\" menu " "in the Authentication pane of the options dialog. %2" ) - .arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) - .arg( mPasswordHelperErrorMessage ); + .arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, mPasswordHelperErrorMessage ); } if ( mPasswordHelperErrorCode != QKeychain::NoError ) { diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index d17b3d678c3..f6e6ab03121 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -84,7 +84,7 @@ bool QgsDistanceArea::setEllipsoid( const QString &ellipsoid ) // Also, b = a-(a/invf) bool QgsDistanceArea::setEllipsoid( double semiMajor, double semiMinor ) { - mEllipsoid = QStringLiteral( "PARAMETER:%1:%2" ).arg( qgsDoubleToString( semiMajor ) ).arg( qgsDoubleToString( semiMinor ) ); + mEllipsoid = QStringLiteral( "PARAMETER:%1:%2" ).arg( qgsDoubleToString( semiMajor ), qgsDoubleToString( semiMinor ) ); mSemiMajor = semiMajor; mSemiMinor = semiMinor; mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor ); diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index 8c359198408..5447ac8a997 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -808,7 +808,7 @@ bool QgsProject::readProjectFile( const QString &filename ) #endif QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" ) - .arg( projectFile.fileName() ).arg( errorMsg ).arg( line ).arg( column ); + .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column ); QgsDebugMsg( errorString ); diff --git a/src/core/qgsruntimeprofiler.cpp b/src/core/qgsruntimeprofiler.cpp index 4fcaff9c87b..7dcf0f9cd9f 100644 --- a/src/core/qgsruntimeprofiler.cpp +++ b/src/core/qgsruntimeprofiler.cpp @@ -43,8 +43,7 @@ void QgsRuntimeProfiler::end() name.prepend( mGroupPrefix ); double timing = mProfileTime.elapsed() / 1000.0; mProfileTimes.append( QPair( name, timing ) ); - QString message = QStringLiteral( "PROFILE: %1 - %2" ).arg( name ).arg( timing ); - QgsDebugMsg( message ); + QgsDebugMsg( QStringLiteral( "PROFILE: %1 - %2" ).arg( name ).arg( timing ) ); } void QgsRuntimeProfiler::clear() diff --git a/src/core/qgsvectorlayerlabeling.cpp b/src/core/qgsvectorlayerlabeling.cpp index c6e1b74c34c..5af5ae87341 100644 --- a/src/core/qgsvectorlayerlabeling.cpp +++ b/src/core/qgsvectorlayerlabeling.cpp @@ -529,7 +529,7 @@ void QgsVectorLayerSimpleLabeling::toSld( QDomNode &parent, const QgsStringMap & x += fontSize / 2; y += fontSize; } - QString resizeSpec = QString( "%1 %2" ).arg( qgsDoubleToString( x, 2 ) ).arg( qgsDoubleToString( y, 2 ) ); + QString resizeSpec = QString( "%1 %2" ).arg( qgsDoubleToString( x, 2 ), qgsDoubleToString( y, 2 ) ); QDomElement voMargin = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "graphic-margin" ), resizeSpec ); textSymbolizerElement.appendChild( voMargin ); } diff --git a/src/gui/qgshelp.cpp b/src/gui/qgshelp.cpp index f188d6f76c8..5977ebafa35 100644 --- a/src/gui/qgshelp.cpp +++ b/src/gui/qgshelp.cpp @@ -71,7 +71,7 @@ QUrl QgsHelp::helpUrl( const QString &key ) } fullPath.replace( QRegularExpression( "(\\$\\$)" ), "$" ); - helpPath = QStringLiteral( "%1/%2" ).arg( fullPath ).arg( key ); + helpPath = QStringLiteral( "%1/%2" ).arg( fullPath, key ); if ( helpPath.startsWith( QStringLiteral( "http" ) ) ) { diff --git a/src/gui/qgsmessagebar.cpp b/src/gui/qgsmessagebar.cpp index 116b23817c7..09aeee60e25 100644 --- a/src/gui/qgsmessagebar.cpp +++ b/src/gui/qgsmessagebar.cpp @@ -272,7 +272,7 @@ void QgsMessageBar::pushItem( QgsMessageBarItem *item ) // Log all messages that are sent to the message bar into the message log so the // user can get them back easier. - QString formattedTitle = QString( "%1 : %2" ).arg( item->title() ).arg( item->text() ); + QString formattedTitle = QString( "%1 : %2" ).arg( item->title(), item->text() ); QgsMessageLog::MessageLevel level; switch ( item->level() ) { diff --git a/src/gui/qgsmessagelogviewer.cpp b/src/gui/qgsmessagelogviewer.cpp index 534fc0ab682..98de3029867 100644 --- a/src/gui/qgsmessagelogviewer.cpp +++ b/src/gui/qgsmessagelogviewer.cpp @@ -93,8 +93,7 @@ void QgsMessageLogViewer::logMessage( const QString &message, const QString &tag } QString prefix = QStringLiteral( "%1\t%2\t" ) - .arg( QDateTime::currentDateTime().toString( Qt::ISODate ) ) - .arg( levelString ); + .arg( QDateTime::currentDateTime().toString( Qt::ISODate ), levelString ); QString cleanedMessage = message; cleanedMessage = cleanedMessage.prepend( prefix ).replace( '\n', QLatin1String( "\n\t\t\t" ) ); w->appendPlainText( cleanedMessage ); diff --git a/src/gui/symbology/qgsstyleexportimportdialog.cpp b/src/gui/symbology/qgsstyleexportimportdialog.cpp index 71b715997fa..895a9cc53d8 100644 --- a/src/gui/symbology/qgsstyleexportimportdialog.cpp +++ b/src/gui/symbology/qgsstyleexportimportdialog.cpp @@ -200,7 +200,7 @@ bool QgsStyleExportImportDialog::populateStyles( QgsStyle *style ) QStandardItem *item = new QStandardItem( name ); QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, listItems->iconSize(), 15 ); item->setIcon( icon ); - item->setToolTip( QString( "%1
      %2" ).arg( name ).arg( tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) ); + item->setToolTip( QString( "%1
      %2" ).arg( name, tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) ); // Set font to 10points to show reasonable text QFont itemFont = item->font(); itemFont.setPointSize( 10 ); diff --git a/src/gui/symbology/qgsstylemanagerdialog.cpp b/src/gui/symbology/qgsstylemanagerdialog.cpp index 57dddf21ff1..3797095e8ae 100644 --- a/src/gui/symbology/qgsstylemanagerdialog.cpp +++ b/src/gui/symbology/qgsstylemanagerdialog.cpp @@ -286,7 +286,7 @@ void QgsStyleManagerDialog::populateSymbols( const QStringList &symbolNames, boo item->setIcon( icon ); item->setData( name ); // used to find out original name when user edited the name item->setCheckable( check ); - item->setToolTip( QString( "%1
      %2" ).arg( name ).arg( tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) ); + item->setToolTip( QString( "%1
      %2" ).arg( name, tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) ); // add to model model->appendRow( item ); } diff --git a/src/gui/symbology/qgssymbolslistwidget.cpp b/src/gui/symbology/qgssymbolslistwidget.cpp index 0a33a5adb61..1832f38e3c9 100644 --- a/src/gui/symbology/qgssymbolslistwidget.cpp +++ b/src/gui/symbology/qgssymbolslistwidget.cpp @@ -243,7 +243,7 @@ void QgsSymbolsListWidget::populateSymbols( const QStringList &names ) QStandardItem *item = new QStandardItem( names[i] ); item->setData( names[i], Qt::UserRole ); //so we can load symbol with that name item->setText( names[i] ); - item->setToolTip( QString( "%1
      %2" ).arg( names[i] ).arg( tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) ); + item->setToolTip( QString( "%1
      %2" ).arg( names[i], tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) ); item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); // Set font to 10points to show reasonable text QFont itemFont = item->font(); diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 3c55c8e5f8d..da4f637a89a 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -253,7 +253,7 @@ QgsVectorLayerExporter::ExportError QgsOgrProvider::createEmptyLayer( const QStr OGR_DS_Destroy( hDS ); if ( errorMessage ) *errorMessage += QObject::tr( "Layer %2 of %1 exists and overwrite flag is false." ) - .arg( uri ).arg( layerName ); + .arg( uri, layerName ); return QgsVectorLayerExporter::ErrCreateDataSource; } OGR_DS_Destroy( hDS ); diff --git a/src/providers/spatialite/qgsspatialitedataitems.cpp b/src/providers/spatialite/qgsspatialitedataitems.cpp index dd0aa3fb08c..76135c3b65c 100644 --- a/src/providers/spatialite/qgsspatialitedataitems.cpp +++ b/src/providers/spatialite/qgsspatialitedataitems.cpp @@ -219,7 +219,7 @@ bool QgsSLConnectionItem::handleDrop( const QMimeData *data, Qt::DropAction ) QgsVectorLayer *srcLayer = u.vectorLayer( owner, error ); if ( !srcLayer ) { - importResults.append( tr( "%1: %2" ).arg( u.name ).arg( error ) ); + importResults.append( tr( "%1: %2" ).arg( u.name, error ) ); hasError = true; continue; } From be5d2979b93ecf5acc923c92816d3e6918747355 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:30:40 +1000 Subject: [PATCH 201/364] Fix some Clazy detaching container warnings (not all are valid, but at worst this flips some more Q_FOREACH uses to c++11 for loops) --- src/app/dwg/qgsdwgimportdialog.cpp | 2 +- src/app/dwg/qgsdwgimporter.cpp | 2 +- src/app/qgscrashreport.cpp | 2 +- src/app/qgsjoindialog.cpp | 3 ++- src/app/qgsmaplayerstyleguiutils.cpp | 2 +- src/core/composer/qgscomposerattributetablev2.cpp | 7 ++++--- src/core/providers/memory/qgsmemoryproviderutils.cpp | 2 +- src/core/qgsattributetableconfig.cpp | 2 +- src/core/qgsdataitem.cpp | 4 +--- src/core/qgsgml.cpp | 2 +- src/core/qgsjsonutils.cpp | 2 +- src/core/qgsofflineediting.cpp | 3 ++- src/core/qgsprojectproperty.cpp | 10 +++++++--- src/core/qgsvectorlayer.cpp | 2 +- src/core/qgsvectorlayereditbuffer.cpp | 2 +- src/core/qgsvectorlayerjoinbuffer.cpp | 6 ++++-- src/core/qgsvectorlayerjoininfo.cpp | 3 ++- src/core/qgsvirtuallayerdefinitionutils.cpp | 6 ++++-- src/core/qgsziputils.cpp | 2 +- src/gui/qgsattributeform.cpp | 3 ++- src/gui/qgsfilewidget.cpp | 7 +++++-- src/gui/qgsmaptoolidentify.cpp | 7 ++++--- src/gui/qgsoptionsdialogbase.cpp | 2 +- .../symbology/qgspointdisplacementrendererwidget.cpp | 3 ++- src/providers/grass/qgsgrass.cpp | 2 +- src/providers/grass/qgsgrassvectormaplayer.cpp | 8 ++++---- src/providers/grass/qgsgrassvectormaplayer.h | 2 +- src/providers/wfs/qgswfsshareddata.cpp | 2 +- 28 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/app/dwg/qgsdwgimportdialog.cpp b/src/app/dwg/qgsdwgimportdialog.cpp index ab7033f7851..42e11961204 100644 --- a/src/app/dwg/qgsdwgimportdialog.cpp +++ b/src/app/dwg/qgsdwgimportdialog.cpp @@ -471,7 +471,7 @@ void QgsDwgImportDialog::on_buttonBox_accepted() QgsLayerTreeGroup *dwgGroup = QgisApp::instance()->layerTreeView()->layerTreeModel()->rootGroup()->addGroup( leLayerGroup->text() ); Q_ASSERT( dwgGroup ); - Q_FOREACH ( QString layer, layers.keys() ) + Q_FOREACH ( const QString &layer, layers.keys() ) { createGroup( dwgGroup, layer, QStringList( layer ), layers[layer] ); } diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index 02b7382620a..0056055ed9f 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -2606,7 +2606,7 @@ bool QgsDwgImporter::expandInserts( QString &error ) QTransform t; t.translate( p.x(), p.y() ).scale( xscale, yscale ).rotateRadians( angle ); - Q_FOREACH ( QString name, QStringList() << "hatches" << "lines" << "polylines" << "texts" << "points" ) + Q_FOREACH ( const QString &name, QStringList() << "hatches" << "lines" << "polylines" << "texts" << "points" ) { OGRLayerH src = OGR_DS_ExecuteSQL( mDs, QString( "SELECT * FROM %1 WHERE block=%2" ).arg( name ).arg( handle ).toUtf8().constData(), nullptr, nullptr ); if ( !src ) diff --git a/src/app/qgscrashreport.cpp b/src/app/qgscrashreport.cpp index c1a8a55f7b0..daed5c51ac0 100644 --- a/src/app/qgscrashreport.cpp +++ b/src/app/qgscrashreport.cpp @@ -108,7 +108,7 @@ const QString QgsCrashReport::toHtml() const } QString report; - Q_FOREACH ( QString line, reportData ) + Q_FOREACH ( const QString &line, reportData ) { report += line + "
      "; } diff --git a/src/app/qgsjoindialog.cpp b/src/app/qgsjoindialog.cpp index abda87d9a6c..7633b3093ef 100644 --- a/src/app/qgsjoindialog.cpp +++ b/src/app/qgsjoindialog.cpp @@ -171,7 +171,8 @@ void QgsJoinDialog::joinedLayerChanged( QgsMapLayer *layer ) mUseJoinFieldsSubset->setChecked( false ); QStandardItemModel *subsetModel = new QStandardItemModel( this ); - Q_FOREACH ( const QgsField &field, vLayer->fields() ) + const QgsFields layerFields = vLayer->fields(); + for ( const QgsField &field : layerFields ) { QStandardItem *subsetItem = new QStandardItem( field.name() ); subsetItem->setCheckable( true ); diff --git a/src/app/qgsmaplayerstyleguiutils.cpp b/src/app/qgsmaplayerstyleguiutils.cpp index cb4b0bff3d8..7bde441fd2e 100644 --- a/src/app/qgsmaplayerstyleguiutils.cpp +++ b/src/app/qgsmaplayerstyleguiutils.cpp @@ -62,7 +62,7 @@ QList QgsMapLayerStyleGuiUtils::actionsUseStyle( QgsMapLayer *layer, bool onlyOneStyle = mgr->styles().count() == 1; QList actions; - Q_FOREACH ( QString name, mgr->styles() ) + Q_FOREACH ( const QString &name, mgr->styles() ) { bool active = name == mgr->currentStyle(); QAction *actionUse = new QAction( name, parent ); diff --git a/src/core/composer/qgscomposerattributetablev2.cpp b/src/core/composer/qgscomposerattributetablev2.cpp index 7972421f986..f2e740b7f77 100644 --- a/src/core/composer/qgscomposerattributetablev2.cpp +++ b/src/core/composer/qgscomposerattributetablev2.cpp @@ -201,7 +201,8 @@ void QgsComposerAttributeTableV2::resetColumns() //rebuild columns list from vector layer fields int idx = 0; - Q_FOREACH ( const QgsField &field, source->fields() ) + const QgsFields sourceFields = source->fields(); + for ( const auto &field : sourceFields ) { QString currentAlias = source->attributeDisplayName( idx ); QgsComposerTableColumn *col = new QgsComposerTableColumn; @@ -319,7 +320,7 @@ void QgsComposerAttributeTableV2::setDisplayedFields( const QStringList &fields, qDeleteAll( mColumns ); mColumns.clear(); - QgsFields layerFields = source->fields(); + const QgsFields layerFields = source->fields(); if ( !fields.isEmpty() ) { @@ -340,7 +341,7 @@ void QgsComposerAttributeTableV2::setDisplayedFields( const QStringList &fields, { //resetting, so add all attributes to columns int idx = 0; - Q_FOREACH ( const QgsField &field, layerFields ) + for ( const QgsField &field : layerFields ) { QString currentAlias = source->attributeDisplayName( idx ); QgsComposerTableColumn *col = new QgsComposerTableColumn; diff --git a/src/core/providers/memory/qgsmemoryproviderutils.cpp b/src/core/providers/memory/qgsmemoryproviderutils.cpp index b0eb108be55..c4782032411 100644 --- a/src/core/providers/memory/qgsmemoryproviderutils.cpp +++ b/src/core/providers/memory/qgsmemoryproviderutils.cpp @@ -61,7 +61,7 @@ QgsVectorLayer *QgsMemoryProviderUtils::createMemoryLayer( const QString &name, { parts << QStringLiteral( "crs=" ) + crs.authid(); } - Q_FOREACH ( const QgsField &field, fields ) + for ( const auto &field : fields ) { parts << QStringLiteral( "field=%1:%2" ).arg( field.name(), memoryLayerFieldType( field.type() ) ); } diff --git a/src/core/qgsattributetableconfig.cpp b/src/core/qgsattributetableconfig.cpp index 71af24c4676..c6aa0d2b00b 100644 --- a/src/core/qgsattributetableconfig.cpp +++ b/src/core/qgsattributetableconfig.cpp @@ -80,7 +80,7 @@ void QgsAttributeTableConfig::update( const QgsFields &fields ) } } - Q_FOREACH ( const QgsField &field, fields ) + for ( const auto &field : fields ) { if ( !columns.contains( field.name() ) ) { diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 841903efad2..4e5cffbdc98 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -1135,10 +1135,8 @@ void QgsZipItem::init() // keys << "ogr" << "gdal"; keys << QStringLiteral( "gdal" ) << QStringLiteral( "ogr" ); - QStringList::const_iterator i; - for ( i = keys.begin(); i != keys.end(); ++i ) + for ( const auto &k : qgsAsConst( keys ) ) { - QString k( *i ); QgsDebugMsgLevel( "provider " + k, 3 ); // some providers hangs with empty uri (PostGIS) etc... // -> using libraries directly diff --git a/src/core/qgsgml.cpp b/src/core/qgsgml.cpp index 30ffb698aa9..134ef15145b 100644 --- a/src/core/qgsgml.cpp +++ b/src/core/qgsgml.cpp @@ -603,7 +603,7 @@ void QgsGmlStreamingParser::startElement( const XML_Char *el, const XML_Char **a { QString currentTypename( QString::fromUtf8( pszLocalName, localNameLen ) ); QMap< QString, LayerProperties >::const_iterator iter = mMapTypeNameToProperties.constFind( currentTypename ); - if ( iter != mMapTypeNameToProperties.end() ) + if ( iter != mMapTypeNameToProperties.constEnd() ) { mFeatureTupleDepth = mParseDepth; mCurrentTypename = currentTypename; diff --git a/src/core/qgsjsonutils.cpp b/src/core/qgsjsonutils.cpp index 6654658164c..3aa91618b38 100644 --- a/src/core/qgsjsonutils.cpp +++ b/src/core/qgsjsonutils.cpp @@ -178,7 +178,7 @@ QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVarian QgsFeatureIterator it = childLayer->getFeatures( req ); QVector attributeWidgetCaches; int fieldIndex = 0; - Q_FOREACH ( const QgsField &field, childLayer->fields() ) + for ( const auto &field : childLayer->fields() ) { QgsEditorWidgetSetup setup = field.editorWidgetSetup(); QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() ); diff --git a/src/core/qgsofflineediting.cpp b/src/core/qgsofflineediting.cpp index 4a016e2d2d1..ccecbd4dee5 100644 --- a/src/core/qgsofflineediting.cpp +++ b/src/core/qgsofflineediting.cpp @@ -497,7 +497,8 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit // create table QString sql = QStringLiteral( "CREATE TABLE '%1' (" ).arg( tableName ); QString delim = QLatin1String( "" ); - Q_FOREACH ( const QgsField &field, layer->dataProvider()->fields() ) + const QgsFields providerFields = layer->dataProvider()->fields(); + for ( const auto &field : providerFields ) { QString dataType = QLatin1String( "" ); QVariant::Type type = field.type(); diff --git a/src/core/qgsprojectproperty.cpp b/src/core/qgsprojectproperty.cpp index d311fd64f38..43b2a5c2b4f 100644 --- a/src/core/qgsprojectproperty.cpp +++ b/src/core/qgsprojectproperty.cpp @@ -27,22 +27,26 @@ QgsProjectProperty::QgsProjectProperty() //NOLINT void QgsProjectPropertyValue::dump( int tabs ) const { + Q_UNUSED( tabs ); +#ifdef QGISDEBUG + QString tabString; tabString.fill( '\t', tabs ); if ( QVariant::StringList == mValue.type() ) { - QStringList sl = mValue.toStringList(); + const QStringList sl = mValue.toStringList(); - for ( QStringList::const_iterator i = sl.begin(); i != sl.end(); ++i ) + for ( const auto &string : sl ) { - QgsDebugMsg( QString( "%1[%2] " ).arg( tabString, *i ) ); + QgsDebugMsg( QString( "%1[%2] " ).arg( tabString, string ) ); } } else { QgsDebugMsg( QString( "%1%2" ).arg( tabString, mValue.toString() ) ); } +#endif } bool QgsProjectPropertyValue::readXml( const QDomNode &keyNode ) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index fab196aa9b3..62c644b7b7a 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -257,7 +257,7 @@ QgsVectorLayer *QgsVectorLayer::clone() const layer->setDefaultValueExpression( i, defaultValueExpression( i ) ); QMap< QgsFieldConstraints::Constraint, QgsFieldConstraints::ConstraintStrength> constraints = fieldConstraintsAndStrength( i ); - for ( QgsFieldConstraints::Constraint c : constraints.keys() ) + Q_FOREACH ( QgsFieldConstraints::Constraint c, constraints.keys() ) { layer->setFieldConstraint( i, c, constraints.value( c ) ); } diff --git a/src/core/qgsvectorlayereditbuffer.cpp b/src/core/qgsvectorlayereditbuffer.cpp index 989dcb9a374..82c0dc19409 100644 --- a/src/core/qgsvectorlayereditbuffer.cpp +++ b/src/core/qgsvectorlayereditbuffer.cpp @@ -320,7 +320,7 @@ bool QgsVectorLayerEditBuffer::commitChanges( QStringList &commitErrors ) { if ( provider->doesStrictFeatureTypeCheck() ) { - for ( QgsFeature f : mAddedFeatures ) + for ( const auto &f : qgsAsConst( mAddedFeatures ) ) { if ( ( ! f.hasGeometry() ) || ( f.geometry().wkbType() == provider->wkbType() ) ) diff --git a/src/core/qgsvectorlayerjoinbuffer.cpp b/src/core/qgsvectorlayerjoinbuffer.cpp index ea4350a6fbe..620db65ef7c 100644 --- a/src/core/qgsvectorlayerjoinbuffer.cpp +++ b/src/core/qgsvectorlayerjoinbuffer.cpp @@ -564,7 +564,8 @@ bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeature } else { - Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + const QgsFields joinFields = joinFeature.fields(); + for ( const auto &field : joinFields ) { QVariant newValue = joinFeature.attribute( field.name() ); int fieldIndex = joinLayer->fields().indexOf( field.name() ); @@ -576,7 +577,8 @@ bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeature { // joined feature is added only if one of its field is not null bool notNullFields = false; - Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + const QgsFields joinFields = joinFeature.fields(); + for ( const auto &field : joinFields ) { if ( field.name() == info.joinFieldName() ) continue; diff --git a/src/core/qgsvectorlayerjoininfo.cpp b/src/core/qgsvectorlayerjoininfo.cpp index 7b3bfda29b3..f2556331ef3 100644 --- a/src/core/qgsvectorlayerjoininfo.cpp +++ b/src/core/qgsvectorlayerjoininfo.cpp @@ -56,7 +56,8 @@ QgsFeature QgsVectorLayerJoinInfo::extractJoinedFeature( const QgsFeature &featu joinFeature.setFields( joinLayer()->fields() ); joinFeature.setAttribute( joinFieldName(), idFieldValue ); - Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + const QgsFields joinFields = joinFeature.fields(); + for ( const auto &field : joinFields ) { const QString prefixedName = prefixedFieldName( field ); diff --git a/src/core/qgsvirtuallayerdefinitionutils.cpp b/src/core/qgsvirtuallayerdefinitionutils.cpp index 1f37e690294..09408c4d811 100644 --- a/src/core/qgsvirtuallayerdefinitionutils.cpp +++ b/src/core/qgsvirtuallayerdefinitionutils.cpp @@ -48,7 +48,8 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe def.setUid( uid ); } } - Q_FOREACH ( const QgsField &f, layer->dataProvider()->fields() ) + const QgsFields providerFields = layer->dataProvider()->fields(); + for ( const auto &f : providerFields ) { columns << "t." + f.name(); } @@ -72,7 +73,8 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinitionUtils::fromJoinedLayer( QgsVe } else { - Q_FOREACH ( const QgsField &f, joinedLayer->fields() ) + const QgsFields joinFields = joinedLayer->fields(); + for ( const QgsField &f : joinFields ) { if ( f.name() == join.joinFieldName() ) continue; diff --git a/src/core/qgsziputils.cpp b/src/core/qgsziputils.cpp index 3f4803cabdb..17585a8e327 100644 --- a/src/core/qgsziputils.cpp +++ b/src/core/qgsziputils.cpp @@ -132,7 +132,7 @@ bool QgsZipUtils::zip( const QString &zipFilename, const QStringList &files ) if ( rc == ZIP_ER_OK && z != NULL ) { - Q_FOREACH ( QString file, files ) + for ( const auto &file : files ) { QFileInfo fileInfo( file ); if ( !fileInfo.exists() ) diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index f7cb3e52bf1..11cbc283c32 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -2009,7 +2009,8 @@ void QgsAttributeForm::updateJoinedFields( const QgsEditorWidgetWrapper &eww ) } else { - Q_FOREACH ( const QgsField &field, joinFeature.fields() ) + const QgsFields joinFields = joinFeature.fields(); + for ( const QgsField &field : joinFields ) { QString prefixedName = info->prefixedFieldName( field ); QVariant val; diff --git a/src/gui/qgsfilewidget.cpp b/src/gui/qgsfilewidget.cpp index 4895d4a62d7..2c6edaff7ef 100644 --- a/src/gui/qgsfilewidget.cpp +++ b/src/gui/qgsfilewidget.cpp @@ -80,9 +80,12 @@ QString QgsFileWidget::filePath() QStringList QgsFileWidget::splitFilePaths( const QString &path ) { QStringList paths; - for ( auto pathsPart : path.split( QRegExp( "\"\\s+\"" ), QString::SkipEmptyParts ) ) + const QStringList pathParts = path.split( QRegExp( "\"\\s+\"" ), QString::SkipEmptyParts ); + for ( const auto &pathsPart : pathParts ) { - paths.append( pathsPart.remove( QRegExp( "(^\\s*\")|(\"\\s*)" ) ) ); + QString cleaned = pathsPart; + cleaned.remove( QRegExp( "(^\\s*\")|(\"\\s*)" ) ); + paths.append( cleaned ); } return paths; } diff --git a/src/gui/qgsmaptoolidentify.cpp b/src/gui/qgsmaptoolidentify.cpp index bf0a94305af..80446f68a9b 100644 --- a/src/gui/qgsmaptoolidentify.cpp +++ b/src/gui/qgsmaptoolidentify.cpp @@ -610,11 +610,12 @@ bool QgsMapToolIdentify::identifyRasterLayer( QList *results, Qg } // list of feature stores for a single sublayer - QgsFeatureStoreList featureStoreList = values.value( i ).value(); + const QgsFeatureStoreList featureStoreList = values.value( i ).value(); - Q_FOREACH ( QgsFeatureStore featureStore, featureStoreList ) + for ( const QgsFeatureStore &featureStore : featureStoreList ) { - Q_FOREACH ( QgsFeature feature, featureStore.features() ) + const QgsFeatureList storeFeatures = featureStore.features(); + for ( QgsFeature feature : storeFeatures ) { attributes.clear(); // WMS sublayer and feature type, a sublayer may contain multiple feature types. diff --git a/src/gui/qgsoptionsdialogbase.cpp b/src/gui/qgsoptionsdialogbase.cpp index e7ef6372ff7..ac1c9c90fd6 100644 --- a/src/gui/qgsoptionsdialogbase.cpp +++ b/src/gui/qgsoptionsdialogbase.cpp @@ -232,7 +232,7 @@ void QgsOptionsDialogBase::searchText( const QString &text ) mOptListWidget->setRowHidden( r, !text.isEmpty() ); } - for ( QPair< QgsSearchHighlightOptionWidget *, int > rsw : mRegisteredSearchWidgets ) + for ( const QPair< QgsSearchHighlightOptionWidget *, int > &rsw : qgsAsConst( mRegisteredSearchWidgets ) ) { rsw.first->reset(); if ( !text.isEmpty() && rsw.first->searchHighlight( text ) ) diff --git a/src/gui/symbology/qgspointdisplacementrendererwidget.cpp b/src/gui/symbology/qgspointdisplacementrendererwidget.cpp index 44135b1fa3b..be6c155a403 100644 --- a/src/gui/symbology/qgspointdisplacementrendererwidget.cpp +++ b/src/gui/symbology/qgspointdisplacementrendererwidget.cpp @@ -72,7 +72,8 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto //insert attributes into combo box if ( layer ) { - Q_FOREACH ( const QgsField &f, layer->fields() ) + const QgsFields layerFields = layer->fields(); + for ( const QgsField &f : layerFields ) { mLabelFieldComboBox->addItem( f.name() ); } diff --git a/src/providers/grass/qgsgrass.cpp b/src/providers/grass/qgsgrass.cpp index 9bdb642ff21..e66f8726734 100644 --- a/src/providers/grass/qgsgrass.cpp +++ b/src/providers/grass/qgsgrass.cpp @@ -2369,7 +2369,7 @@ void QgsGrass::createTable( dbDriver *driver, const QString &tableName, const Qg } QStringList fieldsStringList; - Q_FOREACH ( const QgsField &field, fields ) + for ( const QgsField &field : fields ) { QString name = field.name().toLower().replace( QLatin1String( " " ), QLatin1String( "_" ) ); if ( name.at( 0 ).isDigit() ) diff --git a/src/providers/grass/qgsgrassvectormaplayer.cpp b/src/providers/grass/qgsgrassvectormaplayer.cpp index 8a9cb71073b..cd29765e3e1 100644 --- a/src/providers/grass/qgsgrassvectormaplayer.cpp +++ b/src/providers/grass/qgsgrassvectormaplayer.cpp @@ -332,10 +332,10 @@ void QgsGrassVectorMapLayer::close() } } -QStringList QgsGrassVectorMapLayer::fieldNames( QgsFields &fields ) +QStringList QgsGrassVectorMapLayer::fieldNames( const QgsFields &fields ) { QStringList list; - Q_FOREACH ( const QgsField &field, fields ) + for ( const QgsField &field : fields ) { list << field.name(); } @@ -598,7 +598,7 @@ void QgsGrassVectorMapLayer::createTable( const QgsFields &fields, QString &erro QgsFields catFields; catFields.append( QgsField( mFieldInfo->key, QVariant::Int, QStringLiteral( "integer" ) ) ); - Q_FOREACH ( const QgsField &field, fields ) + for ( const QgsField &field : fields ) { catFields.append( field ); } @@ -643,7 +643,7 @@ void QgsGrassVectorMapLayer::createTable( const QgsFields &fields, QString &erro if ( mFieldInfo ) { - Q_FOREACH ( const QgsField &field, fields ) + for ( const QgsField &field : fields ) { mTableFields.append( field ); mAttributeFields.append( field ); diff --git a/src/providers/grass/qgsgrassvectormaplayer.h b/src/providers/grass/qgsgrassvectormaplayer.h index c930050d62e..af6f6348ea1 100644 --- a/src/providers/grass/qgsgrassvectormaplayer.h +++ b/src/providers/grass/qgsgrassvectormaplayer.h @@ -65,7 +65,7 @@ class GRASS_LIB_EXPORT QgsGrassVectorMapLayer : public QObject * This fields are used by layers which are not editied to reflect current state of editing. */ QgsFields &tableFields() { return mTableFields; } - static QStringList fieldNames( QgsFields &fields ); + static QStringList fieldNames( const QgsFields &fields ); QMap > &attributes() { return mAttributes; } diff --git a/src/providers/wfs/qgswfsshareddata.cpp b/src/providers/wfs/qgswfsshareddata.cpp index 641237c7924..1c65d82d76c 100644 --- a/src/providers/wfs/qgswfsshareddata.cpp +++ b/src/providers/wfs/qgswfsshareddata.cpp @@ -371,7 +371,7 @@ bool QgsWFSSharedData::createCache() { mCacheTablename = QStringLiteral( "features" ); sql = QStringLiteral( "CREATE TABLE %1 (%2 INTEGER PRIMARY KEY" ).arg( mCacheTablename, fidName ); - Q_FOREACH ( const QgsField &field, cacheFields ) + for ( const QgsField &field : qgsAsConst( cacheFields ) ) { QString type( QStringLiteral( "VARCHAR" ) ); if ( field.type() == QVariant::Int ) From a4357e37fe8292114dcbc52ffe2fbcdf8902a804 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:31:33 +1000 Subject: [PATCH 202/364] Remove some unused variables Thanks to Clazy --- src/app/composer/qgscomposer.cpp | 1 - src/app/dwg/qgsdwgimporter.cpp | 7 +++---- src/app/qgisapp.cpp | 3 +-- src/app/qgscrashreport.cpp | 2 ++ src/app/qgsfieldsproperties.cpp | 1 - src/app/qgslayerstylingwidget.cpp | 1 - src/app/qgsselectbyformdialog.cpp | 2 -- src/app/qgsvectorlayerproperties.cpp | 1 - src/core/composer/qgscomposermap.cpp | 2 -- src/core/composer/qgscomposition.cpp | 1 - src/core/qgsproject.cpp | 1 - src/core/scalebar/qgsnumericscalebarrenderer.cpp | 2 -- src/gui/qgsexpressionselectiondialog.cpp | 2 -- src/gui/raster/qgsrendererrasterpropertieswidget.cpp | 10 ++++++---- src/gui/symbology/qgsstylemanagerdialog.cpp | 2 +- src/providers/grass/qgsgrassimport.cpp | 6 +++++- src/providers/mssql/qgsmssqlprovider.cpp | 9 +++------ src/providers/virtual/qgsvirtuallayerprovider.cpp | 1 - src/providers/wfs/qgswfsshareddata.cpp | 2 -- 19 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp index 31386670776..394875ad291 100644 --- a/src/app/composer/qgscomposer.cpp +++ b/src/app/composer/qgscomposer.cpp @@ -2125,7 +2125,6 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode ) QgsSettings myQSettings; QString lastUsedDir = myQSettings.value( QStringLiteral( "UI/lastSaveAtlasAsImagesDir" ), QDir::homePath() ).toString(); - QString lastUsedFormat = myQSettings.value( QStringLiteral( "UI/lastSaveAtlasAsImagesFormat" ), "jpg" ).toString(); QFileDialog dlg( this, tr( "Export atlas to directory" ) ); dlg.setFileMode( QFileDialog::Directory ); diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index 0056055ed9f..c380393a369 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -215,7 +215,7 @@ bool QgsDwgImporter::import( const QString &drawing, QString &error, bool doExpa } OGRFeatureDefnH dfn = OGR_L_GetLayerDefn( layer ); - int pathIdx = OGR_FD_GetFieldIndex( dfn, "path" ); + //int pathIdx = OGR_FD_GetFieldIndex( dfn, "path" ); int lastmodifiedIdx = OGR_FD_GetFieldIndex( dfn, "lastmodified" ); OGR_L_ResetReading( layer ); @@ -229,8 +229,6 @@ bool QgsDwgImporter::import( const QString &drawing, QString &error, bool doExpa return false; } - QString path = QString::fromUtf8( OGR_F_GetFieldAsString( f, pathIdx ) ); - int year, month, day, hour, minute, second, tzf; if ( !OGR_F_GetFieldAsDateTime( f, lastmodifiedIdx, &year, &month, &day, &hour, &minute, &second, &tzf ) ) { @@ -241,9 +239,10 @@ bool QgsDwgImporter::import( const QString &drawing, QString &error, bool doExpa return false; } - QDateTime lastModified( QDate( year, month, day ), QTime( hour, minute, second ) ); #if 0 + QDateTime lastModified( QDate( year, month, day ), QTime( hour, minute, second ) ); + QString path = QString::fromUtf8( OGR_F_GetFieldAsString( f, pathIdx ) ); if ( path == fi.canonicalPath() && fi.lastModified() <= lastModified ) { LOG( QObject::tr( "Drawing already uptodate in database." ) ); diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 30839c85dcd..e493c40c2ed 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -3354,8 +3354,7 @@ QgsMapCanvasDockWidget *QgisApp::createNewMapCanvasDock( const QString &name, bo { if ( canvas->objectName() == name ) { - QString errorMessage = tr( "A map canvas with name '%1' already exists!" ).arg( name ); - QgsDebugMsg( errorMessage ); + QgsDebugMsg( tr( "A map canvas with name '%1' already exists!" ).arg( name ) ); return nullptr; } } diff --git a/src/app/qgscrashreport.cpp b/src/app/qgscrashreport.cpp index daed5c51ac0..e4d9797ab0d 100644 --- a/src/app/qgscrashreport.cpp +++ b/src/app/qgscrashreport.cpp @@ -125,8 +125,10 @@ const QString QgsCrashReport::crashID() const // Hashes the full stack. Q_FOREACH ( const QgsStackTrace::StackLine &line, mStackTrace ) { +#if 0 QFileInfo fileInfo( line.fileName ); QString filename( fileInfo.fileName() ); +#endif data += line.symbolName; } diff --git a/src/app/qgsfieldsproperties.cpp b/src/app/qgsfieldsproperties.cpp index 2f1e3246534..589240629c4 100644 --- a/src/app/qgsfieldsproperties.cpp +++ b/src/app/qgsfieldsproperties.cpp @@ -987,7 +987,6 @@ void QgsFieldsProperties::apply() for ( int i = 0; i < mFieldsList->rowCount(); i++ ) { int idx = mFieldsList->item( i, AttrIdCol )->text().toInt(); - QString name = mLayer->fields().at( idx ).name(); FieldConfig cfg = configForRow( i ); editFormConfig.setReadOnly( idx, !cfg.mEditable ); diff --git a/src/app/qgslayerstylingwidget.cpp b/src/app/qgslayerstylingwidget.cpp index 69cca2eb207..817feb8f931 100644 --- a/src/app/qgslayerstylingwidget.cpp +++ b/src/app/qgslayerstylingwidget.cpp @@ -74,7 +74,6 @@ QgsLayerStylingWidget::QgsLayerStylingWidget( QgsMapCanvas *canvas, const QList< mStyleManagerFactory = new QgsLayerStyleManagerWidgetFactory(); - QList l; setPageFactories( pages ); connect( mUndoButton, &QAbstractButton::pressed, this, &QgsLayerStylingWidget::undo ); diff --git a/src/app/qgsselectbyformdialog.cpp b/src/app/qgsselectbyformdialog.cpp index d4ba60bb176..c13185f311c 100644 --- a/src/app/qgsselectbyformdialog.cpp +++ b/src/app/qgsselectbyformdialog.cpp @@ -67,8 +67,6 @@ void QgsSelectByFormDialog::setMapCanvas( QgsMapCanvas *canvas ) void QgsSelectByFormDialog::zoomToFeatures( const QString &filter ) { - QgsFeatureIds ids; - QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) ); QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( filter ) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 8ea09cc0cd1..760a6ebba9e 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -1285,7 +1285,6 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo QTreeWidgetItem *childFields = new QTreeWidgetItem(); childFields->setText( 0, "Joined fields" ); const QStringList *list = join.joinFieldNamesSubset(); - QString fields; if ( list ) childFields->setText( 1, QStringLiteral( "%1" ).arg( list->count() ) ); else diff --git a/src/core/composer/qgscomposermap.cpp b/src/core/composer/qgscomposermap.cpp index 4210221712a..0bcbc59790f 100644 --- a/src/core/composer/qgscomposermap.cpp +++ b/src/core/composer/qgscomposermap.cpp @@ -922,8 +922,6 @@ void QgsComposerMap::refreshMapExtents( const QgsExpressionContext *context ) const QgsExpressionContext *evalContext = context ? context : &scopedContext; //data defined map extents set? - QVariant exprVal; - QgsRectangle newExtent = *currentMapExtent(); bool useDdXMin = false; bool useDdXMax = false; diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp index 2e03e15d440..9bade0ed8c5 100644 --- a/src/core/composer/qgscomposition.cpp +++ b/src/core/composer/qgscomposition.cpp @@ -3207,7 +3207,6 @@ void QgsComposition::refreshPageSize( const QgsExpressionContext *context ) double pageWidth = mPageWidth; double pageHeight = mPageHeight; - QVariant exprVal; //in order of precedence - first consider predefined page size bool ok = false; QString presetString = mDataDefinedProperties.valueAsString( QgsComposerObject::PresetPaperSize, *evalContext, QString(), &ok ); diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index 5447ac8a997..1ffec6d4856 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -2156,7 +2156,6 @@ bool QgsProject::zip( const QString &filename ) archive->addFile( qgsFile.fileName() ); // zip - QString errMsg; if ( !archive->zip( filename ) ) { setError( tr( "Unable to perform zip" ) ); diff --git a/src/core/scalebar/qgsnumericscalebarrenderer.cpp b/src/core/scalebar/qgsnumericscalebarrenderer.cpp index 063e8e17826..4d258214b7e 100644 --- a/src/core/scalebar/qgsnumericscalebarrenderer.cpp +++ b/src/core/scalebar/qgsnumericscalebarrenderer.cpp @@ -60,8 +60,6 @@ void QgsNumericScaleBarRenderer::draw( QgsRenderContext &context, const QgsScale QSizeF QgsNumericScaleBarRenderer::calculateBoxSize( const QgsScaleBarSettings &settings, const QgsScaleBarRenderer::ScaleBarContext &scaleContext ) const { - QRectF rect; - double textWidth = QgsComposerUtils::textWidthMM( settings.font(), scaleText( scaleContext.scale ) ); double textHeight = QgsComposerUtils::fontAscentMM( settings.font() ); diff --git a/src/gui/qgsexpressionselectiondialog.cpp b/src/gui/qgsexpressionselectiondialog.cpp index 8bdd94ea650..85e96020e2b 100644 --- a/src/gui/qgsexpressionselectiondialog.cpp +++ b/src/gui/qgsexpressionselectiondialog.cpp @@ -124,8 +124,6 @@ void QgsExpressionSelectionDialog::on_mButtonZoomToFeatures_clicked() if ( mExpressionBuilder->expressionText().isEmpty() || !mMapCanvas ) return; - QgsFeatureIds ids; - QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) ); QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( mExpressionBuilder->expressionText() ) diff --git a/src/gui/raster/qgsrendererrasterpropertieswidget.cpp b/src/gui/raster/qgsrendererrasterpropertieswidget.cpp index 7ea971ae833..7b16415a333 100644 --- a/src/gui/raster/qgsrendererrasterpropertieswidget.cpp +++ b/src/gui/raster/qgsrendererrasterpropertieswidget.cpp @@ -365,13 +365,15 @@ void QgsRendererRasterPropertiesWidget::setRendererWidget( const QString &render // Compare used bands in new and old renderer and reset transparency dialog if different QgsRasterRenderer *oldRenderer = oldWidget->renderer(); QgsRasterRenderer *newRenderer = mRendererWidget->renderer(); +#if 0 QList oldBands = oldRenderer->usesBands(); QList newBands = newRenderer->usesBands(); -// if ( oldBands != newBands ) -// { -// populateTransparencyTable( newRenderer ); -// } + if ( oldBands != newBands ) + { + populateTransparencyTable( newRenderer ); + } +#endif delete oldRenderer; delete newRenderer; diff --git a/src/gui/symbology/qgsstylemanagerdialog.cpp b/src/gui/symbology/qgsstylemanagerdialog.cpp index 3797095e8ae..5e434b4868a 100644 --- a/src/gui/symbology/qgsstylemanagerdialog.cpp +++ b/src/gui/symbology/qgsstylemanagerdialog.cpp @@ -1538,4 +1538,4 @@ void QgsStyleManagerDialog::onClose() void QgsStyleManagerDialog::showHelp() { QgsHelp::openHelp( QStringLiteral( "working_with_vector/style_library.html#the-style-manager" ) ); -} \ No newline at end of file +} diff --git a/src/providers/grass/qgsgrassimport.cpp b/src/providers/grass/qgsgrassimport.cpp index 4fd679cd8d5..5609805f5ea 100644 --- a/src/providers/grass/qgsgrassimport.cpp +++ b/src/providers/grass/qgsgrassimport.cpp @@ -435,14 +435,16 @@ bool QgsGrassRasterImport::import() // TODO: best timeout? mProcess->waitForFinished( 30000 ); - QString stdoutString = mProcess->readAllStandardOutput().constData(); QString stderrString = mProcess->readAllStandardError().constData(); +#ifdef QGISDEBUG + QString stdoutString = mProcess->readAllStandardOutput().constData(); QString processResult = QStringLiteral( "exitStatus=%1, exitCode=%2, error=%3, errorString=%4 stdout=%5, stderr=%6" ) .arg( mProcess->exitStatus() ).arg( mProcess->exitCode() ) .arg( mProcess->error() ).arg( mProcess->errorString(), stdoutString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ), stderrString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ) ); QgsDebugMsg( "processResult: " + processResult ); +#endif if ( mProcess->exitStatus() != QProcess::NormalExit ) { @@ -716,11 +718,13 @@ bool QgsGrassVectorImport::import() QString stdoutString = mProcess->readAllStandardOutput().constData(); QString stderrString = mProcess->readAllStandardError().constData(); +#ifdef QGISDEBUG QString processResult = QStringLiteral( "exitStatus=%1, exitCode=%2, error=%3, errorString=%4 stdout=%5, stderr=%6" ) .arg( mProcess->exitStatus() ).arg( mProcess->exitCode() ) .arg( mProcess->error() ).arg( mProcess->errorString(), stdoutString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ), stderrString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ) ); QgsDebugMsg( "processResult: " + processResult ); +#endif if ( mProcess->exitStatus() != QProcess::NormalExit ) { diff --git a/src/providers/mssql/qgsmssqlprovider.cpp b/src/providers/mssql/qgsmssqlprovider.cpp index 31cd69190e8..325f30f78a8 100644 --- a/src/providers/mssql/qgsmssqlprovider.cpp +++ b/src/providers/mssql/qgsmssqlprovider.cpp @@ -2010,8 +2010,7 @@ QGISEXTERN bool saveStyle( const QString &uri, const QString &qmlStyle, const QS query.setForwardOnly( true ); if ( !query.exec( QStringLiteral( "SELECT COUNT(*) FROM information_schema.tables WHERE table_name= N'layer_styles'" ) ) ) { - QString msg = query.lastError().text(); - QgsDebugMsg( msg ); + QgsDebugMsg( query.lastError().text() ); return false; } if ( query.isActive() && query.next() && query.value( 0 ).toInt() == 0 ) @@ -2265,8 +2264,7 @@ QGISEXTERN int listStyles( const QString &uri, QStringList &ids, QStringList &na queryOk = query.exec( selectOthersQuery ); if ( !queryOk ) { - QString msg = query.lastError().text(); - QgsDebugMsg( msg ); + QgsDebugMsg( query.lastError().text() ); return -1; } QgsDebugMsg( query.isActive() && query.size() ); @@ -2299,8 +2297,7 @@ QGISEXTERN QString getStyleById( const QString &uri, QString styleId, QString &e bool queryOk = query.exec( selectQmlQuery ); if ( !queryOk ) { - QString msg = query.lastError().text(); - QgsDebugMsg( msg ); + QgsDebugMsg( query.lastError().text() ); errCause = query.lastError().text(); return QString(); } diff --git a/src/providers/virtual/qgsvirtuallayerprovider.cpp b/src/providers/virtual/qgsvirtuallayerprovider.cpp index 1e5f2494155..d8b613aaf93 100644 --- a/src/providers/virtual/qgsvirtuallayerprovider.cpp +++ b/src/providers/virtual/qgsvirtuallayerprovider.cpp @@ -313,7 +313,6 @@ bool QgsVirtualLayerProvider::createIt() } QgsFields tfields; - QList geometryFields; if ( !mDefinition.query().isEmpty() ) { // look for column types of the query diff --git a/src/providers/wfs/qgswfsshareddata.cpp b/src/providers/wfs/qgswfsshareddata.cpp index 1c65d82d76c..f24c0b8f5f0 100644 --- a/src/providers/wfs/qgswfsshareddata.cpp +++ b/src/providers/wfs/qgswfsshareddata.cpp @@ -683,7 +683,6 @@ QString QgsWFSSharedData::findGmlId( QgsFeatureId fid ) QgsFeatureIterator iterGmlIds( mCacheDataProvider->getFeatures( request ) ); QgsFeature gmlidFeature; - QSet setExistingGmlIds; while ( iterGmlIds.nextFeature( gmlidFeature ) ) { const QVariant &v = gmlidFeature.attributes().value( gmlidIdx ); @@ -1281,7 +1280,6 @@ QgsRectangle QgsWFSSingleFeatureRequest::getExtent() { QVector featurePtrList = parser->getAndStealReadyFeatures(); - QVector featureList; for ( int i = 0; i < featurePtrList.size(); i++ ) { QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair &featPair = featurePtrList[i]; From 5197b5fdabb81b9461b213a9d5f6776d96e9eb30 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:32:00 +1000 Subject: [PATCH 203/364] Fix detaching of temporary container Thanks to Clazy --- src/core/expression/qgsexpressionnodeimpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/expression/qgsexpressionnodeimpl.cpp b/src/core/expression/qgsexpressionnodeimpl.cpp index d7528d875ef..91b51128434 100644 --- a/src/core/expression/qgsexpressionnodeimpl.cpp +++ b/src/core/expression/qgsexpressionnodeimpl.cpp @@ -949,7 +949,7 @@ QSet QgsExpressionNodeFunction::referencedVariables() const { if ( !mArgs->list().isEmpty() ) { - QgsExpressionNodeLiteral *var = dynamic_cast( mArgs->list().first() ); + QgsExpressionNodeLiteral *var = dynamic_cast( mArgs->list().at( 0 ) ); if ( var ) return QSet() << var->value().toString(); } From ce4ac074cd40e328953dc6f6c7793786f05eb27f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:32:29 +1000 Subject: [PATCH 204/364] Don't use .lower() to perform case-insensitive string operations Use the faster versions with Qt::CaseInsensitive instead. These don't require a string copy and conversion to lowercase. Thanks to Clazy --- src/core/processing/qgsprocessingparameters.cpp | 2 +- src/core/qgsstacktrace.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 5caea31263e..59561855195 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -888,7 +888,7 @@ bool QgsProcessingParameters::parseScriptCodeParameterOptions( const QString &co name = m.captured( 1 ); QString tokens = m.captured( 2 ); - if ( tokens.toLower().startsWith( QStringLiteral( "optional" ) ) ) + if ( tokens.startsWith( QStringLiteral( "optional" ), Qt::CaseInsensitive ) ) { isOptional = true; tokens.remove( 0, 8 ); // length "optional" = 8 diff --git a/src/core/qgsstacktrace.cpp b/src/core/qgsstacktrace.cpp index 081d5fc90fb..3bb99fa8061 100644 --- a/src/core/qgsstacktrace.cpp +++ b/src/core/qgsstacktrace.cpp @@ -168,14 +168,14 @@ QgsStackTrace::QgsStackTrace() bool QgsStackTrace::StackLine::isQgisModule() const { - return moduleName.toLower().contains( "qgis" ); + return moduleName.contains( "qgis", Qt::CaseInsensitive ); } bool QgsStackTrace::StackLine::isValid() const { - return !( fileName.toLower().contains( "exe_common" ) || - fileName.toLower().contains( "unknown" ) || - lineNumber.toLower().contains( "unknown" ) ); + return !( fileName.contains( "exe_common", Qt::CaseInsensitive ) || + fileName.contains( "unknown", Qt::CaseInsensitive ) || + lineNumber.contains( "unknown", Qt::CaseInsensitive ) ); } ///@endcond From 3abd0fa5309f833a833ffe5f8f87073552e6377c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:33:19 +1000 Subject: [PATCH 205/364] Fix missing (or incorrect use of) emit Thanks to Clazy --- src/core/qgsbrowsermodel.cpp | 4 +- .../attributetable/qgsfeaturelistmodel.cpp | 4 +- src/gui/qgsadvanceddigitizingdockwidget.cpp | 2 +- src/gui/qgskeyvaluewidget.cpp | 4 +- src/gui/qgslistwidget.cpp | 4 +- src/gui/qgsmaptoolcapture.cpp | 2 +- .../symbology/qgsrulebasedrendererwidget.cpp | 2 +- src/plugins/gps_importer/qgsgpsplugin.cpp | 60 +++++++++---------- 8 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/core/qgsbrowsermodel.cpp b/src/core/qgsbrowsermodel.cpp index 401e98ffa6e..94ca1279aae 100644 --- a/src/core/qgsbrowsermodel.cpp +++ b/src/core/qgsbrowsermodel.cpp @@ -569,9 +569,9 @@ void QgsBrowserModel::hidePath( QgsDataItem *item ) else { int i = mRootItems.indexOf( item ); - emit beginRemoveRows( QModelIndex(), i, i ); + beginRemoveRows( QModelIndex(), i, i ); mRootItems.remove( i ); item->deleteLater(); - emit endRemoveRows(); + endRemoveRows(); } } diff --git a/src/gui/attributetable/qgsfeaturelistmodel.cpp b/src/gui/attributetable/qgsfeaturelistmodel.cpp index bbdafc947a9..a6f82614e43 100644 --- a/src/gui/attributetable/qgsfeaturelistmodel.cpp +++ b/src/gui/attributetable/qgsfeaturelistmodel.cpp @@ -197,9 +197,9 @@ void QgsFeatureListModel::setInjectNull( bool injectNull ) { if ( mInjectNull != injectNull ) { - emit beginResetModel(); + beginResetModel(); mInjectNull = injectNull; - emit endResetModel(); + endResetModel(); } } diff --git a/src/gui/qgsadvanceddigitizingdockwidget.cpp b/src/gui/qgsadvanceddigitizingdockwidget.cpp index 23e1862bef8..97c9020ffda 100644 --- a/src/gui/qgsadvanceddigitizingdockwidget.cpp +++ b/src/gui/qgsadvanceddigitizingdockwidget.cpp @@ -964,7 +964,7 @@ bool QgsAdvancedDigitizingDockWidget::canvasMoveEvent( QgsMapMouseEvent *e ) } else { - popWarning(); + emit popWarning(); } // perpendicular/parallel constraint diff --git a/src/gui/qgskeyvaluewidget.cpp b/src/gui/qgskeyvaluewidget.cpp index 9c587d9145c..5b1ad4cec70 100644 --- a/src/gui/qgskeyvaluewidget.cpp +++ b/src/gui/qgskeyvaluewidget.cpp @@ -31,13 +31,13 @@ void QgsKeyValueWidget::setMap( const QVariantMap &map ) ///@cond PRIVATE void QgsKeyValueModel::setMap( const QVariantMap &map ) { - emit beginResetModel(); + beginResetModel(); mLines.clear(); for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) { mLines.append( Line( it.key(), it.value() ) ); } - emit endResetModel(); + endResetModel(); } QVariantMap QgsKeyValueModel::map() const diff --git a/src/gui/qgslistwidget.cpp b/src/gui/qgslistwidget.cpp index ac459dda672..6b0e58ccb33 100644 --- a/src/gui/qgslistwidget.cpp +++ b/src/gui/qgslistwidget.cpp @@ -39,9 +39,9 @@ QgsListModel::QgsListModel( QVariant::Type subType, QObject *parent ) : void QgsListModel::setList( const QVariantList &list ) { - emit beginResetModel(); + beginResetModel(); mLines = list; - emit endResetModel(); + endResetModel(); } QVariantList QgsListModel::list() const diff --git a/src/gui/qgsmaptoolcapture.cpp b/src/gui/qgsmaptoolcapture.cpp index fc77ed06470..2d22233a93d 100644 --- a/src/gui/qgsmaptoolcapture.cpp +++ b/src/gui/qgsmaptoolcapture.cpp @@ -676,7 +676,7 @@ void QgsMapToolCapture::validateGeometry() connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError ); connect( mValidator, &QThread::finished, this, &QgsMapToolCapture::validationFinished ); mValidator->start(); - messageEmitted( tr( "Validation started" ) ); + emit messageEmitted( tr( "Validation started" ) ); } void QgsMapToolCapture::addError( QgsGeometry::Error e ) diff --git a/src/gui/symbology/qgsrulebasedrendererwidget.cpp b/src/gui/symbology/qgsrulebasedrendererwidget.cpp index ae2ba7908d7..18b8d8f8726 100644 --- a/src/gui/symbology/qgsrulebasedrendererwidget.cpp +++ b/src/gui/symbology/qgsrulebasedrendererwidget.cpp @@ -1227,7 +1227,7 @@ void QgsRuleBasedRendererModel::willAddRules( const QModelIndex &parent, int cou void QgsRuleBasedRendererModel::finishedAddingRules() { - emit endInsertRows(); + endInsertRows(); } void QgsRuleBasedRendererModel::setFeatureCounts( const QHash &countMap ) diff --git a/src/plugins/gps_importer/qgsgpsplugin.cpp b/src/plugins/gps_importer/qgsgpsplugin.cpp index 8f17464ce8b..80ddcc0c5fe 100644 --- a/src/plugins/gps_importer/qgsgpsplugin.cpp +++ b/src/plugins/gps_importer/qgsgpsplugin.cpp @@ -186,12 +186,12 @@ void QgsGPSPlugin::createGPX() ofs << "" << std::endl; - emit drawVectorLayer( fileName + "?type=track", - fileInfo.baseName() + ", tracks", QStringLiteral( "gpx" ) ); - emit drawVectorLayer( fileName + "?type=route", - fileInfo.baseName() + ", routes", QStringLiteral( "gpx" ) ); - emit drawVectorLayer( fileName + "?type=waypoint", - fileInfo.baseName() + ", waypoints", QStringLiteral( "gpx" ) ); + drawVectorLayer( fileName + "?type=track", + fileInfo.baseName() + ", tracks", QStringLiteral( "gpx" ) ); + drawVectorLayer( fileName + "?type=route", + fileInfo.baseName() + ", routes", QStringLiteral( "gpx" ) ); + drawVectorLayer( fileName + "?type=waypoint", + fileInfo.baseName() + ", waypoints", QStringLiteral( "gpx" ) ); } } @@ -230,14 +230,14 @@ void QgsGPSPlugin::loadGPXFile( const QString &fileName, bool loadWaypoints, boo // add the requested layers if ( loadTracks ) - emit drawVectorLayer( fileName + "?type=track", - fileInfo.baseName() + ", tracks", QStringLiteral( "gpx" ) ); + drawVectorLayer( fileName + "?type=track", + fileInfo.baseName() + ", tracks", QStringLiteral( "gpx" ) ); if ( loadRoutes ) - emit drawVectorLayer( fileName + "?type=route", - fileInfo.baseName() + ", routes", QStringLiteral( "gpx" ) ); + drawVectorLayer( fileName + "?type=route", + fileInfo.baseName() + ", routes", QStringLiteral( "gpx" ) ); if ( loadWaypoints ) - emit drawVectorLayer( fileName + "?type=waypoint", - fileInfo.baseName() + ", waypoints", QStringLiteral( "gpx" ) ); + drawVectorLayer( fileName + "?type=waypoint", + fileInfo.baseName() + ", waypoints", QStringLiteral( "gpx" ) ); emit closeGui(); } @@ -297,14 +297,14 @@ void QgsGPSPlugin::importGPSFile( const QString &inputFileName, QgsBabelFormat * // add the layer if ( importTracks ) - emit drawVectorLayer( outputFileName + "?type=track", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=track", + layerName, QStringLiteral( "gpx" ) ); if ( importRoutes ) - emit drawVectorLayer( outputFileName + "?type=route", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=route", + layerName, QStringLiteral( "gpx" ) ); if ( importWaypoints ) - emit drawVectorLayer( outputFileName + "?type=waypoint", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=waypoint", + layerName, QStringLiteral( "gpx" ) ); emit closeGui(); } @@ -377,16 +377,16 @@ void QgsGPSPlugin::convertGPSFile( const QString &inputFileName, { case 0: case 3: - emit drawVectorLayer( outputFileName + "?type=waypoint", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=waypoint", + layerName, QStringLiteral( "gpx" ) ); break; case 1: - emit drawVectorLayer( outputFileName + "?type=route", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=route", + layerName, QStringLiteral( "gpx" ) ); break; case 2: - emit drawVectorLayer( outputFileName + "?type=track", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=track", + layerName, QStringLiteral( "gpx" ) ); break; default: QgsDebugMsg( "Illegal conversion index!" ); @@ -464,14 +464,14 @@ void QgsGPSPlugin::downloadFromGPS( const QString &device, const QString &port, // add the layer if ( downloadWaypoints ) - emit drawVectorLayer( outputFileName + "?type=waypoint", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=waypoint", + layerName, QStringLiteral( "gpx" ) ); if ( downloadRoutes ) - emit drawVectorLayer( outputFileName + "?type=route", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=route", + layerName, QStringLiteral( "gpx" ) ); if ( downloadTracks ) - emit drawVectorLayer( outputFileName + "?type=track", - layerName, QStringLiteral( "gpx" ) ); + drawVectorLayer( outputFileName + "?type=track", + layerName, QStringLiteral( "gpx" ) ); // everything was OK, remember the device and port for next time QgsSettings settings; From 52474785aadca37afaf774f131b192df80e33a5d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 16:33:47 +1000 Subject: [PATCH 206/364] Use faster QColor constructor with r/g/b parameters The QString based constructor is slow, as the string must be parsed to interpret the color. Thanks to Clazy --- src/core/qgscolorscheme.cpp | 20 ++++++++++---------- src/core/symbology/qgs25drenderer.cpp | 6 +++--- src/core/symbology/qgssymbollayerutils.cpp | 6 +++--- src/gui/qgscodeeditor.cpp | 10 +++++----- src/gui/qgscodeeditorpython.cpp | 2 +- src/gui/qgsexpressionbuilderwidget.cpp | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/core/qgscolorscheme.cpp b/src/core/qgscolorscheme.cpp index bd793b55aba..94414700d87 100644 --- a/src/core/qgscolorscheme.cpp +++ b/src/core/qgscolorscheme.cpp @@ -133,16 +133,16 @@ QgsNamedColorList QgsCustomColorScheme::fetchColors( const QString &context, con if ( !settings.contains( QStringLiteral( "/colors/palettecolors" ) ) ) { //no custom palette, return default colors - colorList.append( qMakePair( QColor( "#000000" ), QString() ) ); - colorList.append( qMakePair( QColor( "#ffffff" ), QString() ) ); - colorList.append( qMakePair( QColor( "#a6cee3" ), QString() ) ); - colorList.append( qMakePair( QColor( "#1f78b4" ), QString() ) ); - colorList.append( qMakePair( QColor( "#b2df8a" ), QString() ) ); - colorList.append( qMakePair( QColor( "#33a02c" ), QString() ) ); - colorList.append( qMakePair( QColor( "#fb9a99" ), QString() ) ); - colorList.append( qMakePair( QColor( "#e31a1c" ), QString() ) ); - colorList.append( qMakePair( QColor( "#fdbf6f" ), QString() ) ); - colorList.append( qMakePair( QColor( "#ff7f00" ), QString() ) ); + colorList.append( qMakePair( QColor( 0, 0, 0 ), QString() ) ); + colorList.append( qMakePair( QColor( 255, 255, 255 ), QString() ) ); + colorList.append( qMakePair( QColor( 166, 206, 227 ), QString() ) ); + colorList.append( qMakePair( QColor( 31, 120, 180 ), QString() ) ); + colorList.append( qMakePair( QColor( 178, 223, 138 ), QString() ) ); + colorList.append( qMakePair( QColor( 51, 160, 44 ), QString() ) ); + colorList.append( qMakePair( QColor( 251, 154, 153 ), QString() ) ); + colorList.append( qMakePair( QColor( 227, 26, 28 ), QString() ) ); + colorList.append( qMakePair( QColor( 253, 191, 111 ), QString() ) ); + colorList.append( qMakePair( QColor( 255, 127, 0 ), QString() ) ); return colorList; } diff --git a/src/core/symbology/qgs25drenderer.cpp b/src/core/symbology/qgs25drenderer.cpp index cca1b31628d..dd37f0fb8fe 100644 --- a/src/core/symbology/qgs25drenderer.cpp +++ b/src/core/symbology/qgs25drenderer.cpp @@ -93,13 +93,13 @@ Qgs25DRenderer::Qgs25DRenderer() // These methods must only be used after the above initialization! - setRoofColor( QColor( "#b1a97c" ) ); - setWallColor( QColor( "#777777" ) ); + setRoofColor( QColor( 177, 169, 124 ) ); + setWallColor( QColor( 119, 119, 119 ) ); wallLayer()->setDataDefinedProperty( QgsSymbolLayer::PropertyFillColor, QgsProperty::fromExpression( QString( WALL_SHADING_EXPRESSION ) ) ); setShadowSpread( 4 ); - setShadowColor( QColor( "#111111" ) ); + setShadowColor( QColor( 17, 17, 17 ) ); QgsFeatureRequest::OrderBy orderBy; orderBy << QgsFeatureRequest::OrderByClause( diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 8b420fba078..84de12b9b5f 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -1734,7 +1734,7 @@ bool QgsSymbolLayerUtils::fillFromSld( QDomElement &element, Qt::BrushStyle &bru QgsDebugMsg( "Entered." ); brushStyle = Qt::SolidPattern; - color = QColor( "#808080" ); + color = QColor( 128, 128, 128 ); if ( element.isNull() ) { @@ -1875,7 +1875,7 @@ bool QgsSymbolLayerUtils::lineFromSld( QDomElement &element, QgsDebugMsg( "Entered." ); penStyle = Qt::SolidLine; - color = QColor( "#000000" ); + color = QColor( 0, 0, 0 ); width = 1; if ( penJoinStyle ) *penJoinStyle = Qt::BevelJoin; @@ -2224,7 +2224,7 @@ bool QgsSymbolLayerUtils::wellKnownMarkerFromSld( QDomElement &element, name = QStringLiteral( "square" ); color = QColor(); - strokeColor = QColor( "#000000" ); + strokeColor = QColor( 0, 0, 0 ); strokeWidth = 1; size = 6; diff --git a/src/gui/qgscodeeditor.cpp b/src/gui/qgscodeeditor.cpp index 0ed8cbd9183..4ef416bd9a2 100644 --- a/src/gui/qgscodeeditor.cpp +++ b/src/gui/qgscodeeditor.cpp @@ -88,10 +88,10 @@ void QgsCodeEditor::setSciWidget() { setUtf8( true ); setCaretLineVisible( true ); - setCaretLineBackgroundColor( QColor( "#fcf3ed" ) ); + setCaretLineBackgroundColor( QColor( 252, 243, 237 ) ); setBraceMatching( QsciScintilla::SloppyBraceMatch ); - setMatchedBraceBackgroundColor( QColor( "#b7f907" ) ); + setMatchedBraceBackgroundColor( QColor( 183, 249, 7 ) ); // whether margin will be shown setMarginVisible( mMargin ); // whether margin will be shown @@ -121,8 +121,8 @@ void QgsCodeEditor::setMarginVisible( bool margin ) setMarginLineNumbers( 1, true ); setMarginsFont( marginFont ); setMarginWidth( 1, QStringLiteral( "00000" ) ); - setMarginsForegroundColor( QColor( "#3E3EE3" ) ); - setMarginsBackgroundColor( QColor( "#f9f9f9" ) ); + setMarginsForegroundColor( QColor( 62, 62, 227 ) ); + setMarginsBackgroundColor( QColor( 249, 249, 249 ) ); } else { @@ -138,7 +138,7 @@ void QgsCodeEditor::setFoldingVisible( bool folding ) if ( folding ) { setFolding( QsciScintilla::PlainFoldStyle ); - setFoldMarginColors( QColor( "#f4f4f4" ), QColor( "#f4f4f4" ) ); + setFoldMarginColors( QColor( 244, 244, 244 ), QColor( 244, 244, 244 ) ); } else { diff --git a/src/gui/qgscodeeditorpython.cpp b/src/gui/qgscodeeditorpython.cpp index 3ca76052cb0..be2472c5c6b 100644 --- a/src/gui/qgscodeeditorpython.cpp +++ b/src/gui/qgscodeeditorpython.cpp @@ -43,7 +43,7 @@ void QgsCodeEditorPython::setSciLexerPython() setEdgeMode( QsciScintilla::EdgeLine ); setEdgeColumn( 80 ); - setEdgeColor( QColor( "#FF0000" ) ); + setEdgeColor( QColor( 255, 0, 0 ) ); setWhitespaceVisibility( QsciScintilla::WsVisibleAfterIndent ); diff --git a/src/gui/qgsexpressionbuilderwidget.cpp b/src/gui/qgsexpressionbuilderwidget.cpp index f5b73fec3de..3f18c185a07 100644 --- a/src/gui/qgsexpressionbuilderwidget.cpp +++ b/src/gui/qgsexpressionbuilderwidget.cpp @@ -381,7 +381,7 @@ void QgsExpressionBuilderWidget::registerItem( const QString &group, //Recent group should always be last group newgroupNode->setData( group.startsWith( QLatin1String( "Recent (" ) ) ? 2 : 1, QgsExpressionItem::CUSTOM_SORT_ROLE ); newgroupNode->appendRow( item ); - newgroupNode->setBackground( QBrush( QColor( "#eee" ) ) ); + newgroupNode->setBackground( QBrush( QColor( 238, 238, 238 ) ) ); mModel->appendRow( newgroupNode ); mExpressionGroups.insert( group, newgroupNode ); } From 2b14c46255e4dadaef584f915caf86c2eb724451 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 17:51:10 +1000 Subject: [PATCH 207/364] Avoid detach of temporary --- src/analysis/interpolation/qgsgridfilewriter.cpp | 2 +- src/app/qgsmaptooladdpart.cpp | 3 ++- src/app/qgsmeasuredialog.cpp | 9 ++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/analysis/interpolation/qgsgridfilewriter.cpp b/src/analysis/interpolation/qgsgridfilewriter.cpp index 50e2bba5e0c..24999832ef9 100644 --- a/src/analysis/interpolation/qgsgridfilewriter.cpp +++ b/src/analysis/interpolation/qgsgridfilewriter.cpp @@ -98,7 +98,7 @@ int QgsGridFileWriter::writeFile( QgsFeedback *feedback ) // create prj file QgsInterpolator::LayerData ld; - ld = mInterpolator->layerData().first(); + ld = mInterpolator->layerData().at( 0 ); QgsVectorLayer *vl = ld.vectorLayer; QString crs = vl->crs().toWkt(); QFileInfo fi( mOutputFilePath ); diff --git a/src/app/qgsmaptooladdpart.cpp b/src/app/qgsmaptooladdpart.cpp index 0d703043744..14e2fdd8845 100644 --- a/src/app/qgsmaptooladdpart.cpp +++ b/src/app/qgsmaptooladdpart.cpp @@ -68,7 +68,8 @@ void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) } bool isGeometryEmpty = false; - if ( vlayer->selectedFeatures()[0].geometry().isNull() ) + QgsFeatureList selectedFeatures = vlayer->selectedFeatures(); + if ( !selectedFeatures.isEmpty() && selectedFeatures.at( 0 ).geometry().isNull() ) isGeometryEmpty = true; if ( !checkSelection() ) diff --git a/src/app/qgsmeasuredialog.cpp b/src/app/qgsmeasuredialog.cpp index 6249a30626d..6d4878648f5 100644 --- a/src/app/qgsmeasuredialog.cpp +++ b/src/app/qgsmeasuredialog.cpp @@ -156,7 +156,8 @@ void QgsMeasureDialog::mouseMove( const QgsPointXY &point ) } else if ( !mMeasureArea && mTool->points().size() >= 1 ) { - QgsPointXY p1( mTool->points().last() ), p2( point ); + QList< QgsPointXY > tmpPoints = mTool->points(); + QgsPointXY p1( tmpPoints.at( tmpPoints.size() - 1 ) ), p2( point ); double d = mDa.measureLine( p1, p2 ); editTotal->setText( formatDistance( mTotal + d ) ); @@ -224,7 +225,8 @@ void QgsMeasureDialog::removeLastPoint() if ( !mTool->done() ) { // need to add the distance for the temporary mouse cursor point - QgsPointXY p1( mTool->points().last() ); + QList< QgsPointXY > tmpPoints = mTool->points(); + QgsPointXY p1( tmpPoints.at( tmpPoints.size() - 1 ) ); double d = mDa.measureLine( p1, mLastMousePoint ); d = convertLength( d, mDistanceUnits ); @@ -481,7 +483,8 @@ void QgsMeasureDialog::updateUi() QgsPointXY p1, p2; mTotal = 0; - for ( it = mTool->points().constBegin(); it != mTool->points().constEnd(); ++it ) + QList< QgsPointXY > tmpPoints = mTool->points(); + for ( it = tmpPoints.constBegin(); it != tmpPoints.constEnd(); ++it ) { p2 = *it; if ( !b ) From 8312dfe5d818c66666ff0a39f16c696cc3b2cc41 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 17:55:27 +1000 Subject: [PATCH 208/364] Use faster static QFileInfo::exists instead of constructing QFileInfo Thanks to Clazy --- src/app/dwg/qgsdwgimportdialog.cpp | 2 +- src/app/dwg/qgsdwgimporter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/dwg/qgsdwgimportdialog.cpp b/src/app/dwg/qgsdwgimportdialog.cpp index 42e11961204..bf2a74688ae 100644 --- a/src/app/dwg/qgsdwgimportdialog.cpp +++ b/src/app/dwg/qgsdwgimportdialog.cpp @@ -151,7 +151,7 @@ void QgsDwgImportDialog::on_leLayerGroup_textChanged( const QString &text ) void QgsDwgImportDialog::on_pbLoadDatabase_clicked() { - if ( !QFileInfo( leDatabase->text() ).exists() ) + if ( !QFileInfo::exists( leDatabase->text() ) ) return; CursorOverride waitCursor; diff --git a/src/app/dwg/qgsdwgimporter.cpp b/src/app/dwg/qgsdwgimporter.cpp index c380393a369..9cca66c0124 100644 --- a/src/app/dwg/qgsdwgimporter.cpp +++ b/src/app/dwg/qgsdwgimporter.cpp @@ -195,7 +195,7 @@ bool QgsDwgImporter::import( const QString &drawing, QString &error, bool doExpa return false; } - if ( QFileInfo( mDatabase ).exists() ) + if ( QFileInfo::exists( mDatabase ) ) { mDs = OGROpen( mDatabase.toUtf8().constData(), true, nullptr ); if ( !mDs ) From 2ebb5c0031889c504b9d4a52f104ab3fbe06ee08 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 17:57:25 +1000 Subject: [PATCH 209/364] Use faster QVariant::toString method Thanks to Clazy --- src/app/qgsbookmarks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/qgsbookmarks.cpp b/src/app/qgsbookmarks.cpp index b113e54e7c9..9802f385b4f 100644 --- a/src/app/qgsbookmarks.cpp +++ b/src/app/qgsbookmarks.cpp @@ -445,10 +445,10 @@ bool QgsProjectBookmarksTableModel::setData( const QModelIndex &index, const QVa switch ( index.column() ) { case 1: - QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Name" ).arg( index.row() ), value.value() ); + QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Name" ).arg( index.row() ), value.toString() ); return true; case 2: - QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Project" ).arg( index.row() ), value.value() ); + QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Project" ).arg( index.row() ), value.toString() ); return true; case 3: QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinX" ).arg( index.row() ), value.toDouble() ); From cca964a92401f7c874abf730e1ae0019f2f88171 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 17:58:09 +1000 Subject: [PATCH 210/364] Include qglobal.h before relying on Qt macro definitions Thanks to Clazy --- src/providers/grass/qgsgrass.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/providers/grass/qgsgrass.cpp b/src/providers/grass/qgsgrass.cpp index e66f8726734..00f0a601387 100644 --- a/src/providers/grass/qgsgrass.cpp +++ b/src/providers/grass/qgsgrass.cpp @@ -14,6 +14,8 @@ * * ***************************************************************************/ +#include + #ifdef _MSC_VER // to avoid conflicting SF_UNKNOWN #define _OLE2_H_ From 7e9428f6ac1d4f257b2bc22882f03b9400015dd8 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 17:58:35 +1000 Subject: [PATCH 211/364] Use .at(0) instead of .left(1), as it avoids string allocation Thanks to Clazy --- src/providers/wfs/qgswfsprovider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/wfs/qgswfsprovider.cpp b/src/providers/wfs/qgswfsprovider.cpp index bb589f42abd..a83f79e8d27 100644 --- a/src/providers/wfs/qgswfsprovider.cpp +++ b/src/providers/wfs/qgswfsprovider.cpp @@ -1363,7 +1363,7 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc, geometryAttribute = ref.mid( 4 ); // Strip gml: prefix QString propertyType( gmlRefProperty.cap( 1 ) ); // Set the first character in upper case - propertyType = propertyType.left( 1 ).toUpper() + propertyType.mid( 1 ); + propertyType = propertyType.at( 0 ).toUpper() + propertyType.mid( 1 ); geomType = geomTypeFromPropertyType( geometryAttribute, propertyType ); } else if ( !name.isEmpty() ) //todo: distinguish between numerical and non-numerical types From 0815a6e714d35c1838756933474f776f6044b03e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 18:14:33 +1000 Subject: [PATCH 212/364] Some more Clazy cleanups --- src/providers/ogr/qgsgeopackagedataitems.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index d053bbd82e1..38fc088745d 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -357,7 +357,7 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct } if ( !srcLayer ) { - importResults.append( tr( "%1: %2" ).arg( dropUri.name ).arg( error ) ); + importResults.append( tr( "%1: %2" ).arg( dropUri.name, error ) ); hasError = true; continue; } @@ -370,7 +370,8 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct // check if the destination layer already exists bool exists = false; // Q_FOREACH won't detach ... - for ( const auto child : children() ) + const QVector< QgsDataItem *> c = children(); + for ( const auto child : c ) { if ( child->name() == dropUri.name ) { From d2fe65826e6036f8650d93340e554ea17ec2141f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 20:00:19 +1000 Subject: [PATCH 213/364] Update sip --- python/core/core_auto.sip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index b038143d860..37cfc8ee399 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -198,7 +198,6 @@ %Include raster/qgsrasterfilewriter.sip %Include raster/qgsrasterhistogram.sip %Include raster/qgsrasteridentifyresult.sip -%Include raster/qgsrasterinterface.sip %Include raster/qgsrasteriterator.sip %Include raster/qgsrasterminmaxorigin.sip %Include raster/qgsrasternuller.sip @@ -377,6 +376,7 @@ %Include raster/qgsrasterfilewritertask.sip %Include raster/qgsrasterlayer.sip %Include raster/qgsrasterdataprovider.sip +%Include raster/qgsrasterinterface.sip %Include geometry/qgspoint.sip %Include gps/qgsgpsconnection.sip %Include gps/qgsgpsdetector.sip From b370a39497b9cf2d957630ccf086585fc3c5cd72 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 20:03:42 +1000 Subject: [PATCH 214/364] Restore qRegisterMetaType for Q_ENUMed enums It's odd - because one reason to swap to Q_ENUM is to gain automatic registration of these types, so it should work without the manual registration. Might be a SIP binding issue... --- src/core/qgsapplication.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 8946ec2a9a1..04c42400bf5 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -146,7 +146,9 @@ void QgsApplication::init( QString profileFolder ) qRegisterMetaType( "QgsGeometry::Error" ); qRegisterMetaType( "QgsProcessingFeatureSourceDefinition" ); qRegisterMetaType( "QgsProcessingOutputLayerDefinition" ); + qRegisterMetaType( "QgsUnitTypes::LayoutUnit" ); qRegisterMetaType( "QgsFeatureIds" ); + qRegisterMetaType( "QgsMessageLog::MessageLevel" ); QString prefixPath( getenv( "QGIS_PREFIX_PATH" ) ? getenv( "QGIS_PREFIX_PATH" ) : applicationDirPath() ); // QgsDebugMsg( QString( "prefixPath(): %1" ).arg( prefixPath ) ); From e4c1e1b9051717e58f92301436e6ef85628b6293 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 29 Aug 2017 20:09:54 +1000 Subject: [PATCH 215/364] Ignore Q_ENUM in doxygen test --- tests/code_layout/doxygen_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/code_layout/doxygen_parser.py b/tests/code_layout/doxygen_parser.py index f8d86b5f440..40b54d4f0d2 100644 --- a/tests/code_layout/doxygen_parser.py +++ b/tests/code_layout/doxygen_parser.py @@ -355,7 +355,7 @@ class DoxygenParser(): # ignore certain obvious operators try: - if name.text in ('operator=', 'operator==', 'operator!='): + if name.text in ('operator=', 'operator==', 'operator!=', 'Q_ENUM'): return False except: pass From 94987918eff36bd42111363fb9b7192522f6e9d2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 16:56:44 +1000 Subject: [PATCH 216/364] Rename .cc files to .cpp for consistency --- python/analysis/analysis_auto.sip | 1 + src/analysis/CMakeLists.txt | 32 +++++++++---------- .../{Bezier3D.cc => Bezier3D.cpp} | 0 ...olator.cc => CloughTocherInterpolator.cpp} | 0 ...ngulation.cc => DualEdgeTriangulation.cpp} | 0 .../{HalfEdge.cc => HalfEdge.cpp} | 0 ...polator.cc => LinTriangleInterpolator.cpp} | 0 .../interpolation/{Line3D.cc => Line3D.cpp} | 0 .../{MathUtils.cc => MathUtils.cpp} | 0 .../interpolation/{Node.cc => Node.cpp} | 0 ...rmVecDecorator.cc => NormVecDecorator.cpp} | 0 .../{ParametricLine.cc => ParametricLine.cpp} | 0 .../{TriDecorator.cc => TriDecorator.cpp} | 0 ...terpolator.cc => TriangleInterpolator.cpp} | 0 .../{Triangulation.cc => Triangulation.cpp} | 0 .../{Vector3D.cc => Vector3D.cpp} | 0 16 files changed, 17 insertions(+), 16 deletions(-) rename src/analysis/interpolation/{Bezier3D.cc => Bezier3D.cpp} (100%) rename src/analysis/interpolation/{CloughTocherInterpolator.cc => CloughTocherInterpolator.cpp} (100%) rename src/analysis/interpolation/{DualEdgeTriangulation.cc => DualEdgeTriangulation.cpp} (100%) rename src/analysis/interpolation/{HalfEdge.cc => HalfEdge.cpp} (100%) rename src/analysis/interpolation/{LinTriangleInterpolator.cc => LinTriangleInterpolator.cpp} (100%) rename src/analysis/interpolation/{Line3D.cc => Line3D.cpp} (100%) rename src/analysis/interpolation/{MathUtils.cc => MathUtils.cpp} (100%) rename src/analysis/interpolation/{Node.cc => Node.cpp} (100%) rename src/analysis/interpolation/{NormVecDecorator.cc => NormVecDecorator.cpp} (100%) rename src/analysis/interpolation/{ParametricLine.cc => ParametricLine.cpp} (100%) rename src/analysis/interpolation/{TriDecorator.cc => TriDecorator.cpp} (100%) rename src/analysis/interpolation/{TriangleInterpolator.cc => TriangleInterpolator.cpp} (100%) rename src/analysis/interpolation/{Triangulation.cc => Triangulation.cpp} (100%) rename src/analysis/interpolation/{Vector3D.cc => Vector3D.cpp} (100%) diff --git a/python/analysis/analysis_auto.sip b/python/analysis/analysis_auto.sip index 4966059b096..4c16f72c366 100644 --- a/python/analysis/analysis_auto.sip +++ b/python/analysis/analysis_auto.sip @@ -45,3 +45,4 @@ %Include vector/qgsgeometrysnapper.sip %Include network/qgsgraphdirector.sip %Include network/qgsvectorlayerdirector.sip +%Include interpolation/MathUtils.sip diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 072f6f3dd3c..d23b5c9a43f 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -6,20 +6,20 @@ SET(QGIS_ANALYSIS_SRCS interpolation/qgsidwinterpolator.cpp interpolation/qgsinterpolator.cpp interpolation/qgstininterpolator.cpp - interpolation/Bezier3D.cc - interpolation/CloughTocherInterpolator.cc - interpolation/DualEdgeTriangulation.cc - interpolation/HalfEdge.cc - interpolation/Line3D.cc - interpolation/LinTriangleInterpolator.cc - interpolation/MathUtils.cc - interpolation/NormVecDecorator.cc - interpolation/Node.cc - interpolation/ParametricLine.cc - interpolation/TriangleInterpolator.cc - interpolation/Triangulation.cc - interpolation/TriDecorator.cc - interpolation/Vector3D.cc + interpolation/Bezier3D.cpp + interpolation/CloughTocherInterpolator.cpp + interpolation/DualEdgeTriangulation.cpp + interpolation/HalfEdge.cpp + interpolation/Line3D.cpp + interpolation/LinTriangleInterpolator.cpp + interpolation/MathUtils.cpp + interpolation/NormVecDecorator.cpp + interpolation/Node.cpp + interpolation/ParametricLine.cpp + interpolation/TriangleInterpolator.cpp + interpolation/Triangulation.cpp + interpolation/TriDecorator.cpp + interpolation/Vector3D.cpp raster/qgsalignraster.cpp raster/qgsninecellfilter.cpp @@ -88,8 +88,8 @@ ENDIF(NOT MSVC) IF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") SET_SOURCE_FILES_PROPERTIES( interpolation/qgstininterpolator.cpp - interpolation/NormVecDecorator.cc - interpolation/CloughTocherInterpolator.cc + interpolation/NormVecDecorator.cpp + interpolation/CloughTocherInterpolator.cpp PROPERTIES COMPILE_FLAGS "-Wno-overloaded-virtual" ) ENDIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") diff --git a/src/analysis/interpolation/Bezier3D.cc b/src/analysis/interpolation/Bezier3D.cpp similarity index 100% rename from src/analysis/interpolation/Bezier3D.cc rename to src/analysis/interpolation/Bezier3D.cpp diff --git a/src/analysis/interpolation/CloughTocherInterpolator.cc b/src/analysis/interpolation/CloughTocherInterpolator.cpp similarity index 100% rename from src/analysis/interpolation/CloughTocherInterpolator.cc rename to src/analysis/interpolation/CloughTocherInterpolator.cpp diff --git a/src/analysis/interpolation/DualEdgeTriangulation.cc b/src/analysis/interpolation/DualEdgeTriangulation.cpp similarity index 100% rename from src/analysis/interpolation/DualEdgeTriangulation.cc rename to src/analysis/interpolation/DualEdgeTriangulation.cpp diff --git a/src/analysis/interpolation/HalfEdge.cc b/src/analysis/interpolation/HalfEdge.cpp similarity index 100% rename from src/analysis/interpolation/HalfEdge.cc rename to src/analysis/interpolation/HalfEdge.cpp diff --git a/src/analysis/interpolation/LinTriangleInterpolator.cc b/src/analysis/interpolation/LinTriangleInterpolator.cpp similarity index 100% rename from src/analysis/interpolation/LinTriangleInterpolator.cc rename to src/analysis/interpolation/LinTriangleInterpolator.cpp diff --git a/src/analysis/interpolation/Line3D.cc b/src/analysis/interpolation/Line3D.cpp similarity index 100% rename from src/analysis/interpolation/Line3D.cc rename to src/analysis/interpolation/Line3D.cpp diff --git a/src/analysis/interpolation/MathUtils.cc b/src/analysis/interpolation/MathUtils.cpp similarity index 100% rename from src/analysis/interpolation/MathUtils.cc rename to src/analysis/interpolation/MathUtils.cpp diff --git a/src/analysis/interpolation/Node.cc b/src/analysis/interpolation/Node.cpp similarity index 100% rename from src/analysis/interpolation/Node.cc rename to src/analysis/interpolation/Node.cpp diff --git a/src/analysis/interpolation/NormVecDecorator.cc b/src/analysis/interpolation/NormVecDecorator.cpp similarity index 100% rename from src/analysis/interpolation/NormVecDecorator.cc rename to src/analysis/interpolation/NormVecDecorator.cpp diff --git a/src/analysis/interpolation/ParametricLine.cc b/src/analysis/interpolation/ParametricLine.cpp similarity index 100% rename from src/analysis/interpolation/ParametricLine.cc rename to src/analysis/interpolation/ParametricLine.cpp diff --git a/src/analysis/interpolation/TriDecorator.cc b/src/analysis/interpolation/TriDecorator.cpp similarity index 100% rename from src/analysis/interpolation/TriDecorator.cc rename to src/analysis/interpolation/TriDecorator.cpp diff --git a/src/analysis/interpolation/TriangleInterpolator.cc b/src/analysis/interpolation/TriangleInterpolator.cpp similarity index 100% rename from src/analysis/interpolation/TriangleInterpolator.cc rename to src/analysis/interpolation/TriangleInterpolator.cpp diff --git a/src/analysis/interpolation/Triangulation.cc b/src/analysis/interpolation/Triangulation.cpp similarity index 100% rename from src/analysis/interpolation/Triangulation.cc rename to src/analysis/interpolation/Triangulation.cpp diff --git a/src/analysis/interpolation/Vector3D.cc b/src/analysis/interpolation/Vector3D.cpp similarity index 100% rename from src/analysis/interpolation/Vector3D.cc rename to src/analysis/interpolation/Vector3D.cpp From 9e962054fea0d02b9d10e56994b6fd740c6b4085 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 16:58:43 +1000 Subject: [PATCH 217/364] Exclude MathUtils from sip bindings We don't want these part of stable API - this namespace really shouldn't exist, as it mostly contains code which is duplicated in other parts of the API (with better maintained and tested versions). --- python/analysis/analysis_auto.sip | 1 - src/analysis/interpolation/MathUtils.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/python/analysis/analysis_auto.sip b/python/analysis/analysis_auto.sip index 4c16f72c366..4966059b096 100644 --- a/python/analysis/analysis_auto.sip +++ b/python/analysis/analysis_auto.sip @@ -45,4 +45,3 @@ %Include vector/qgsgeometrysnapper.sip %Include network/qgsgraphdirector.sip %Include network/qgsvectorlayerdirector.sip -%Include interpolation/MathUtils.sip diff --git a/src/analysis/interpolation/MathUtils.h b/src/analysis/interpolation/MathUtils.h index 14baab7cb12..7753a026d56 100644 --- a/src/analysis/interpolation/MathUtils.h +++ b/src/analysis/interpolation/MathUtils.h @@ -23,6 +23,8 @@ class QgsPoint; class Vector3D; +#define SIP_NO_FILE + namespace MathUtils { //! Calculates the barycentric coordinates of a point (x,y) with respect to p1, p2, p3 and stores the three barycentric coordinates in 'result'. Thus the u-coordinate is stored in result::x, the v-coordinate in result::y and the w-coordinate in result::z. Attention: p1, p2 and p3 have to be ordered counterclockwise From 90d142243fb3f59f04f1e075d8903fedd4d8dabd Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 17:02:56 +1000 Subject: [PATCH 218/364] More .cc -> .cpp renames --- src/analysis/interpolation/Bezier3D.cpp | 4 ++-- src/analysis/interpolation/CloughTocherInterpolator.cpp | 4 ++-- src/analysis/interpolation/DualEdgeTriangulation.cpp | 4 ++-- src/analysis/interpolation/HalfEdge.cpp | 4 ++-- src/analysis/interpolation/LinTriangleInterpolator.cpp | 4 ++-- src/analysis/interpolation/Line3D.cpp | 4 ++-- src/analysis/interpolation/MathUtils.cpp | 4 ++-- src/analysis/interpolation/Node.cpp | 4 ++-- src/analysis/interpolation/NormVecDecorator.cpp | 4 ++-- src/analysis/interpolation/ParametricLine.cpp | 4 ++-- src/analysis/interpolation/TriDecorator.cpp | 4 ++-- src/analysis/interpolation/TriangleInterpolator.cpp | 4 ++-- src/analysis/interpolation/Triangulation.cpp | 4 ++-- src/analysis/interpolation/Vector3D.cpp | 4 ++-- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/analysis/interpolation/Bezier3D.cpp b/src/analysis/interpolation/Bezier3D.cpp index 52d1d6b27cd..1bab22a647b 100644 --- a/src/analysis/interpolation/Bezier3D.cpp +++ b/src/analysis/interpolation/Bezier3D.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - Bezier3D.cc - description - ------------------- + Bezier3D.cpp + ------------ copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/CloughTocherInterpolator.cpp b/src/analysis/interpolation/CloughTocherInterpolator.cpp index 45286acdfb0..e6c9d1ddc18 100644 --- a/src/analysis/interpolation/CloughTocherInterpolator.cpp +++ b/src/analysis/interpolation/CloughTocherInterpolator.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - CloughTocherInterpolator.cc - description - ------------------- + CloughTocherInterpolator.cpp + ---------------------------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/DualEdgeTriangulation.cpp b/src/analysis/interpolation/DualEdgeTriangulation.cpp index 0c1ac63021f..d1111862ea2 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.cpp +++ b/src/analysis/interpolation/DualEdgeTriangulation.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - DualEdgeTriangulation.cc - description - ------------------- + DualEdgeTriangulation.cpp + ------------------------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/HalfEdge.cpp b/src/analysis/interpolation/HalfEdge.cpp index 6651b5b468c..7d782719d6a 100644 --- a/src/analysis/interpolation/HalfEdge.cpp +++ b/src/analysis/interpolation/HalfEdge.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - HalfEdge.cc - --------------------- + HalfEdge.cpp + ------------ begin : September 2009 copyright : (C) 2009 by Marco Hugentobler email : marco dot hugentobler at sourcepole dot ch diff --git a/src/analysis/interpolation/LinTriangleInterpolator.cpp b/src/analysis/interpolation/LinTriangleInterpolator.cpp index a158a1bf8c0..aa22a6eb31f 100644 --- a/src/analysis/interpolation/LinTriangleInterpolator.cpp +++ b/src/analysis/interpolation/LinTriangleInterpolator.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - LinTriangleInterpolator.cc - description - ------------------- + LinTriangleInterpolator.cpp + --------------------------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/Line3D.cpp b/src/analysis/interpolation/Line3D.cpp index 9ef79b33b6b..5cb6a0944b4 100644 --- a/src/analysis/interpolation/Line3D.cpp +++ b/src/analysis/interpolation/Line3D.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - Line3D.cc - description - ------------------- + Line3D.cpp + ---------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/MathUtils.cpp b/src/analysis/interpolation/MathUtils.cpp index cf8f5a93c89..fe879072425 100644 --- a/src/analysis/interpolation/MathUtils.cpp +++ b/src/analysis/interpolation/MathUtils.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - MathUtils.cc - description - ------------------- + MathUtils.cpp + ------------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/Node.cpp b/src/analysis/interpolation/Node.cpp index 8ceb2106b2f..122e48f1f5a 100644 --- a/src/analysis/interpolation/Node.cpp +++ b/src/analysis/interpolation/Node.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - Node.cc - description - ------------------- + Node.cpp + -------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/NormVecDecorator.cpp b/src/analysis/interpolation/NormVecDecorator.cpp index e9318d444d2..697c9081a07 100644 --- a/src/analysis/interpolation/NormVecDecorator.cpp +++ b/src/analysis/interpolation/NormVecDecorator.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - NormVecDecorator.cc - description - ------------------- + NormVecDecorator.cpp + -------------------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/ParametricLine.cpp b/src/analysis/interpolation/ParametricLine.cpp index 8c8c5929ac2..31c74916f8c 100644 --- a/src/analysis/interpolation/ParametricLine.cpp +++ b/src/analysis/interpolation/ParametricLine.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - ParametricLine.cc - description - ------------------- + ParametricLine.cpp + ------------------ copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/TriDecorator.cpp b/src/analysis/interpolation/TriDecorator.cpp index 5d0d009524f..666bc9ff008 100644 --- a/src/analysis/interpolation/TriDecorator.cpp +++ b/src/analysis/interpolation/TriDecorator.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - TriDecorator.cc - description - ------------------- + TriDecorator.cpp + ---------------- copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ diff --git a/src/analysis/interpolation/TriangleInterpolator.cpp b/src/analysis/interpolation/TriangleInterpolator.cpp index a5fec8b882a..d6b26f6ac09 100644 --- a/src/analysis/interpolation/TriangleInterpolator.cpp +++ b/src/analysis/interpolation/TriangleInterpolator.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - TriangleInterpolator.cc - --------------------- + TriangleInterpolator.cpp + ------------------------ begin : September 2009 copyright : (C) 2009 by Marco Hugentobler email : marco dot hugentobler at sourcepole dot ch diff --git a/src/analysis/interpolation/Triangulation.cpp b/src/analysis/interpolation/Triangulation.cpp index 31065a2537e..2febb7060a2 100644 --- a/src/analysis/interpolation/Triangulation.cpp +++ b/src/analysis/interpolation/Triangulation.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - Triangulation.cc - --------------------- + Triangulation.cpp + ----------------- begin : September 2009 copyright : (C) 2009 by Marco Hugentobler email : marco dot hugentobler at sourcepole dot ch diff --git a/src/analysis/interpolation/Vector3D.cpp b/src/analysis/interpolation/Vector3D.cpp index c5c874aade3..701cb8e9b66 100644 --- a/src/analysis/interpolation/Vector3D.cpp +++ b/src/analysis/interpolation/Vector3D.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - Vector3D.cc - description - ------------------- + Vector3D.cpp + ------------ copyright : (C) 2004 by Marco Hugentobler email : mhugent@geo.unizh.ch ***************************************************************************/ From 04fec09c3ea378b90280dcfd5f4b5a16c9f4b6fa Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 17:12:46 +1000 Subject: [PATCH 219/364] Remove interpolation internal classes from stable API These classes still need a lot of cleanup (e.g. use of proper 3d geometry classes instead of their own 3d line classes, etc) and we don't want them locked into the 3.0 API. Better to remove them from the API and reintroduce them after they have been cleaned up (in >3.0). They are mostly implementation details anyway, and unlikely to be used outside of the high level interpolation classes. --- doc/api_break.dox | 14 +++++++++++++- python/analysis/analysis_auto.sip | 13 ------------- src/analysis/interpolation/Bezier3D.h | 6 +++++- .../interpolation/CloughTocherInterpolator.h | 6 +++++- src/analysis/interpolation/DualEdgeTriangulation.h | 6 +++++- src/analysis/interpolation/HalfEdge.h | 3 +++ .../interpolation/LinTriangleInterpolator.h | 6 +++++- src/analysis/interpolation/Line3D.h | 6 +++++- src/analysis/interpolation/Node.h | 6 +++++- src/analysis/interpolation/NormVecDecorator.h | 6 +++++- src/analysis/interpolation/ParametricLine.h | 6 +++++- src/analysis/interpolation/TriDecorator.h | 6 +++++- src/analysis/interpolation/TriangleInterpolator.h | 6 +++++- src/analysis/interpolation/Triangulation.h | 6 +++++- src/analysis/interpolation/Vector3D.h | 3 +++ 15 files changed, 74 insertions(+), 25 deletions(-) diff --git a/doc/api_break.dox b/doc/api_break.dox index fd63bf35ee5..51864859047 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -249,6 +249,8 @@ Removed Classes {#qgis_api_break_3_0_removed_classes} - QgsAnnotationItem was removed. This was replaced by QgsAnnotation and QgsMapCanvasAnnotationItem. - QgsAttributeAction was removed, and replaced by QgsActionManager. - QgsAttributeEditor was removed. Use QgsEditorWidgetRegistry::create() instead. +- Bezier3D. +- CloughTocherInterpolator. - QgsColorbutton was removed. QgsColorButtonV2 has now been renamed to QgsColorButton. Hence, QgsColorButtonV2 does not exist anymore. - QgsColorDialog was removed, and QgsColorDialogV2 was renamed to QgsColorDialog. Hence, QgsColorButtonV2 does not exist anymore. All the functionality from the old QgsColorDialog has been moved to the new class. @@ -266,10 +268,12 @@ should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinat - QgsDataDefinedButton was removed. Use QgsPropertyOverrideButton instead. - QgsDataDefinedSymbolDialog was removed. Code using this dialog should be reworked to use QgsPropertyOverrideButton - QgsDefaultPluginLayerLegend was removed. Use QgsMapLayer::setLegend() to provide legend nodes for plugin layers. +- DualEdgeTriangulation - QgsFileNameWidgetWrapper was removed. Use QgsExternalResourceWidgetWrapper instead. - QgsFileDropEdit was removed. Use QgsFileWidget instead. - QgsFormAnnotationItem. Use QgsFormAnnotation instead. - QgsGeometryAnalyzer. Use the equivalent Processing algorithms instead. +- HalfEdge. - QgsHtmlAnnotationItem. Use QgsHtmlAnnotation instead. - QgsHttpTransaction. This class was outdated and code should be ported to native Qt or Python implementations. - QgsGenericProjectionSelector. Use QgsProjectionSelectionTreeWidget instead. @@ -282,11 +286,16 @@ should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinat - addLegendLayerActionForLayer() moved to QgisInterface::addCustomActionForLayer() - removeLegendLayerAction() moved to QgisInterface::removeCustomActionForLayerType() - QgsLegendModel was removed. +- Line3D. +- LinTriangleInterpolator. - QgsMapCanvasLayer. Map canvas and overview canvas are updated separately with their own setLayers() calls. - QgsMapCanvasMap. It is an internal class used by map canvas. - QgsMapLayerRegistry. Its functionality has been moved to QgsProject. - QgsMapRenderer. It has been replaced by QgsMapRendererJob with subclasses and QgsMapSettings. - QgsMapToolTouch. The touch navigation functionality is now built into the standard QgsMapToolPan tool. +- Node. +- NormVecDecorator. +- ParametericLine. - 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. @@ -302,10 +311,13 @@ should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinat - QgsSvgAnnotationItem. Use QgsSvgAnnotation instead. - QgsSymbologyV2Conversion was removed. Reading of renderers from pre-1.0 versions is not supported anymore. - QgsTextAnnotationItem. Use QgsTextAnnotation instead. +- TriangleInterpolator. +- Triangulation. +- TriDecorator. - QgsSnapper. Use QgsSnappingUtils instead. - QgsSnappingResult. Use QgsSnappingUtils instead. - QgsMapCanvasSnapper. Use QgsMapCanvas::snappingUtils() instead. - +- Vector3D General changes {#qgis_api_break_3_0_global} --------------- diff --git a/python/analysis/analysis_auto.sip b/python/analysis/analysis_auto.sip index 4966059b096..bb2c8263e25 100644 --- a/python/analysis/analysis_auto.sip +++ b/python/analysis/analysis_auto.sip @@ -18,19 +18,6 @@ %Include interpolation/qgsgridfilewriter.sip %Include interpolation/qgsidwinterpolator.sip %Include interpolation/qgstininterpolator.sip -%Include interpolation/Bezier3D.sip -%Include interpolation/ParametricLine.sip -%Include interpolation/CloughTocherInterpolator.sip -%Include interpolation/TriangleInterpolator.sip -%Include interpolation/Vector3D.sip -%Include interpolation/DualEdgeTriangulation.sip -%Include interpolation/Node.sip -%Include interpolation/TriDecorator.sip -%Include interpolation/Triangulation.sip -%Include interpolation/HalfEdge.sip -%Include interpolation/LinTriangleInterpolator.sip -%Include interpolation/NormVecDecorator.sip -%Include interpolation/Line3D.sip %Include openstreetmap/qgsosmbase.sip %Include openstreetmap/qgsosmdatabase.sip %Include network/qgsgraph.sip diff --git a/src/analysis/interpolation/Bezier3D.h b/src/analysis/interpolation/Bezier3D.h index 9d9393567cb..bfdd4266d6c 100644 --- a/src/analysis/interpolation/Bezier3D.h +++ b/src/analysis/interpolation/Bezier3D.h @@ -21,8 +21,12 @@ #include "qgslogger.h" #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis - * Class Bezier3D represents a bezier curve, represented by control points. Parameter t is running from 0 to 1. The class is capable to calculate the curve point and the first two derivatives belonging to t.*/ + * Class Bezier3D represents a bezier curve, represented by control points. Parameter t is running from 0 to 1. The class is capable to calculate the curve point and the first two derivatives belonging to it. + * \note Not available in Python bindings +*/ class ANALYSIS_EXPORT Bezier3D: public ParametricLine { protected: diff --git a/src/analysis/interpolation/CloughTocherInterpolator.h b/src/analysis/interpolation/CloughTocherInterpolator.h index 1687733107f..c8f07bd9828 100644 --- a/src/analysis/interpolation/CloughTocherInterpolator.h +++ b/src/analysis/interpolation/CloughTocherInterpolator.h @@ -23,8 +23,12 @@ class NormVecDecorator; +#define SIP_NO_FILE + /** \ingroup analysis - * This is an implementation of a Clough-Tocher interpolator based on a triangular tessellation. The derivatives orthogonal to the boundary curves are interpolated linearly along a triangle edge.*/ + * This is an implementation of a Clough-Tocher interpolator based on a triangular tessellation. The derivatives orthogonal to the boundary curves are interpolated linearly along a triangle edge. + * \note Not available in Python bindings +*/ class ANALYSIS_EXPORT CloughTocherInterpolator : public TriangleInterpolator { protected: diff --git a/src/analysis/interpolation/DualEdgeTriangulation.h b/src/analysis/interpolation/DualEdgeTriangulation.h index 70f477c15c4..534a283713d 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.h +++ b/src/analysis/interpolation/DualEdgeTriangulation.h @@ -34,8 +34,12 @@ #include #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis - * DualEdgeTriangulation is an implementation of a triangulation class based on the dual edge data structure*/ + * DualEdgeTriangulation is an implementation of a triangulation class based on the dual edge data structure. + * \note Not available in Python bindings. +*/ class ANALYSIS_EXPORT DualEdgeTriangulation: public Triangulation { public: diff --git a/src/analysis/interpolation/HalfEdge.h b/src/analysis/interpolation/HalfEdge.h index d0646154646..9b1abd5e546 100644 --- a/src/analysis/interpolation/HalfEdge.h +++ b/src/analysis/interpolation/HalfEdge.h @@ -19,8 +19,11 @@ #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis * \class HalfEdge + * \note Not available in Python bindings. */ class ANALYSIS_EXPORT HalfEdge { diff --git a/src/analysis/interpolation/LinTriangleInterpolator.h b/src/analysis/interpolation/LinTriangleInterpolator.h index b2a969c7f78..507021d3e2c 100644 --- a/src/analysis/interpolation/LinTriangleInterpolator.h +++ b/src/analysis/interpolation/LinTriangleInterpolator.h @@ -21,8 +21,12 @@ #include "DualEdgeTriangulation.h" #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis - * LinTriangleInterpolator is a class which interpolates linearly on a triangulation*/ + * LinTriangleInterpolator is a class which interpolates linearly on a triangulation. + * \note Not available in Python bindings. +*/ class ANALYSIS_EXPORT LinTriangleInterpolator : public TriangleInterpolator { public: diff --git a/src/analysis/interpolation/Line3D.h b/src/analysis/interpolation/Line3D.h index 37c1fef84e5..ed6e2f12eff 100644 --- a/src/analysis/interpolation/Line3D.h +++ b/src/analysis/interpolation/Line3D.h @@ -21,8 +21,12 @@ #include "qgis_analysis.h" #include "qgis_sip.h" +#define SIP_NO_FILE + /** \ingroup analysis - * This class represents a line. It is implemented as a single directed linked list of nodes (with related QgsPoint objects). Attention: the points inserted in a line are not deleted from Line3D*/ + * This class represents a line. It is implemented as a single directed linked list of nodes (with related QgsPoint objects). Attention: the points inserted in a line are not deleted from Line3D + * \note Not available in Python bindings +*/ class ANALYSIS_EXPORT Line3D SIP_NODEFAULTCTORS { private: diff --git a/src/analysis/interpolation/Node.h b/src/analysis/interpolation/Node.h index 31c3d7e514c..38e39da3a68 100644 --- a/src/analysis/interpolation/Node.h +++ b/src/analysis/interpolation/Node.h @@ -20,8 +20,12 @@ #include "qgspoint.h" #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis - * Node is a class used by Line3D. It represents a node in the single directed linked list. Associated QgsPoint objects are deleted when the node is deleted.*/ + * Node is a class used by Line3D. It represents a node in the single directed linked list. Associated QgsPoint objects are deleted when the node is deleted. + * \note Not available in Python bindings +*/ class ANALYSIS_EXPORT Node { protected: diff --git a/src/analysis/interpolation/NormVecDecorator.h b/src/analysis/interpolation/NormVecDecorator.h index ee592217c0f..7d18473fa44 100644 --- a/src/analysis/interpolation/NormVecDecorator.h +++ b/src/analysis/interpolation/NormVecDecorator.h @@ -25,10 +25,14 @@ #include "qgslogger.h" #include "qgis_analysis.h" +#define SIP_NO_FILE + class QgsFeedback; /** \ingroup analysis - * Decorator class which adds the functionality of estimating normals at the data points*/ + * Decorator class which adds the functionality of estimating normals at the data points. + * \note Not available in Python bindings. +*/ class ANALYSIS_EXPORT NormVecDecorator: public TriDecorator { public: diff --git a/src/analysis/interpolation/ParametricLine.h b/src/analysis/interpolation/ParametricLine.h index 13081517b94..8a8d499baa5 100644 --- a/src/analysis/interpolation/ParametricLine.h +++ b/src/analysis/interpolation/ParametricLine.h @@ -24,9 +24,13 @@ class Vector3D; +#define SIP_NO_FILE + /** \ingroup analysis * ParametricLine is an Interface for parametric lines. It is possible, that a parametric line is composed of several parametric - * lines (see the composite pattern in Gamma et al. 'Design Patterns'). Do not build instances of it since it is an abstract class.*/ + * lines (see the composite pattern in Gamma et al. 'Design Patterns'). Do not build instances of it since it is an abstract class. + * \note Not available in Python bindings +*/ class ANALYSIS_EXPORT ParametricLine { protected: diff --git a/src/analysis/interpolation/TriDecorator.h b/src/analysis/interpolation/TriDecorator.h index 67ffadd00dc..dccb3eb40a4 100644 --- a/src/analysis/interpolation/TriDecorator.h +++ b/src/analysis/interpolation/TriDecorator.h @@ -21,8 +21,12 @@ #include "qgis.h" #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis - * Decorator class for Triangulations (s. Decorator pattern in Gamma et al.)*/ + * Decorator class for Triangulations (s. Decorator pattern in Gamma et al.). + * \note Not available in Python bindings. +*/ class ANALYSIS_EXPORT TriDecorator : public Triangulation { public: diff --git a/src/analysis/interpolation/TriangleInterpolator.h b/src/analysis/interpolation/TriangleInterpolator.h index 5c811e94f94..beab1344bd7 100644 --- a/src/analysis/interpolation/TriangleInterpolator.h +++ b/src/analysis/interpolation/TriangleInterpolator.h @@ -21,8 +21,12 @@ #include "Vector3D.h" #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis - * This is an interface for interpolator classes for triangulations*/ + * This is an interface for interpolator classes for triangulations. + * \note Not available in Python bindings. +*/ class ANALYSIS_EXPORT TriangleInterpolator { public: diff --git a/src/analysis/interpolation/Triangulation.h b/src/analysis/interpolation/Triangulation.h index 39604df98bb..11fbb8a42d1 100644 --- a/src/analysis/interpolation/Triangulation.h +++ b/src/analysis/interpolation/Triangulation.h @@ -25,8 +25,12 @@ class Line3D; +#define SIP_NO_FILE + /** \ingroup analysis - * Interface for Triangulation classes*/ + * Interface for Triangulation classes. + * \note Not available in Python bindings. +*/ class ANALYSIS_EXPORT Triangulation { public: diff --git a/src/analysis/interpolation/Vector3D.h b/src/analysis/interpolation/Vector3D.h index 6178c4e7a6b..78c59915e27 100644 --- a/src/analysis/interpolation/Vector3D.h +++ b/src/analysis/interpolation/Vector3D.h @@ -20,10 +20,13 @@ #include #include "qgis_analysis.h" +#define SIP_NO_FILE + /** \ingroup analysis * Class Vector3D represents a 3D-Vector, capable to store x-,y- and * z-coordinates in double values. In fact, the class is the same as QgsPoint. * The name 'vector' makes it easier to understand the programs. + * \note Not available in Python bindings */ class ANALYSIS_EXPORT Vector3D From 7cd588011938d949d3dec2d54d8e76cba4d41cfd Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 17:16:19 +1000 Subject: [PATCH 220/364] Remove some unused QProgressDialog imports from analysis lib --- src/analysis/interpolation/DualEdgeTriangulation.h | 1 - src/analysis/interpolation/qgstininterpolator.cpp | 1 - src/analysis/raster/qgskde.h | 1 - src/analysis/raster/qgsninecellfilter.cpp | 1 - src/analysis/vector/qgszonalstatistics.h | 1 - 5 files changed, 5 deletions(-) diff --git a/src/analysis/interpolation/DualEdgeTriangulation.h b/src/analysis/interpolation/DualEdgeTriangulation.h index 534a283713d..c95f4ce8429 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.h +++ b/src/analysis/interpolation/DualEdgeTriangulation.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "qgis_analysis.h" diff --git a/src/analysis/interpolation/qgstininterpolator.cpp b/src/analysis/interpolation/qgstininterpolator.cpp index 716c58605b0..17242774364 100644 --- a/src/analysis/interpolation/qgstininterpolator.cpp +++ b/src/analysis/interpolation/qgstininterpolator.cpp @@ -28,7 +28,6 @@ #include "qgsvectorlayer.h" #include "qgswkbptr.h" #include "qgsfeedback.h" -#include QgsTINInterpolator::QgsTINInterpolator( const QList &inputData, TINInterpolation interpolation, QgsFeedback *feedback ) : QgsInterpolator( inputData ) diff --git a/src/analysis/raster/qgskde.h b/src/analysis/raster/qgskde.h index cb59c2fab88..b147c8271ad 100644 --- a/src/analysis/raster/qgskde.h +++ b/src/analysis/raster/qgskde.h @@ -26,7 +26,6 @@ #include "qgis_analysis.h" class QgsFeatureSource; -class QProgressDialog; class QgsFeature; diff --git a/src/analysis/raster/qgsninecellfilter.cpp b/src/analysis/raster/qgsninecellfilter.cpp index 7f3344c8368..9a6fc29d5bb 100644 --- a/src/analysis/raster/qgsninecellfilter.cpp +++ b/src/analysis/raster/qgsninecellfilter.cpp @@ -19,7 +19,6 @@ #include "qgslogger.h" #include "cpl_string.h" #include "qgsfeedback.h" -#include #include QgsNineCellFilter::QgsNineCellFilter( const QString &inputFile, const QString &outputFile, const QString &outputFormat ) diff --git a/src/analysis/vector/qgszonalstatistics.h b/src/analysis/vector/qgszonalstatistics.h index 94277f1d2e1..90133a37c62 100644 --- a/src/analysis/vector/qgszonalstatistics.h +++ b/src/analysis/vector/qgszonalstatistics.h @@ -31,7 +31,6 @@ class QgsGeometry; class QgsVectorLayer; class QgsRasterLayer; class QgsRasterDataProvider; -class QProgressDialog; class QgsRectangle; class QgsField; From eb7ad7aebed252d8c6123d321035b9b90b46f08d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 17:22:26 +1000 Subject: [PATCH 221/364] Swap final uses of QProgressDialog in analysis lib to QgsFeedback --- doc/api_break.dox | 9 +++++++- .../analysis/raster/qgsrastercalculator.sip | 2 +- python/analysis/vector/qgstransectsample.sip | 5 ++++- src/analysis/raster/qgsrastercalculator.cpp | 21 +++++++----------- src/analysis/raster/qgsrastercalculator.h | 12 +++++----- src/analysis/vector/qgstransectsample.cpp | 22 +++++++++---------- src/analysis/vector/qgstransectsample.h | 9 ++++++-- src/app/qgisapp.cpp | 6 ++++- 8 files changed, 51 insertions(+), 35 deletions(-) diff --git a/doc/api_break.dox b/doc/api_break.dox index 51864859047..93bf38a17b9 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -1908,7 +1908,8 @@ QgsRasterCalculator {#qgis_api_break_3_0_QgsRasterCalculator} ------------------- - Cancelled (Result enum value) has been renamed to Canceled - +- processCalculation() now uses an optional QgsFeedback instead of QProgressDialog +for progress reports and cancelation. QgsRasterDataProvider {#qgis_api_break_3_0_QgsRasterDataProvider} --------------------- @@ -2350,6 +2351,12 @@ QgsTracer {#qgis_api_break_3_0_QgsTracer} - hasCrsTransformEnabled() and setCrsTransformEnabled() were removed. CRS transformation is now always enabled when required. +QgsTransectSample {#qgis_api_break_3_0_QgsTransectSample} +----------------- + +- createSample() now uses an optional QgsFeedback instead of QProgressDialog +for progress reports and cancelation. + QgsTreeWidgetItem {#qgis_api_break_3_0_QgsTreeWidgetItem} ----------------- diff --git a/python/analysis/raster/qgsrastercalculator.sip b/python/analysis/raster/qgsrastercalculator.sip index eedcb71709f..d4afbf8069c 100644 --- a/python/analysis/raster/qgsrastercalculator.sip +++ b/python/analysis/raster/qgsrastercalculator.sip @@ -71,7 +71,7 @@ class QgsRasterCalculator .. versionadded:: 2.10 %End - int processCalculation( QProgressDialog *p = 0 ); + int processCalculation( QgsFeedback *feedback = 0 ); %Docstring :rtype: int %End diff --git a/python/analysis/vector/qgstransectsample.sip b/python/analysis/vector/qgstransectsample.sip index 0e42fe7cce8..28923f8f2f9 100644 --- a/python/analysis/vector/qgstransectsample.sip +++ b/python/analysis/vector/qgstransectsample.sip @@ -30,8 +30,11 @@ class QgsTransectSample const QString &baselineStrataId, const QString &outputPointLayer, const QString &outputLineLayer, const QString &usedBaselineLayer, double minTransectLength = 0.0, double baselineBufferDistance = -1.0, double baselineSimplificationTolerance = -1.0 ); - int createSample( QProgressDialog *pd ); + int createSample( QgsFeedback *feedback = 0 ); %Docstring + Creates the sample. + + The optional ``feedback`` argument can be used for progress reporting and cancelation support. :rtype: int %End diff --git a/src/analysis/raster/qgsrastercalculator.cpp b/src/analysis/raster/qgsrastercalculator.cpp index fc1f6b73030..fbb53700c9e 100644 --- a/src/analysis/raster/qgsrastercalculator.cpp +++ b/src/analysis/raster/qgsrastercalculator.cpp @@ -22,8 +22,8 @@ #include "qgsrasterlayer.h" #include "qgsrastermatrix.h" #include "qgsrasterprojector.h" +#include "qgsfeedback.h" -#include #include #include @@ -56,7 +56,7 @@ QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QS { } -int QgsRasterCalculator::processCalculation( QProgressDialog *p ) +int QgsRasterCalculator::processCalculation( QgsFeedback *feedback ) { //prepare search string / tree QString errorString; @@ -117,23 +117,18 @@ int QgsRasterCalculator::processCalculation( QProgressDialog *p ) float outputNodataValue = -FLT_MAX; GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue ); - if ( p ) - { - p->setMaximum( mNumOutputRows ); - } - QgsRasterMatrix resultMatrix; resultMatrix.setNodataValue( outputNodataValue ); //read / write line by line for ( int i = 0; i < mNumOutputRows; ++i ) { - if ( p ) + if ( feedback ) { - p->setValue( i ); + feedback->setProgress( 100.0 * static_cast< double >( i ) / mNumOutputRows ); } - if ( p && p->wasCanceled() ) + if ( feedback && feedback->isCanceled() ) { break; } @@ -159,9 +154,9 @@ int QgsRasterCalculator::processCalculation( QProgressDialog *p ) } - if ( p ) + if ( feedback ) { - p->setValue( mNumOutputRows ); + feedback->setProgress( 100.0 ); } //close datasets and release memory @@ -169,7 +164,7 @@ int QgsRasterCalculator::processCalculation( QProgressDialog *p ) qDeleteAll( inputBlocks ); inputBlocks.clear(); - if ( p && p->wasCanceled() ) + if ( feedback && feedback->isCanceled() ) { //delete the dataset without closing (because it is faster) GDALDeleteDataset( outputDriver, mOutputFile.toUtf8().constData() ); diff --git a/src/analysis/raster/qgsrastercalculator.h b/src/analysis/raster/qgsrastercalculator.h index 5fec8ea15c4..2d3beb83ece 100644 --- a/src/analysis/raster/qgsrastercalculator.h +++ b/src/analysis/raster/qgsrastercalculator.h @@ -26,7 +26,7 @@ #include "qgis_analysis.h" class QgsRasterLayer; -class QProgressDialog; +class QgsFeedback; struct ANALYSIS_EXPORT QgsRasterCalculatorEntry @@ -85,11 +85,13 @@ class ANALYSIS_EXPORT QgsRasterCalculator QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector &rasterEntries ); - /** Starts the calculation and writes new raster - \param p progress bar (or 0 if called from non-gui code) - \returns 0 in case of success*/ + /** Starts the calculation and writes a new raster. + * + * The optional \a feedback argument can be used for progress reporting and cancelation support. + * \returns 0 in case of success + */ //TODO QGIS 3.0 - return QgsRasterCalculator::Result - int processCalculation( QProgressDialog *p = nullptr ); + int processCalculation( QgsFeedback *feedback = nullptr ); private: //default constructor forbidden. We need formula, output file, output format and output raster resolution obligatory diff --git a/src/analysis/vector/qgstransectsample.cpp b/src/analysis/vector/qgstransectsample.cpp index 26c26140e9e..04549200c53 100644 --- a/src/analysis/vector/qgstransectsample.cpp +++ b/src/analysis/vector/qgstransectsample.cpp @@ -20,7 +20,8 @@ #include "qgsvectorfilewriter.h" #include "qgsvectorlayer.h" #include "qgsproject.h" -#include +#include "qgsfeedback.h" + #include #ifndef _MSC_VER #include @@ -60,10 +61,8 @@ QgsTransectSample::QgsTransectSample() { } -int QgsTransectSample::createSample( QProgressDialog *pd ) +int QgsTransectSample::createSample( QgsFeedback *feedback ) { - Q_UNUSED( pd ); - if ( !mStrataLayer || !mStrataLayer->isValid() ) { return 1; @@ -147,19 +146,20 @@ int QgsTransectSample::createSample( QProgressDialog *pd ) QgsFeature fet; int nTotalTransects = 0; int nFeatures = 0; + int totalFeatures = 0; - if ( pd ) + if ( feedback ) { - pd->setMaximum( mStrataLayer->featureCount() ); + totalFeatures = mStrataLayer->featureCount(); } while ( strataIt.nextFeature( fet ) ) { - if ( pd ) + if ( feedback ) { - pd->setValue( nFeatures ); + feedback->setProgress( 100.0 * static_cast< double >( nFeatures ) / totalFeatures ); } - if ( pd && pd->wasCanceled() ) + if ( feedback && feedback->isCanceled() ) { break; } @@ -321,9 +321,9 @@ int QgsTransectSample::createSample( QProgressDialog *pd ) ++nFeatures; } - if ( pd ) + if ( feedback ) { - pd->setValue( mStrataLayer->featureCount() ); + feedback->setProgress( 100.0 ); } return 0; diff --git a/src/analysis/vector/qgstransectsample.h b/src/analysis/vector/qgstransectsample.h index 2eacc7f9f2a..153895582ac 100644 --- a/src/analysis/vector/qgstransectsample.h +++ b/src/analysis/vector/qgstransectsample.h @@ -25,7 +25,7 @@ class QgsGeometry; class QgsSpatialIndex; class QgsVectorLayer; class QgsPointXY; -class QProgressDialog; +class QgsFeedback; /** \ingroup analysis * A class for the creation of transect sample lines based on a set of strata polygons and baselines*/ @@ -44,7 +44,12 @@ class ANALYSIS_EXPORT QgsTransectSample const QString &baselineStrataId, const QString &outputPointLayer, const QString &outputLineLayer, const QString &usedBaselineLayer, double minTransectLength = 0.0, double baselineBufferDistance = -1.0, double baselineSimplificationTolerance = -1.0 ); - int createSample( QProgressDialog *pd ); + /** + * Creates the sample. + * + * The optional \a feedback argument can be used for progress reporting and cancelation support. + */ + int createSample( QgsFeedback *feedback = nullptr ); private: QgsTransectSample(); //default constructor forbidden diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index e493c40c2ed..e43794236b0 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -5127,7 +5127,11 @@ void QgisApp::showRasterCalculator() QProgressDialog p( tr( "Calculating..." ), tr( "Abort..." ), 0, 0 ); p.setWindowModality( Qt::WindowModal ); - QgsRasterCalculator::Result res = static_cast< QgsRasterCalculator::Result >( rc.processCalculation( &p ) ); + p.setMaximum( 100.0 ); + QgsFeedback feedback; + connect( &feedback, &QgsFeedback::progressChanged, &p, &QProgressDialog::setValue ); + connect( &feedback, &QgsFeedback::canceled, &p, &QProgressDialog::cancel ); + QgsRasterCalculator::Result res = static_cast< QgsRasterCalculator::Result >( rc.processCalculation( &feedback ) ); switch ( res ) { case QgsRasterCalculator::Success: From d5e63bc84f17d21444d6f312c177eaf95247153e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 22:07:48 +1000 Subject: [PATCH 222/364] Add missing headers to analysis CMakeLists --- src/analysis/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index d23b5c9a43f..9919e98e2e3 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -113,6 +113,8 @@ SET(QGIS_ANALYSIS_HDRS raster/qgsrastercalcnode.h raster/qgstotalcurvaturefilter.h + vector/mersenne-twister.h + vector/qgsgeometrysnapper.h vector/qgstransectsample.h vector/qgszonalstatistics.h @@ -126,6 +128,7 @@ SET(QGIS_ANALYSIS_HDRS interpolation/TriangleInterpolator.h interpolation/Vector3D.h interpolation/DualEdgeTriangulation.h + interpolation/MathUtils.h interpolation/Node.h interpolation/TriDecorator.h interpolation/Triangulation.h @@ -136,6 +139,8 @@ SET(QGIS_ANALYSIS_HDRS openstreetmap/qgsosmbase.h openstreetmap/qgsosmdatabase.h + openstreetmap/qgsosmdownload.h + openstreetmap/qgsosmimport.h network/qgsgraph.h network/qgsgraphbuilderinterface.h @@ -144,6 +149,7 @@ SET(QGIS_ANALYSIS_HDRS network/qgsnetworkspeedstrategy.h network/qgsnetworkdistancestrategy.h network/qgsgraphanalyzer.h + network/qgsvectorlayerdirector.h ) INCLUDE_DIRECTORIES( From 9ca57bd62cd2713c7cc31a4a9c4056edf74d2a6b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 22:38:12 +1000 Subject: [PATCH 223/364] Use a QgsFeatureSink instead of path to shapefile in QgsTinInterpolator Instead of just forcing writing the triangulation to a shapefile (boo!) change the parameter to use a QgsFeatureSink, so that anything which implements the QgsFeatureSink interface can be used for storing the triangulation. --- doc/api_break.dox | 1 + .../interpolation/qgstininterpolator.sip | 22 +++++++++++-- .../processing/algs/qgis/TinInterpolation.py | 32 +++++++++++-------- .../interpolation/DualEdgeTriangulation.cpp | 32 ++++--------------- .../interpolation/DualEdgeTriangulation.h | 4 +-- .../interpolation/NormVecDecorator.cpp | 6 ++-- src/analysis/interpolation/NormVecDecorator.h | 4 +-- src/analysis/interpolation/Triangulation.cpp | 8 ++++- src/analysis/interpolation/Triangulation.h | 25 +++++++++++++-- .../interpolation/qgstininterpolator.cpp | 15 +++++++-- .../interpolation/qgstininterpolator.h | 30 +++++++++++++---- 11 files changed, 117 insertions(+), 62 deletions(-) diff --git a/doc/api_break.dox b/doc/api_break.dox index 93bf38a17b9..1a21ffdc9d3 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -2338,6 +2338,7 @@ QgsTINInterpolator {#qgis_api_break_3_0_QgsTINInterpolator} ------------------ - The constructor takes a QgsFeedback argument instead of using a QProgressDialog. +- setExportTriangulationToFile() and setTriangulationFilePath() were removed. Use setTriangulationSink() instead. QgsTolerance {#qgis_api_break_3_0_QgsTolerance} ------------ diff --git a/python/analysis/interpolation/qgstininterpolator.sip b/python/analysis/interpolation/qgstininterpolator.sip index 390a1323f0f..945c69c9ae3 100644 --- a/python/analysis/interpolation/qgstininterpolator.sip +++ b/python/analysis/interpolation/qgstininterpolator.sip @@ -45,8 +45,26 @@ class QgsTINInterpolator: QgsInterpolator :rtype: int %End - void setExportTriangulationToFile( bool e ); - void setTriangulationFilePath( const QString &filepath ); + static QgsFields triangulationFields(); +%Docstring + Returns the fields output by features when saving the triangulation. + These fields should be used when creating + a suitable feature sink for setTriangulationSink() +.. seealso:: setTriangulationSink() +.. versionadded:: 3.0 + :rtype: QgsFields +%End + + void setTriangulationSink( QgsFeatureSink *sink ); +%Docstring + Sets the optional ``sink`` for saving the triangulation features. + + The sink must be setup to accept LineString features, with fields matching + those returned by triangulationFields(). + +.. seealso:: triangulationFields() +.. versionadded:: 3.0 +%End }; diff --git a/python/plugins/processing/algs/qgis/TinInterpolation.py b/python/plugins/processing/algs/qgis/TinInterpolation.py index 9c157b275bb..1bd0558e378 100644 --- a/python/plugins/processing/algs/qgis/TinInterpolation.py +++ b/python/plugins/processing/algs/qgis/TinInterpolation.py @@ -30,13 +30,16 @@ import os from qgis.PyQt.QtGui import QIcon from qgis.core import (QgsProcessingUtils, + QgsProcessing, QgsProcessingParameterDefinition, QgsProcessingParameterEnum, QgsProcessingParameterNumber, QgsProcessingParameterExtent, QgsProcessingParameterRasterDestination, - QgsProcessingParameterFileDestination, - QgsProcessingException) + QgsWkbTypes, + QgsProcessingParameterFeatureSink, + QgsProcessingException, + QgsCoordinateReferenceSystem) from qgis.analysis import (QgsInterpolator, QgsTINInterpolator, QgsGridFileWriter) @@ -85,7 +88,6 @@ class ParameterInterpolationData(QgsProcessingParameterDefinition): class TinInterpolation(QgisAlgorithm): - INTERPOLATION_DATA = 'INTERPOLATION_DATA' METHOD = 'METHOD' COLUMNS = 'COLUMNS' @@ -94,7 +96,7 @@ class TinInterpolation(QgisAlgorithm): CELLSIZE_Y = 'CELLSIZE_Y' EXTENT = 'EXTENT' OUTPUT = 'OUTPUT' - TRIANGULATION_FILE = 'TRIANGULATION_FILE' + TRIANGULATION = 'TRIANGULATION' def icon(self): return QIcon(os.path.join(pluginPath, 'images', 'interpolation.png')) @@ -134,10 +136,10 @@ class TinInterpolation(QgisAlgorithm): self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Interpolated'))) - triangulation_file_param = QgsProcessingParameterFileDestination(self.TRIANGULATION_FILE, - self.tr('Triangulation'), - self.tr('SHP files (*.shp)'), - optional=True) + triangulation_file_param = QgsProcessingParameterFeatureSink(self.TRIANGULATION, + self.tr('Triangulation'), + type=QgsProcessing.TypeVectorLine, + optional=True) triangulation_file_param.setCreateByDefault(False) self.addParameter(triangulation_file_param) @@ -156,7 +158,6 @@ class TinInterpolation(QgisAlgorithm): cellsizeY = self.parameterAsDouble(parameters, self.CELLSIZE_Y, context) bbox = self.parameterAsExtent(parameters, self.EXTENT, context) output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) - triangulation = self.parameterAsFileOutput(parameters, self.TRIANGULATION_FILE, context) if interpolationData is None: raise QgsProcessingException( @@ -168,6 +169,7 @@ class TinInterpolation(QgisAlgorithm): layerData = [] layers = [] + crs = QgsCoordinateReferenceSystem() for row in interpolationData.split(';'): v = row.split(',') data = QgsInterpolator.LayerData() @@ -176,6 +178,8 @@ class TinInterpolation(QgisAlgorithm): layer = QgsProcessingUtils.mapLayerFromString(v[0], context) data.vectorLayer = layer layers.append(layer) + if not crs.isValid(): + crs = layer.crs() data.zCoordInterpolation = bool(v[1]) data.interpolationAttribute = int(v[2]) @@ -192,10 +196,12 @@ class TinInterpolation(QgisAlgorithm): else: interpolationMethod = QgsTINInterpolator.CloughTocher + (triangulation_sink, triangulation_dest_id) = self.parameterAsSink(parameters, self.TRIANGULATION, context, + QgsTINInterpolator.triangulationFields(), QgsWkbTypes.LineString, crs) + interpolator = QgsTINInterpolator(layerData, interpolationMethod, feedback) - if triangulation is not None and triangulation != '': - interpolator.setExportTriangulationToFile(True) - interpolator.setTriangulationFilePath(triangulation) + if triangulation_sink is not None: + interpolator.setTriangulationSink(triangulation_sink) writer = QgsGridFileWriter(interpolator, output, @@ -206,4 +212,4 @@ class TinInterpolation(QgisAlgorithm): cellsizeY) writer.writeFile(feedback) - return {self.OUTPUT: output} + return {self.OUTPUT: output, self.TRIANGULATION: triangulation_dest_id} diff --git a/src/analysis/interpolation/DualEdgeTriangulation.cpp b/src/analysis/interpolation/DualEdgeTriangulation.cpp index d1111862ea2..f63ee467227 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.cpp +++ b/src/analysis/interpolation/DualEdgeTriangulation.cpp @@ -3081,30 +3081,9 @@ QList *DualEdgeTriangulation::getPointsAroundEdge( double x, double y ) } } -bool DualEdgeTriangulation::saveAsShapefile( const QString &fileName ) const +bool DualEdgeTriangulation::saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback ) const { - QString shapeFileName = fileName; - - QgsFields fields; - fields.append( QgsField( QStringLiteral( "type" ), QVariant::String, QStringLiteral( "String" ) ) ); - - // add the extension if not present - if ( shapeFileName.indexOf( QLatin1String( ".shp" ) ) == -1 ) - { - shapeFileName += QLatin1String( ".shp" ); - } - - //delete already existing files - if ( QFile::exists( shapeFileName ) ) - { - if ( !QgsVectorFileWriter::deleteShapeFile( shapeFileName ) ) - { - return false; - } - } - - QgsVectorFileWriter writer( shapeFileName, QStringLiteral( "Utf-8" ), fields, QgsWkbTypes::LineString ); - if ( writer.hasError() != QgsVectorFileWriter::NoError ) + if ( !sink ) { return false; } @@ -3123,6 +3102,9 @@ bool DualEdgeTriangulation::saveAsShapefile( const QString &fileName ) const for ( int i = 0; i < mHalfEdge.size(); ++i ) { + if ( feedback && feedback->isCanceled() ) + break; + HalfEdge *currentEdge = mHalfEdge[i]; if ( currentEdge->getPoint() != -1 && mHalfEdge[currentEdge->getDual()]->getPoint() != -1 && !alreadyVisitedEdges[currentEdge->getDual()] ) { @@ -3152,14 +3134,14 @@ bool DualEdgeTriangulation::saveAsShapefile( const QString &fileName ) const } edgeLineFeature.setAttribute( 0, attributeString ); - writer.addFeature( edgeLineFeature ); + sink->addFeature( edgeLineFeature, QgsFeatureSink::FastInsert ); } alreadyVisitedEdges[i] = true; } delete [] alreadyVisitedEdges; - return true; + return !feedback || !feedback->isCanceled(); } double DualEdgeTriangulation::swapMinAngle( int edge ) const diff --git a/src/analysis/interpolation/DualEdgeTriangulation.h b/src/analysis/interpolation/DualEdgeTriangulation.h index c95f4ce8429..bd887d40d20 100644 --- a/src/analysis/interpolation/DualEdgeTriangulation.h +++ b/src/analysis/interpolation/DualEdgeTriangulation.h @@ -103,9 +103,7 @@ class ANALYSIS_EXPORT DualEdgeTriangulation: public Triangulation //! Returns a value list with the numbers of the four points, which would be affected by an edge swap. This function is e.g. needed by NormVecDecorator to know the points, for which the normals have to be recalculated. The returned ValueList has to be deleted by the code which calls the method virtual QList *getPointsAroundEdge( double x, double y ) override; - /** Saves the triangulation as a (line) shapefile - \returns true in case of success*/ - virtual bool saveAsShapefile( const QString &fileName ) const override; + virtual bool saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const override; protected: //! X-coordinate of the upper right corner of the bounding box diff --git a/src/analysis/interpolation/NormVecDecorator.cpp b/src/analysis/interpolation/NormVecDecorator.cpp index 697c9081a07..cb390872814 100644 --- a/src/analysis/interpolation/NormVecDecorator.cpp +++ b/src/analysis/interpolation/NormVecDecorator.cpp @@ -17,6 +17,7 @@ #include "NormVecDecorator.h" #include "qgsfeedback.h" #include "qgslogger.h" +#include "qgsfields.h" #include NormVecDecorator::~NormVecDecorator() @@ -590,13 +591,12 @@ bool NormVecDecorator::swapEdge( double x, double y ) } } -bool NormVecDecorator::saveAsShapefile( const QString &fileName ) const +bool NormVecDecorator::saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback ) const { if ( !mTIN ) { return false; } - return mTIN->saveAsShapefile( fileName ); + return mTIN->saveTriangulation( sink, feedback ); } - diff --git a/src/analysis/interpolation/NormVecDecorator.h b/src/analysis/interpolation/NormVecDecorator.h index 7d18473fa44..7329a890d56 100644 --- a/src/analysis/interpolation/NormVecDecorator.h +++ b/src/analysis/interpolation/NormVecDecorator.h @@ -68,9 +68,7 @@ class ANALYSIS_EXPORT NormVecDecorator: public TriDecorator //! Swaps the edge which is closest to the point with x and y coordinates (if this is possible) and forces recalculation of the concerned normals (if alreadyestimated is true) virtual bool swapEdge( double x, double y ) override; - /** Saves the triangulation as a (line) shapefile - \returns true in case of success*/ - virtual bool saveAsShapefile( const QString &fileName ) const override; + virtual bool saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const override; protected: //! Is true, if the normals already have been estimated diff --git a/src/analysis/interpolation/Triangulation.cpp b/src/analysis/interpolation/Triangulation.cpp index 2febb7060a2..19ceadaf4ac 100644 --- a/src/analysis/interpolation/Triangulation.cpp +++ b/src/analysis/interpolation/Triangulation.cpp @@ -13,5 +13,11 @@ * * ***************************************************************************/ #include "Triangulation.h" +#include "qgsfields.h" -//empty file (abstract class) +QgsFields Triangulation::triangulationFields() +{ + QgsFields fields; + fields.append( QgsField( QStringLiteral( "type" ), QVariant::String, QStringLiteral( "String" ) ) ); + return fields; +} diff --git a/src/analysis/interpolation/Triangulation.h b/src/analysis/interpolation/Triangulation.h index 11fbb8a42d1..0299b09a373 100644 --- a/src/analysis/interpolation/Triangulation.h +++ b/src/analysis/interpolation/Triangulation.h @@ -23,7 +23,10 @@ #include "TriangleInterpolator.h" #include "qgis_analysis.h" +class QgsFeatureSink; class Line3D; +class QgsFields; +class QgsFeedback; #define SIP_NO_FILE @@ -152,10 +155,26 @@ class ANALYSIS_EXPORT Triangulation virtual bool swapEdge( double x, double y ) = 0; /** - * Saves the triangulation as a (line) shapefile - * \returns true in case of success + * Returns the fields output by features when calling + * saveTriangulation(). These fields should be used when creating + * a suitable feature sink for saveTriangulation() + * \see saveTriangulation() + * \since QGIS 3.0 */ - virtual bool saveAsShapefile( const QString &fileName ) const = 0; + static QgsFields triangulationFields(); + + /** + * Saves the triangulation features to a feature \a sink. + * + * The sink must be setup to accept LineString features, with fields matching + * those returned by triangulationFields(). + * + * \returns true in case of success + * + * \see triangulationFields() + * \since QGIS 3.0 + */ + virtual bool saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const = 0; }; #ifndef SIP_RUN diff --git a/src/analysis/interpolation/qgstininterpolator.cpp b/src/analysis/interpolation/qgstininterpolator.cpp index 17242774364..119a8e3db62 100644 --- a/src/analysis/interpolation/qgstininterpolator.cpp +++ b/src/analysis/interpolation/qgstininterpolator.cpp @@ -35,7 +35,6 @@ QgsTINInterpolator::QgsTINInterpolator( const QList &inputData, TINIn , mTriangleInterpolator( nullptr ) , mIsInitialized( false ) , mFeedback( feedback ) - , mExportTriangulationToFile( false ) , mInterpolation( interpolation ) { } @@ -67,6 +66,16 @@ int QgsTINInterpolator::interpolatePoint( double x, double y, double &result ) return 0; } +QgsFields QgsTINInterpolator::triangulationFields() +{ + return Triangulation::triangulationFields(); +} + +void QgsTINInterpolator::setTriangulationSink( QgsFeatureSink *sink ) +{ + mTriangulationSink = sink; +} + void QgsTINInterpolator::initialize() { DualEdgeTriangulation *dualEdgeTriangulation = new DualEdgeTriangulation( 100000, nullptr ); @@ -143,9 +152,9 @@ void QgsTINInterpolator::initialize() mIsInitialized = true; //debug - if ( mExportTriangulationToFile ) + if ( mTriangulationSink ) { - dualEdgeTriangulation->saveAsShapefile( mTriangulationFilePath ); + dualEdgeTriangulation->saveTriangulation( mTriangulationSink, mFeedback ); } } diff --git a/src/analysis/interpolation/qgstininterpolator.h b/src/analysis/interpolation/qgstininterpolator.h index 4e1cc240b98..bc3312d37d4 100644 --- a/src/analysis/interpolation/qgstininterpolator.h +++ b/src/analysis/interpolation/qgstininterpolator.h @@ -22,10 +22,12 @@ #include #include "qgis_analysis.h" +class QgsFeatureSink; class Triangulation; class TriangleInterpolator; class QgsFeature; class QgsFeedback; +class QgsFields; /** \ingroup analysis * Interpolation in a triangular irregular network*/ @@ -54,18 +56,34 @@ class ANALYSIS_EXPORT QgsTINInterpolator: public QgsInterpolator \returns 0 in case of success*/ int interpolatePoint( double x, double y, double &result ) override; - void setExportTriangulationToFile( bool e ) {mExportTriangulationToFile = e;} - void setTriangulationFilePath( const QString &filepath ) {mTriangulationFilePath = filepath;} + /** + * Returns the fields output by features when saving the triangulation. + * These fields should be used when creating + * a suitable feature sink for setTriangulationSink() + * \see setTriangulationSink() + * \since QGIS 3.0 + */ + static QgsFields triangulationFields(); + + /** + * Sets the optional \a sink for saving the triangulation features. + * + * The sink must be setup to accept LineString features, with fields matching + * those returned by triangulationFields(). + * + * \see triangulationFields() + * \since QGIS 3.0 + */ + void setTriangulationSink( QgsFeatureSink *sink ); private: Triangulation *mTriangulation = nullptr; TriangleInterpolator *mTriangleInterpolator = nullptr; bool mIsInitialized; QgsFeedback *mFeedback = nullptr; - //! If true: export triangulation to shapefile after initialization - bool mExportTriangulationToFile; - //! File path to export the triangulation - QString mTriangulationFilePath; + + //! Feature sink for triangulation + QgsFeatureSink *mTriangulationSink = nullptr; //! Type of interpolation TINInterpolation mInterpolation; From e6203847d3ff992c0ef41402ad1b4d92278032e4 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 26 Aug 2017 23:29:27 +1000 Subject: [PATCH 224/364] Update sip include --- python/analysis/analysis_auto.sip | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/analysis/analysis_auto.sip b/python/analysis/analysis_auto.sip index bb2c8263e25..929e30f7d51 100644 --- a/python/analysis/analysis_auto.sip +++ b/python/analysis/analysis_auto.sip @@ -12,6 +12,7 @@ %Include raster/qgsrastermatrix.sip %Include raster/qgsrastercalcnode.sip %Include raster/qgstotalcurvaturefilter.sip +%Include vector/qgsgeometrysnapper.sip %Include vector/qgstransectsample.sip %Include vector/qgszonalstatistics.sip %Include interpolation/qgsinterpolator.sip @@ -20,6 +21,8 @@ %Include interpolation/qgstininterpolator.sip %Include openstreetmap/qgsosmbase.sip %Include openstreetmap/qgsosmdatabase.sip +%Include openstreetmap/qgsosmdownload.sip +%Include openstreetmap/qgsosmimport.sip %Include network/qgsgraph.sip %Include network/qgsgraphbuilderinterface.sip %Include network/qgsgraphbuilder.sip @@ -27,8 +30,5 @@ %Include network/qgsnetworkspeedstrategy.sip %Include network/qgsnetworkdistancestrategy.sip %Include network/qgsgraphanalyzer.sip -%Include openstreetmap/qgsosmdownload.sip -%Include openstreetmap/qgsosmimport.sip -%Include vector/qgsgeometrysnapper.sip -%Include network/qgsgraphdirector.sip %Include network/qgsvectorlayerdirector.sip +%Include network/qgsgraphdirector.sip From 1e4e45d0a7d3cd3593b80bf300ca7d0c0c4daa29 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 29 Aug 2017 16:55:01 +0200 Subject: [PATCH 225/364] [bugfix] Workaround for bug 17087 deadlock Add the layer to the project after iteration completed. This prevents the bug to freeze QGIS forever but it doesn't of course fix it. --- src/core/qgsofflineediting.cpp | 81 ++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/core/qgsofflineediting.cpp b/src/core/qgsofflineediting.cpp index 3e1296508ec..84ad731f17e 100644 --- a/src/core/qgsofflineediting.cpp +++ b/src/core/qgsofflineediting.cpp @@ -141,7 +141,6 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath, { QString origLayerId = vl->id(); QgsVectorLayer *newLayer = copyVectorLayer( vl, db, dbPath, onlySelected ); - if ( newLayer ) { layerIdMapping.insert( origLayerId, newLayer ); @@ -581,45 +580,7 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit layer->name() + " (offline)", QStringLiteral( "spatialite" ) ); if ( newLayer->isValid() ) { - // mark as offline layer - newLayer->setCustomProperty( CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE, true ); - // store original layer source - newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_SOURCE, layer->source() ); - newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_PROVIDER, layer->providerType() ); - - // register this layer with the central layers registry - QgsProject::instance()->addMapLayers( - QList() << newLayer ); - - // copy style - copySymbology( layer, newLayer ); - - QgsLayerTreeGroup *layerTreeRoot = QgsProject::instance()->layerTreeRoot(); - // Find the parent group of the original layer - QgsLayerTreeLayer *layerTreeLayer = layerTreeRoot->findLayer( layer->id() ); - if ( layerTreeLayer ) - { - QgsLayerTreeGroup *parentTreeGroup = qobject_cast( layerTreeLayer->parent() ); - if ( parentTreeGroup ) - { - int index = parentTreeGroup->children().indexOf( layerTreeLayer ); - // Move the new layer from the root group to the new group - QgsLayerTreeLayer *newLayerTreeLayer = layerTreeRoot->findLayer( newLayer->id() ); - if ( newLayerTreeLayer ) - { - QgsLayerTreeNode *newLayerTreeLayerClone = newLayerTreeLayer->clone(); - QgsLayerTreeGroup *grp = qobject_cast( newLayerTreeLayer->parent() ); - parentTreeGroup->insertChildNode( index, newLayerTreeLayerClone ); - if ( grp ) - grp->removeChildNode( newLayerTreeLayer ); - } - } - } - - updateRelations( layer, newLayer ); - updateMapThemes( layer, newLayer ); - updateLayerOrder( layer, newLayer ); // copy features newLayer->startEditing(); QgsFeature f; @@ -703,6 +664,48 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit { showWarning( newLayer->commitErrors().join( QStringLiteral( "\n" ) ) ); } + + // mark as offline layer + newLayer->setCustomProperty( CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE, true ); + + // store original layer source + newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_SOURCE, layer->source() ); + newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_PROVIDER, layer->providerType() ); + + // register this layer with the central layers registry + QgsProject::instance()->addMapLayers( + QList() << newLayer ); + + // copy style + copySymbology( layer, newLayer ); + + QgsLayerTreeGroup *layerTreeRoot = QgsProject::instance()->layerTreeRoot(); + // Find the parent group of the original layer + QgsLayerTreeLayer *layerTreeLayer = layerTreeRoot->findLayer( layer->id() ); + if ( layerTreeLayer ) + { + QgsLayerTreeGroup *parentTreeGroup = qobject_cast( layerTreeLayer->parent() ); + if ( parentTreeGroup ) + { + int index = parentTreeGroup->children().indexOf( layerTreeLayer ); + // Move the new layer from the root group to the new group + QgsLayerTreeLayer *newLayerTreeLayer = layerTreeRoot->findLayer( newLayer->id() ); + if ( newLayerTreeLayer ) + { + QgsLayerTreeNode *newLayerTreeLayerClone = newLayerTreeLayer->clone(); + QgsLayerTreeGroup *grp = qobject_cast( newLayerTreeLayer->parent() ); + parentTreeGroup->insertChildNode( index, newLayerTreeLayerClone ); + if ( grp ) + grp->removeChildNode( newLayerTreeLayer ); + } + } + } + + updateRelations( layer, newLayer ); + updateMapThemes( layer, newLayer ); + updateLayerOrder( layer, newLayer ); + + } return newLayer; } From dbd86d0ddcef077488f1a91f0316483bb59854f6 Mon Sep 17 00:00:00 2001 From: Radim Blazek Date: Tue, 29 Aug 2017 20:28:06 +0200 Subject: [PATCH 226/364] highlight disappearing fix - second attempt, 4659691 and 51170aec follow-up --- src/gui/qgshighlight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgshighlight.cpp b/src/gui/qgshighlight.cpp index 7819bcea4f1..fbb5d404b9d 100644 --- a/src/gui/qgshighlight.cpp +++ b/src/gui/qgshighlight.cpp @@ -257,7 +257,7 @@ void QgsHighlight::paintPolygon( QPainter *p, QgsPolygon polygon ) void QgsHighlight::updatePosition() { - // nothing to do here... + updateRect(); } void QgsHighlight::paint( QPainter *p ) From fd0f6f2555461016c79d25134a4362250aa32c49 Mon Sep 17 00:00:00 2001 From: Radim Blazek Date: Tue, 29 Aug 2017 20:54:01 +0200 Subject: [PATCH 227/364] GRASS uppercase in project description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cde4ff40be..985c395aed5 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ modified. ## Supported raster formats include: - * Grass + * GRASS * USGS DEM * ArcInfo binary grid * ArcInfo ASCII grid From 9464ab75e62b2562e9ac7fef3dc43d2e22db3896 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Tue, 29 Aug 2017 20:59:15 +0200 Subject: [PATCH 228/364] fix translation string --- src/gui/qgsfilewidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgsfilewidget.cpp b/src/gui/qgsfilewidget.cpp index 2c6edaff7ef..b980e860886 100644 --- a/src/gui/qgsfilewidget.cpp +++ b/src/gui/qgsfilewidget.cpp @@ -264,7 +264,7 @@ void QgsFileWidget::openFileDialog() fileName = QFileDialog::getOpenFileName( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); break; case GetMultipleFiles: - title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select one ore more files" ); + title = !mDialogTitle.isEmpty() ? mDialogTitle : tr( "Select one or more files" ); fileNames = QFileDialog::getOpenFileNames( this, title, QFileInfo( oldPath ).absoluteFilePath(), mFilter ); break; case GetDirectory: From c12fc9c62feb430632caa141d032ece780c42154 Mon Sep 17 00:00:00 2001 From: Tom Elwertowski Date: Tue, 29 Aug 2017 17:27:20 -0400 Subject: [PATCH 229/364] Add headers needed by macOS for recently added std:: prefixes --- src/analysis/raster/qgsaspectfilter.cpp | 1 + src/analysis/raster/qgshillshadefilter.cpp | 1 + src/analysis/raster/qgsruggednessfilter.cpp | 1 + src/analysis/raster/qgsslopefilter.cpp | 1 + src/plugins/grass/qtermwidget/ColorScheme.cpp | 3 +++ src/plugins/grass/qtermwidget/TerminalDisplay.cpp | 3 +++ 6 files changed, 10 insertions(+) diff --git a/src/analysis/raster/qgsaspectfilter.cpp b/src/analysis/raster/qgsaspectfilter.cpp index 0a4f6060eb4..a6c69611f26 100644 --- a/src/analysis/raster/qgsaspectfilter.cpp +++ b/src/analysis/raster/qgsaspectfilter.cpp @@ -16,6 +16,7 @@ ***************************************************************************/ #include "qgsaspectfilter.h" +#include QgsAspectFilter::QgsAspectFilter( const QString &inputFile, const QString &outputFile, const QString &outputFormat ) : QgsDerivativeFilter( inputFile, outputFile, outputFormat ) diff --git a/src/analysis/raster/qgshillshadefilter.cpp b/src/analysis/raster/qgshillshadefilter.cpp index d62529c8aad..b0ee79da57c 100644 --- a/src/analysis/raster/qgshillshadefilter.cpp +++ b/src/analysis/raster/qgshillshadefilter.cpp @@ -16,6 +16,7 @@ ***************************************************************************/ #include "qgshillshadefilter.h" +#include QgsHillshadeFilter::QgsHillshadeFilter( const QString &inputFile, const QString &outputFile, const QString &outputFormat, double lightAzimuth, double lightAngle ) diff --git a/src/analysis/raster/qgsruggednessfilter.cpp b/src/analysis/raster/qgsruggednessfilter.cpp index dfc4808e178..1f28b48de1c 100644 --- a/src/analysis/raster/qgsruggednessfilter.cpp +++ b/src/analysis/raster/qgsruggednessfilter.cpp @@ -16,6 +16,7 @@ ***************************************************************************/ #include "qgsruggednessfilter.h" +#include QgsRuggednessFilter::QgsRuggednessFilter( const QString &inputFile, const QString &outputFile, const QString &outputFormat ): QgsNineCellFilter( inputFile, outputFile, outputFormat ) { diff --git a/src/analysis/raster/qgsslopefilter.cpp b/src/analysis/raster/qgsslopefilter.cpp index db263daddf0..a58fc981700 100644 --- a/src/analysis/raster/qgsslopefilter.cpp +++ b/src/analysis/raster/qgsslopefilter.cpp @@ -16,6 +16,7 @@ ***************************************************************************/ #include "qgsslopefilter.h" +#include QgsSlopeFilter::QgsSlopeFilter( const QString &inputFile, const QString &outputFile, const QString &outputFormat ) : QgsDerivativeFilter( inputFile, outputFile, outputFormat ) diff --git a/src/plugins/grass/qtermwidget/ColorScheme.cpp b/src/plugins/grass/qtermwidget/ColorScheme.cpp index 2cc2f3ed425..c029803e083 100644 --- a/src/plugins/grass/qtermwidget/ColorScheme.cpp +++ b/src/plugins/grass/qtermwidget/ColorScheme.cpp @@ -23,6 +23,9 @@ #include "ColorScheme.h" #include "tools.h" +// System +#include + // Qt #include #include diff --git a/src/plugins/grass/qtermwidget/TerminalDisplay.cpp b/src/plugins/grass/qtermwidget/TerminalDisplay.cpp index b0ca8d86b98..98f290437d4 100644 --- a/src/plugins/grass/qtermwidget/TerminalDisplay.cpp +++ b/src/plugins/grass/qtermwidget/TerminalDisplay.cpp @@ -23,6 +23,9 @@ // Own #include "TerminalDisplay.h" +// System +#include + // Qt #include #include From 184899ec30cc5fb48900f242b083671ac5a7c890 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 07:28:25 +1000 Subject: [PATCH 230/364] Port remaining plotly based algs to new API --- .gitignore | 1 + .../plugins/processing/algs/qgis/BarPlot.py | 7 +-- .../plugins/processing/algs/qgis/BoxPlot.py | 56 ++++++++++--------- .../processing/algs/qgis/MeanAndStdDevPlot.py | 40 ++++++------- .../plugins/processing/algs/qgis/PolarPlot.py | 36 ++++++------ .../algs/qgis/QGISAlgorithmProvider.py | 29 +++++----- .../algs/qgis/RasterLayerHistogram.py | 39 ++++++++----- .../algs/qgis/VectorLayerHistogram.py | 41 +++++++------- .../algs/qgis/VectorLayerScatterplot.py | 47 ++++++++-------- .../algs/qgis/VectorLayerScatterplot3D.py | 55 +++++++++--------- python/plugins/processing/tools/raster.py | 4 +- tests/testdata/landsat-int16-b1.tif.aux.xml | 20 ------- 12 files changed, 188 insertions(+), 187 deletions(-) delete mode 100644 tests/testdata/landsat-int16-b1.tif.aux.xml diff --git a/.gitignore b/.gitignore index d31f8283e40..2dbf609ff2a 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ tests/testdata/grass/wgs84/test/.gislock tests/testdata/grass/wgs84/test6/.gislock tests/testdata/grass/wgs84/test7/.gislock tests/testdata/*.aux.xml +tests/testdata/landsat-int16-b1.tif.aux.xml tests/testdata/raster/*.aux.xml tests/testdata/raster/band1_byte_noct_epsg4326.tif.aux.xml tests/testdata/raster/band1_float32_noct_epsg4326.tif.aux.xml diff --git a/python/plugins/processing/algs/qgis/BarPlot.py b/python/plugins/processing/algs/qgis/BarPlot.py index d4c163fc71c..388d83f96a9 100644 --- a/python/plugins/processing/algs/qgis/BarPlot.py +++ b/python/plugins/processing/algs/qgis/BarPlot.py @@ -29,10 +29,7 @@ import plotly as plt import plotly.graph_objs as go -from qgis.core import (QgsApplication, - QgsFeatureSink, - QgsProcessingUtils, - QgsProcessingParameterFeatureSource, +from qgis.core import (QgsProcessingParameterFeatureSource, QgsProcessingParameterField, QgsProcessingParameterFileDestination, QgsProcessingOutputHtml) @@ -63,7 +60,7 @@ class BarPlot(QgisAlgorithm): self.tr('Value field'), None, self.INPUT, QgsProcessingParameterField.Numeric)) - self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Added'), self.tr('HTML files (*.html)'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Bar plot'), self.tr('HTML files (*.html)'))) self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Bar plot'))) diff --git a/python/plugins/processing/algs/qgis/BoxPlot.py b/python/plugins/processing/algs/qgis/BoxPlot.py index 81915c624d8..642679aff61 100644 --- a/python/plugins/processing/algs/qgis/BoxPlot.py +++ b/python/plugins/processing/algs/qgis/BoxPlot.py @@ -28,14 +28,13 @@ __revision__ = '$Format:%H$' import plotly as plt import plotly.graph_objs as go -from qgis.core import (QgsApplication, - QgsFeatureSink, - QgsProcessingUtils) -from processing.core.parameters import ParameterTable -from processing.core.parameters import ParameterTableField -from processing.core.parameters import ParameterSelection +from qgis.core import (QgsProcessingParameterFeatureSource, + QgsProcessingParameterField, + QgsProcessingParameterEnum, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml, + QgsFeatureRequest) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.outputs import OutputHTML from processing.tools import vector @@ -54,25 +53,27 @@ class BoxPlot(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterTable(self.INPUT, self.tr('Input table'))) - self.addParameter(ParameterTableField(self.NAME_FIELD, - self.tr('Category name field'), - self.INPUT, - ParameterTableField.DATA_TYPE_ANY)) - self.addParameter(ParameterTableField(self.VALUE_FIELD, - self.tr('Value field'), - self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterField(self.NAME_FIELD, + self.tr('Category name field'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Any)) + self.addParameter(QgsProcessingParameterField(self.VALUE_FIELD, + self.tr('Value field'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) msd = [self.tr('Show Mean'), self.tr('Show Standard Deviation'), self.tr('Don\'t show Mean and Standard Deviation') ] - self.addParameter(ParameterSelection( + self.addParameter(QgsProcessingParameterEnum( self.MSD, self.tr('Additional Statistic Lines'), - msd, default=0)) + options=msd, defaultValue=0)) - self.addOutput(OutputHTML(self.OUTPUT, self.tr('Box plot'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Box plot'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Box plot'))) def name(self): return 'boxplot' @@ -81,17 +82,18 @@ class BoxPlot(QgisAlgorithm): return self.tr('Box plot') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - namefieldname = self.getParameterValue(self.NAME_FIELD) - valuefieldname = self.getParameterValue(self.VALUE_FIELD) + source = self.parameterAsSource(parameters, self.INPUT, context) + namefieldname = self.parameterAsString(parameters, self.NAME_FIELD, context) + valuefieldname = self.parameterAsString(parameters, self.VALUE_FIELD, context) - output = self.getOutputValue(self.OUTPUT) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) - values = vector.values(layer, valuefieldname) + values = vector.values(source, valuefieldname) - x_var = [i[namefieldname] for i in layer.getFeatures()] + x_index = source.fields().lookupField(namefieldname) + x_var = [i[namefieldname] for i in source.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([x_index]))] - msdIndex = self.getParameterValue(self.MSD) + msdIndex = self.parameterAsEnum(parameters, self.MSD, context) msd = True if msdIndex == 1: @@ -105,3 +107,5 @@ class BoxPlot(QgisAlgorithm): boxmean=msd)] plt.offline.plot(data, filename=output, auto_open=False) + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py b/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py index fdca9a7a4d6..f5e64299a96 100644 --- a/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py +++ b/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py @@ -28,13 +28,12 @@ __revision__ = '$Format:%H$' import plotly as plt import plotly.graph_objs as go -from qgis.core import (QgsApplication, - QgsFeatureSink, - QgsProcessingUtils) +from qgis.core import (QgsProcessingParameterFeatureSource, + QgsProcessingParameterField, + QgsProcessingUtils, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterTable -from processing.core.parameters import ParameterTableField -from processing.core.outputs import OutputHTML from processing.tools import vector @@ -53,15 +52,16 @@ class MeanAndStdDevPlot(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterTable(self.INPUT, - self.tr('Input table'))) - self.addParameter(ParameterTableField(self.NAME_FIELD, - self.tr('Category name field'), self.INPUT, - ParameterTableField.DATA_TYPE_ANY)) - self.addParameter(ParameterTableField(self.VALUE_FIELD, - self.tr('Value field'), self.INPUT)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input table'))) + self.addParameter(QgsProcessingParameterField(self.NAME_FIELD, + self.tr('Category name field'), parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Any)) + self.addParameter(QgsProcessingParameterField(self.VALUE_FIELD, + self.tr('Value field'), parentLayerParameterName=self.INPUT)) - self.addOutput(OutputHTML(self.OUTPUT, self.tr('Plot'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Plot'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Plot'))) def name(self): return 'meanandstandarddeviationplot' @@ -70,13 +70,13 @@ class MeanAndStdDevPlot(QgisAlgorithm): return self.tr('Mean and standard deviation plot') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - namefieldname = self.getParameterValue(self.NAME_FIELD) - valuefieldname = self.getParameterValue(self.VALUE_FIELD) + source = self.parameterAsSource(parameters, self.INPUT, context) + namefieldname = self.parameterAsString(parameters, self.NAME_FIELD, context) + valuefieldname = self.parameterAsString(parameters, self.VALUE_FIELD, context) - output = self.getOutputValue(self.OUTPUT) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) - values = vector.values(layer, namefieldname, valuefieldname) + values = vector.values(source, namefieldname, valuefieldname) d = {} for i in range(len(values[namefieldname])): @@ -93,3 +93,5 @@ class MeanAndStdDevPlot(QgisAlgorithm): name=k )) plt.offline.plot(data, filename=output, auto_open=False) + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/PolarPlot.py b/python/plugins/processing/algs/qgis/PolarPlot.py index d3bb27e330b..dd0b650786c 100644 --- a/python/plugins/processing/algs/qgis/PolarPlot.py +++ b/python/plugins/processing/algs/qgis/PolarPlot.py @@ -29,12 +29,11 @@ import plotly as plt import plotly.graph_objs as go import numpy as np -from qgis.core import (QgsApplication, - QgsProcessingUtils) +from qgis.core import (QgsProcessingParameterFeatureSource, + QgsProcessingParameterField, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterTable -from processing.core.parameters import ParameterTableField -from processing.core.outputs import OutputHTML from processing.tools import vector @@ -52,14 +51,15 @@ class PolarPlot(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterTable(self.INPUT, - self.tr('Input table'))) - self.addParameter(ParameterTableField(self.NAME_FIELD, - self.tr('Category name field'), self.INPUT)) # FIXME unused? - self.addParameter(ParameterTableField(self.VALUE_FIELD, - self.tr('Value field'), self.INPUT)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterField(self.NAME_FIELD, + self.tr('Category name field'), parentLayerParameterName=self.INPUT)) # FIXME unused? + self.addParameter(QgsProcessingParameterField(self.VALUE_FIELD, + self.tr('Value field'), parentLayerParameterName=self.INPUT)) - self.addOutput(OutputHTML(self.OUTPUT, self.tr('Polar plot'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Polar plot'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Polar plot'))) def name(self): return 'polarplot' @@ -68,14 +68,16 @@ class PolarPlot(QgisAlgorithm): return self.tr('Polar plot') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - namefieldname = self.getParameterValue(self.NAME_FIELD) # NOQA FIXME unused? - valuefieldname = self.getParameterValue(self.VALUE_FIELD) + source = self.parameterAsSource(parameters, self.INPUT, context) + namefieldname = self.parameterAsString(parameters, self.NAME_FIELD, context) # NOQA FIXME unused? + valuefieldname = self.parameterAsString(parameters, self.VALUE_FIELD, context) - output = self.getOutputValue(self.OUTPUT) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) - values = vector.values(layer, valuefieldname) + values = vector.values(source, valuefieldname) data = [go.Area(r=values[valuefieldname], t=np.degrees(np.arange(0.0, 2 * np.pi, 2 * np.pi / len(values[valuefieldname]))))] plt.offline.plot(data, filename=output, auto_open=False) + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 0f2621d936b..8e062e8f664 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -323,20 +323,23 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ] if hasPlotly: - # from .VectorLayerHistogram import VectorLayerHistogram - # from .RasterLayerHistogram import RasterLayerHistogram - # from .VectorLayerScatterplot import VectorLayerScatterplot - # from .MeanAndStdDevPlot import MeanAndStdDevPlot from .BarPlot import BarPlot - # from .PolarPlot import PolarPlot - # from .BoxPlot import BoxPlot - # from .VectorLayerScatterplot3D import VectorLayerScatterplot3D - # - algs.extend([BarPlot()]) - #[VectorLayerHistogram(), RasterLayerHistogram(), - # VectorLayerScatterplot(), MeanAndStdDevPlot(), - # BarPlot(), PolarPlot(), BoxPlot(), - # VectorLayerScatterplot3D()]) + from .BoxPlot import BoxPlot + from .MeanAndStdDevPlot import MeanAndStdDevPlot + from .PolarPlot import PolarPlot + from .RasterLayerHistogram import RasterLayerHistogram + from .VectorLayerHistogram import VectorLayerHistogram + from .VectorLayerScatterplot import VectorLayerScatterplot + from .VectorLayerScatterplot3D import VectorLayerScatterplot3D + + algs.extend([BarPlot(), + BoxPlot(), + MeanAndStdDevPlot(), + PolarPlot(), + RasterLayerHistogram(), + VectorLayerHistogram(), + VectorLayerScatterplot(), + VectorLayerScatterplot3D()]) # to store algs added by 3rd party plugins as scripts folder = os.path.join(os.path.dirname(__file__), 'scripts') diff --git a/python/plugins/processing/algs/qgis/RasterLayerHistogram.py b/python/plugins/processing/algs/qgis/RasterLayerHistogram.py index bd2da55208f..02488cc6361 100644 --- a/python/plugins/processing/algs/qgis/RasterLayerHistogram.py +++ b/python/plugins/processing/algs/qgis/RasterLayerHistogram.py @@ -28,12 +28,12 @@ __revision__ = '$Format:%H$' import plotly as plt import plotly.graph_objs as go -from qgis.core import (QgsApplication, - QgsProcessingUtils) +from qgis.core import (QgsProcessingParameterRasterLayer, + QgsProcessingParameterBand, + QgsProcessingParameterNumber, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterNumber -from processing.core.parameters import ParameterRaster -from processing.core.outputs import OutputHTML from processing.tools import raster @@ -41,7 +41,8 @@ class RasterLayerHistogram(QgisAlgorithm): INPUT = 'INPUT' BINS = 'BINS' - PLOT = 'PLOT' + OUTPUT = 'OUTPUT' + BAND = 'BAND' def group(self): return self.tr('Graphics') @@ -50,12 +51,17 @@ class RasterLayerHistogram(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterRaster(self.INPUT, - self.tr('Input layer'))) - self.addParameter(ParameterNumber(self.BINS, - self.tr('Number of bins'), 2, None, 10)) + self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterBand(self.BAND, + self.tr('Band number'), + 1, + self.INPUT)) + self.addParameter(QgsProcessingParameterNumber(self.BINS, + self.tr('number of bins'), minValue=2, defaultValue=10)) - self.addOutput(OutputHTML(self.PLOT, self.tr('Histogram'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Histogram'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Histogram'))) def name(self): return 'rasterlayerhistogram' @@ -64,13 +70,14 @@ class RasterLayerHistogram(QgisAlgorithm): return self.tr('Raster layer histogram') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - nbins = self.getParameterValue(self.BINS) + layer = self.parameterAsRasterLayer(parameters, self.INPUT, context) + band = self.parameterAsInt(parameters, self.BAND, context) + nbins = self.parameterAsInt(parameters, self.BINS, context) - output = self.getOutputValue(self.PLOT) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) # ALERT: this is potentially blocking if the layer is too big - values = raster.scanraster(layer, feedback) + values = raster.scanraster(layer, feedback, band) valueslist = [] for v in values: @@ -80,3 +87,5 @@ class RasterLayerHistogram(QgisAlgorithm): data = [go.Histogram(x=valueslist, nbinsx=nbins)] plt.offline.plot(data, filename=output, auto_open=False) + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/VectorLayerHistogram.py b/python/plugins/processing/algs/qgis/VectorLayerHistogram.py index 5d8f1c3da63..ce0137baa56 100644 --- a/python/plugins/processing/algs/qgis/VectorLayerHistogram.py +++ b/python/plugins/processing/algs/qgis/VectorLayerHistogram.py @@ -28,14 +28,12 @@ __revision__ = '$Format:%H$' import plotly as plt import plotly.graph_objs as go -from qgis.core import (QgsApplication, - QgsFeatureSink, - QgsProcessingUtils) +from qgis.core import (QgsProcessingParameterFeatureSource, + QgsProcessingParameterField, + QgsProcessingParameterNumber, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterTableField -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputHTML from processing.tools import vector @@ -53,15 +51,16 @@ class VectorLayerHistogram(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'))) - self.addParameter(ParameterTableField(self.FIELD, - self.tr('Attribute'), self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) - self.addParameter(ParameterNumber(self.BINS, - self.tr('number of bins'), 2, None, 10)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterField(self.FIELD, + self.tr('Attribute'), parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) + self.addParameter(QgsProcessingParameterNumber(self.BINS, + self.tr('number of bins'), minValue=2, defaultValue=10)) - self.addOutput(OutputHTML(self.OUTPUT, self.tr('Histogram'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Histogram'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Histogram'))) def name(self): return 'vectorlayerhistogram' @@ -70,14 +69,16 @@ class VectorLayerHistogram(QgisAlgorithm): return self.tr('Vector layer histogram') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - fieldname = self.getParameterValue(self.FIELD) - bins = self.getParameterValue(self.BINS) + source = self.parameterAsSource(parameters, self.INPUT, context) + fieldname = self.parameterAsString(parameters, self.FIELD, context) + bins = self.parameterAsInt(parameters, self.BINS, context) - output = self.getOutputValue(self.OUTPUT) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) - values = vector.values(layer, fieldname) + values = vector.values(source, fieldname) data = [go.Histogram(x=values[fieldname], nbinsx=bins)] plt.offline.plot(data, filename=output, auto_open=False) + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py b/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py index 62a16e12038..b89a6226141 100644 --- a/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py +++ b/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py @@ -28,14 +28,12 @@ __revision__ = '$Format:%H$' import plotly as plt import plotly.graph_objs as go -from qgis.core import (QgsApplication, - QgsFeatureSink, - QgsProcessingUtils) +from qgis.core import (QgsProcessingParameterFeatureSource, + QgsProcessingParameterField, + QgsProcessingUtils, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterTableField -from processing.core.outputs import OutputHTML - from processing.tools import vector @@ -53,18 +51,19 @@ class VectorLayerScatterplot(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'))) - self.addParameter(ParameterTableField(self.XFIELD, - self.tr('X attribute'), - self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) - self.addParameter(ParameterTableField(self.YFIELD, - self.tr('Y attribute'), - self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterField(self.XFIELD, + self.tr('X attribute'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) + self.addParameter(QgsProcessingParameterField(self.YFIELD, + self.tr('Y attribute'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) - self.addOutput(OutputHTML(self.OUTPUT, self.tr('Scatterplot'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Scatterplot'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Scatterplot'))) def name(self): return 'vectorlayerscatterplot' @@ -73,14 +72,16 @@ class VectorLayerScatterplot(QgisAlgorithm): return self.tr('Vector layer scatterplot') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - xfieldname = self.getParameterValue(self.XFIELD) - yfieldname = self.getParameterValue(self.YFIELD) + source = self.parameterAsSource(parameters, self.INPUT, context) + xfieldname = self.parameterAsString(parameters, self.XFIELD, context) + yfieldname = self.parameterAsString(parameters, self.YFIELD, context) - output = self.getOutputValue(self.OUTPUT) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) - values = vector.values(layer, xfieldname, yfieldname) + values = vector.values(source, xfieldname, yfieldname) data = [go.Scatter(x=values[xfieldname], y=values[yfieldname], mode='markers')] plt.offline.plot(data, filename=output, auto_open=False) + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py b/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py index 97720c98f02..da5b27631c7 100644 --- a/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py +++ b/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py @@ -28,13 +28,12 @@ __revision__ = '$Format:%H$' import plotly as plt import plotly.graph_objs as go -from qgis.core import (QgsApplication, - QgsFeatureSink, +from qgis.core import (QgsProcessingParameterFeatureSource, + QgsProcessingParameterField, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml, QgsProcessingUtils) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterTableField -from processing.core.outputs import OutputHTML from processing.tools import vector @@ -54,22 +53,23 @@ class VectorLayerScatterplot3D(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'))) - self.addParameter(ParameterTableField(self.XFIELD, - self.tr('X attribute'), - self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) - self.addParameter(ParameterTableField(self.YFIELD, - self.tr('Y attribute'), - self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) - self.addParameter(ParameterTableField(self.ZFIELD, - self.tr('Z attribute'), - self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterField(self.XFIELD, + self.tr('X attribute'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) + self.addParameter(QgsProcessingParameterField(self.YFIELD, + self.tr('Y attribute'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) + self.addParameter(QgsProcessingParameterField(self.ZFIELD, + self.tr('Z attribute'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.Numeric)) - self.addOutput(OutputHTML(self.OUTPUT, self.tr('Scatterplot 3D'))) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Histogram'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Histogram'))) def name(self): return 'scatter3dplot' @@ -78,15 +78,14 @@ class VectorLayerScatterplot3D(QgisAlgorithm): return self.tr('Vector layer scatterplot 3D') def processAlgorithm(self, parameters, context, feedback): + source = self.parameterAsSource(parameters, self.INPUT, context) + xfieldname = self.parameterAsString(parameters, self.XFIELD, context) + yfieldname = self.parameterAsString(parameters, self.YFIELD, context) + zfieldname = self.parameterAsString(parameters, self.ZFIELD, context) - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - xfieldname = self.getParameterValue(self.XFIELD) - yfieldname = self.getParameterValue(self.YFIELD) - zfieldname = self.getParameterValue(self.ZFIELD) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) - output = self.getOutputValue(self.OUTPUT) - - values = vector.values(layer, xfieldname, yfieldname, zfieldname) + values = vector.values(source, xfieldname, yfieldname, zfieldname) data = [go.Scatter3d( x=values[xfieldname], @@ -95,3 +94,5 @@ class VectorLayerScatterplot3D(QgisAlgorithm): mode='markers')] plt.offline.plot(data, filename=output, auto_open=False) + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/tools/raster.py b/python/plugins/processing/tools/raster.py index 0ddc9db02ec..8cc8889ab58 100644 --- a/python/plugins/processing/tools/raster.py +++ b/python/plugins/processing/tools/raster.py @@ -38,10 +38,10 @@ from osgeo import gdal from qgis.core import QgsProcessingException -def scanraster(layer, feedback): +def scanraster(layer, feedback, band_number=1): filename = str(layer.source()) dataset = gdal.Open(filename, gdal.GA_ReadOnly) - band = dataset.GetRasterBand(1) + band = dataset.GetRasterBand(band_number) nodata = band.GetNoDataValue() bandtype = gdal.GetDataTypeName(band.DataType) for y in range(band.YSize): diff --git a/tests/testdata/landsat-int16-b1.tif.aux.xml b/tests/testdata/landsat-int16-b1.tif.aux.xml deleted file mode 100644 index 9aac780357f..00000000000 --- a/tests/testdata/landsat-int16-b1.tif.aux.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - 121.5555555555556 - 130.4444444444445 - 9 - 0 - 0 - 22|377|2878|10013|13500|9834|2936|409|31 - - - - 130 - 126.001725 - 122 - 1.1294343825018 - - - From 12e69d09914d443adb2ca48f8c89416fc762cd64 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Wed, 30 Aug 2017 00:05:57 +0200 Subject: [PATCH 231/364] dxf export: * empty field name resets to layer name (fixes #17060) * allow forcing to 2d to support polyline width (fixes #17049) --- python/core/dxf/qgsdxfexport.sip | 15 ++++++++++ src/app/qgisapp.cpp | 1 + src/app/qgsdxfexportdialog.cpp | 7 ++++- src/app/qgsdxfexportdialog.h | 1 + src/core/dxf/qgsdxfexport.cpp | 51 ++++++++++++++++---------------- src/core/dxf/qgsdxfexport.h | 15 ++++++++++ src/ui/qgsdxfexportdialogbase.ui | 27 +++++++++++------ 7 files changed, 82 insertions(+), 35 deletions(-) diff --git a/python/core/dxf/qgsdxfexport.sip b/python/core/dxf/qgsdxfexport.sip index 7a5f761d684..6eeb2fb9bbd 100644 --- a/python/core/dxf/qgsdxfexport.sip +++ b/python/core/dxf/qgsdxfexport.sip @@ -126,6 +126,21 @@ class QgsDxfExport :rtype: bool %End + void setForce2d( bool force2d ); +%Docstring + Force 2d output (eg. to support linewidth in polylines) + \param force2d flag +.. seealso:: force2d +%End + + bool force2d(); +%Docstring + Retrieve whether the output should be forced to 2d + :return: flag +.. seealso:: setForce2d + :rtype: bool +%End + static int closestColorMatch( QRgb color ); %Docstring Get DXF palette index of nearest entry for given color diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index e43794236b0..78c8cee15c4 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -5528,6 +5528,7 @@ void QgisApp::dxfExport() dxfExport.setSymbologyExport( d.symbologyMode() ); dxfExport.setLayerTitleAsName( d.layerTitleAsName() ); dxfExport.setDestinationCrs( d.crs() ); + dxfExport.setForce2d( d.force2d() ); if ( mapCanvas() ) { //extent diff --git a/src/app/qgsdxfexportdialog.cpp b/src/app/qgsdxfexportdialog.cpp index 1350cbcd4f4..37fe1fd77e0 100644 --- a/src/app/qgsdxfexportdialog.cpp +++ b/src/app/qgsdxfexportdialog.cpp @@ -52,9 +52,9 @@ QWidget *FieldSelectorDelegate::createEditor( QWidget *parent, const QStyleOptio if ( !vl ) return nullptr; - QgsFieldComboBox *w = new QgsFieldComboBox( parent ); w->setLayer( vl ); + w->setAllowEmptyFieldName( true ); return w; } @@ -607,6 +607,11 @@ bool QgsDxfExportDialog::layerTitleAsName() const return mLayerTitleAsName->isChecked(); } +bool QgsDxfExportDialog::force2d() const +{ + return mForce2d->isChecked(); +} + void QgsDxfExportDialog::saveSettings() { QgsSettings s; diff --git a/src/app/qgsdxfexportdialog.h b/src/app/qgsdxfexportdialog.h index 3f42aff4944..c3ea3fce305 100644 --- a/src/app/qgsdxfexportdialog.h +++ b/src/app/qgsdxfexportdialog.h @@ -88,6 +88,7 @@ class QgsDxfExportDialog : public QDialog, private Ui::QgsDxfExportDialogBase QString saveFile() const; bool exportMapExtent() const; bool layerTitleAsName() const; + bool force2d() const; QString mapTheme() const; QString encoding() const; QgsCoordinateReferenceSystem crs() const; diff --git a/src/core/dxf/qgsdxfexport.cpp b/src/core/dxf/qgsdxfexport.cpp index 9e4b4069070..55a78847b49 100644 --- a/src/core/dxf/qgsdxfexport.cpp +++ b/src/core/dxf/qgsdxfexport.cpp @@ -375,6 +375,7 @@ QgsDxfExport::QgsDxfExport() , mNextHandleId( DXF_HANDSEED ) , mBlockCounter( 0 ) , mFactor( 1 ) + , mForce2d( false ) { } @@ -443,7 +444,7 @@ void QgsDxfExport::writeGroup( int code, const QgsPoint &p ) { writeGroup( code + 10, p.x() ); writeGroup( code + 20, p.y() ); - if ( p.is3D() && std::isfinite( p.z() ) ) + if ( !mForce2d && p.is3D() && std::isfinite( p.z() ) ) writeGroup( code + 30, p.z() ); } @@ -735,28 +736,28 @@ void QgsDxfExport::writeTables() writeGroup( 5, QgsPoint( 1.0, 1.0 ) ); // grid spacing writeGroup( 6, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) ); // view direction from target point writeGroup( 7, QgsPoint( mExtent.center() ) ); // view target point - writeGroup( 40, mExtent.height() ); // view height - writeGroup( 41, mExtent.width() / mExtent.height() ); // view aspect ratio - writeGroup( 42, 50.0 ); // lens length - writeGroup( 43, 0.0 ); // front clipping plane - writeGroup( 44, 0.0 ); // back clipping plane - writeGroup( 50, 0.0 ); // snap rotation - writeGroup( 51, 0.0 ); // view twist angle - writeGroup( 71, 0 ); // view mode (0 = deactivates) - writeGroup( 72, 100 ); // circle zoom percent - writeGroup( 73, 1 ); // fast zoom setting - writeGroup( 74, 1 ); // UCSICON setting - writeGroup( 75, 0 ); // snapping off - writeGroup( 76, 0 ); // grid off - writeGroup( 77, 0 ); // snap style - writeGroup( 78, 0 ); // snap isopair - writeGroup( 281, 0 ); // render mode (0 = 2D optimized) - writeGroup( 65, 1 ); // value of UCSVP for this viewport + writeGroup( 40, mExtent.height() ); // view height + writeGroup( 41, mExtent.width() / mExtent.height() ); // view aspect ratio + writeGroup( 42, 50.0 ); // lens length + writeGroup( 43, 0.0 ); // front clipping plane + writeGroup( 44, 0.0 ); // back clipping plane + writeGroup( 50, 0.0 ); // snap rotation + writeGroup( 51, 0.0 ); // view twist angle + writeGroup( 71, 0 ); // view mode (0 = deactivates) + writeGroup( 72, 100 ); // circle zoom percent + writeGroup( 73, 1 ); // fast zoom setting + writeGroup( 74, 1 ); // UCSICON setting + writeGroup( 75, 0 ); // snapping off + writeGroup( 76, 0 ); // grid off + writeGroup( 77, 0 ); // snap style + writeGroup( 78, 0 ); // snap isopair + writeGroup( 281, 0 ); // render mode (0 = 2D optimized) + writeGroup( 65, 1 ); // value of UCSVP for this viewport writeGroup( 100, QgsPoint( QgsWkbTypes::PointZ ) ); // UCS origin writeGroup( 101, QgsPoint( QgsWkbTypes::PointZ, 1.0 ) ); // UCS x axis writeGroup( 102, QgsPoint( QgsWkbTypes::PointZ, 0.0, 1.0 ) ); // UCS y axis - writeGroup( 79, 0 ); // Orthographic type of UCS (0 = UCS is not orthographic) - writeGroup( 146, 0.0 ); // Elevation + writeGroup( 79, 0 ); // Orthographic type of UCS (0 = UCS is not orthographic) + writeGroup( 146, 0.0 ); // Elevation writeGroup( 70, 0 ); writeGroup( 0, QStringLiteral( "ENDTAB" ) ); @@ -3457,7 +3458,7 @@ void QgsDxfExport::writePolyline( const QgsPointSequence &line, const QString &l return; } - if ( !line.at( 0 ).is3D() ) + if ( mForce2d || !line.at( 0 ).is3D() ) { writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) ); writeHandle(); @@ -3760,7 +3761,7 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT QgsGeos geos( tempGeom ); if ( tempGeom != geom.get() ) delete tempGeom; - tempGeom = geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok //#spellok + tempGeom = geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok if ( !tempGeom ) tempGeom = geom.get(); } @@ -3781,7 +3782,7 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT QgsGeos geos( tempGeom ); if ( tempGeom != geom.get() ) delete tempGeom; - tempGeom = geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok //#spellok + tempGeom = geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok if ( !tempGeom ) tempGeom = geom.get(); } @@ -3807,7 +3808,7 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT QgsGeos geos( tempGeom ); if ( tempGeom != geom.get() ) delete tempGeom; - tempGeom = geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok //#spellok + tempGeom = geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok if ( !tempGeom ) tempGeom = geom.get(); } @@ -3828,7 +3829,7 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT QgsGeos geos( tempGeom ); if ( tempGeom != geom.get() ) delete tempGeom; - tempGeom = geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok //#spellok + tempGeom = geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok if ( !tempGeom ) tempGeom = geom.get(); } diff --git a/src/core/dxf/qgsdxfexport.h b/src/core/dxf/qgsdxfexport.h index 2829c2ec60d..0f27dc78fc7 100644 --- a/src/core/dxf/qgsdxfexport.h +++ b/src/core/dxf/qgsdxfexport.h @@ -155,6 +155,20 @@ class CORE_EXPORT QgsDxfExport */ bool layerTitleAsName() { return mLayerTitleAsName; } + /** + * Force 2d output (eg. to support linewidth in polylines) + * \param force2d flag + * \see force2d + */ + void setForce2d( bool force2d ) { mForce2d = force2d; } + + /** + * Retrieve whether the output should be forced to 2d + * \returns flag + * \see setForce2d + */ + bool force2d() { return mForce2d; } + /** * Get DXF palette index of nearest entry for given color * \param color @@ -403,6 +417,7 @@ class CORE_EXPORT QgsDxfExport QgsMapSettings mMapSettings; QHash mLayerNameAttribute; double mFactor; + bool mForce2d; }; #endif // QGSDXFEXPORT_H diff --git a/src/ui/qgsdxfexportdialogbase.ui b/src/ui/qgsdxfexportdialogbase.ui index 3c04d5a92be..4e533d89a78 100644 --- a/src/ui/qgsdxfexportdialogbase.ui +++ b/src/ui/qgsdxfexportdialogbase.ui @@ -14,6 +14,13 @@ DXF Export + + + + Symbology scale + + + @@ -54,13 +61,6 @@ - - - - Symbology scale - - - @@ -125,7 +125,7 @@ - + Qt::Horizontal @@ -173,6 +173,13 @@ + + + + Force 2d output (eg. to support polyline width) + + + @@ -197,8 +204,8 @@ mFileLineEdit mFileSelectionButton mSymbologyModeComboBox - mScaleWidget mEncoding + mScaleWidget mVisibilityPresets mCrsSelector mTreeView @@ -206,6 +213,8 @@ mDeselectAllButton mLayerTitleAsName mMapExtentCheckBox + mForce2d + buttonBox From 44e7f3371e9d131e5c6799634f89451dbbaefadf Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 14:40:21 +1000 Subject: [PATCH 232/364] [FEATURE] New algorithm for creating vector layer from raster layer's extent Allows creation of a new vector layer with a single feature containing a raster layer's extent. Previously this was only possible for vector layers. --- python/plugins/processing/algs/help/qgis.yaml | 4 +- .../algs/qgis/ExtentFromRasterLayer.py | 127 ++++++++++++++++++ .../algs/qgis/QGISAlgorithmProvider.py | 2 + .../tests/testdata/expected/raster_extent.gfs | 66 +++++++++ .../tests/testdata/expected/raster_extent.gml | 29 ++++ .../tests/testdata/qgis_algorithm_tests.yaml | 11 ++ 6 files changed, 238 insertions(+), 1 deletion(-) create mode 100755 python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py create mode 100755 python/plugins/processing/tests/testdata/expected/raster_extent.gfs create mode 100755 python/plugins/processing/tests/testdata/expected/raster_extent.gml diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index 0eb07954e2b..e2afbfd3174 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -402,12 +402,14 @@ qgis:polygoncentroids: > NOTE: This algorithm is deprecated and the generic "centroids" algorithm (which works for line and multi geometry layers) should be used instead. - qgis:polygonfromlayerextent: > This algorithm takes a vector layer and generates a new one with the minimum bounding box (rectangle with N-S orientation) that covers all the input features. As an alternative, the output layer can contain not just a single bounding box, but one for each input feature, representing the bounding box of each of them. +qgis:polygonfromrasterextent: > + This algorithm takes a raster layer and generates a vector layer containing a feature with the minimum bounding box that covers the raster layer's extent. + qgis:polygonize: > This algorithm takes a lines layer and creates a polygon layer, with polygons generated from the lines in the input layer. diff --git a/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py b/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py new file mode 100755 index 00000000000..63efd366aef --- /dev/null +++ b/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + ExtentFromRasterLayer.py + --------------------- + Date : August 2017 + Copyright : (C) 2017 by Nyall Dawson + Email : nyall dot dawson at gmail dot com +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" + +__author__ = 'Nyall Dawson' +__date__ = 'August 2017' +__copyright__ = '(C) 2017, Nyall Dawson' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +import os + +from qgis.PyQt.QtGui import QIcon +from qgis.PyQt.QtCore import QVariant + +from qgis.core import (QgsField, + QgsFeatureSink, + QgsPointXY, + QgsGeometry, + QgsFeature, + QgsWkbTypes, + QgsProcessing, + QgsProcessingParameterRasterLayer, + QgsProcessingParameterFeatureSink, + QgsFields) + +from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm + +pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] + + +class ExtentFromRasterLayer(QgisAlgorithm): + + INPUT = 'INPUT' + OUTPUT = 'OUTPUT' + + def icon(self): + return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'layer_extent.png')) + + def tags(self): + return self.tr('extent,envelope,bounds,bounding,boundary,layer').split(',') + + def group(self): + return self.tr('Raster tools') + + def __init__(self): + super().__init__() + + def initAlgorithm(self, config=None): + self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent'), type=QgsProcessing.TypeVectorPolygon)) + + def name(self): + return 'polygonfromrasterextent' + + def displayName(self): + return self.tr('Polygon from raster extent') + + def processAlgorithm(self, parameters, context, feedback): + raster = self.parameterAsRasterLayer(parameters, self.INPUT, context) + + fields = QgsFields() + fields.append(QgsField('MINX', QVariant.Double)) + fields.append(QgsField('MINY', QVariant.Double)) + fields.append(QgsField('MAXX', QVariant.Double)) + fields.append(QgsField('MAXY', QVariant.Double)) + fields.append(QgsField('CNTX', QVariant.Double)) + fields.append(QgsField('CNTY', QVariant.Double)) + fields.append(QgsField('AREA', QVariant.Double)) + fields.append(QgsField('PERIM', QVariant.Double)) + fields.append(QgsField('HEIGHT', QVariant.Double)) + fields.append(QgsField('WIDTH', QVariant.Double)) + + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, QgsWkbTypes.Polygon, raster.crs()) + + self.layerExtent(raster, sink, feedback) + + return {self.OUTPUT: dest_id} + + def layerExtent(self, raster, sink, feedback): + rect = raster.extent() + geometry = QgsGeometry.fromRect(rect) + minx = rect.xMinimum() + miny = rect.yMinimum() + maxx = rect.xMaximum() + maxy = rect.yMaximum() + height = rect.height() + width = rect.width() + cntx = minx + width / 2.0 + cnty = miny + height / 2.0 + area = width * height + perim = 2 * width + 2 * height + + feat = QgsFeature() + feat.setGeometry(geometry) + attrs = [ + minx, + miny, + maxx, + maxy, + cntx, + cnty, + area, + perim, + height, + width, + ] + feat.setAttributes(attrs) + sink.addFeature(feat, QgsFeatureSink.FastInsert) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 8e062e8f664..9a8539cd641 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -70,6 +70,7 @@ from .Explode import Explode from .ExportGeometryInfo import ExportGeometryInfo from .ExtendLines import ExtendLines from .ExtentFromLayer import ExtentFromLayer +from .ExtentFromRasterLayer import ExtentFromRasterLayer from .ExtractNodes import ExtractNodes from .ExtractSpecificNodes import ExtractSpecificNodes from .FieldPyculator import FieldsPyculator @@ -222,6 +223,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ExportGeometryInfo(), ExtendLines(), ExtentFromLayer(), + ExtentFromRasterLayer(), ExtractNodes(), ExtractSpecificNodes(), FieldsCalculator(), diff --git a/python/plugins/processing/tests/testdata/expected/raster_extent.gfs b/python/plugins/processing/tests/testdata/expected/raster_extent.gfs new file mode 100755 index 00000000000..c8cd5ab33c1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/raster_extent.gfs @@ -0,0 +1,66 @@ + + + raster_extent + raster_extent + + 3 + EPSG:4326 + + 1 + 18.66630 + 18.70360 + 45.77670 + 45.81170 + + + MINX + MINX + Real + + + MINY + MINY + Real + + + MAXX + MAXX + Real + + + MAXY + MAXY + Real + + + CNTX + CNTX + Real + + + CNTY + CNTY + Real + + + AREA + AREA + Real + + + PERIM + PERIM + Real + + + HEIGHT + HEIGHT + Real + + + WIDTH + WIDTH + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/raster_extent.gml b/python/plugins/processing/tests/testdata/expected/raster_extent.gml new file mode 100755 index 00000000000..1b84c0ed0ff --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/raster_extent.gml @@ -0,0 +1,29 @@ + + + + + 18.666297944245.7767014376 + 18.703597944245.8117014376 + + + + + + 18.6662979442,45.7767014376 18.7035979442,45.7767014376 18.7035979442,45.8117014376 18.6662979442,45.8117014376 18.6662979442,45.7767014376 + 18.6662979442 + 45.7767014376 + 18.7035979442 + 45.8117014376 + 18.6849479442 + 45.7942014376 + 0.00130549999999981 + 0.14459999999999 + 0.0349999999999966 + 0.0372999999999983 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 39ebd6d0a99..e115d432c57 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3228,3 +3228,14 @@ tests: OUTPUT: name: expected/execute_sql.gml type: vector + + - algorithm: qgis:polygonfromrasterextent + name: Polygon from raster extent + params: + INPUT: + name: dem.tif + type: raster + results: + OUTPUT: + name: expected\raster_extent.gml + type: vector From 43e4ed0b27ca0b5d484aea872d18b232da40fc1b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 14:41:24 +1000 Subject: [PATCH 233/364] Rename Polygon from Layer Extent to Polygon from Vector Extent Helps clarify that this alg only functions for vector layers --- python/plugins/processing/algs/qgis/ExtentFromLayer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/algs/qgis/ExtentFromLayer.py b/python/plugins/processing/algs/qgis/ExtentFromLayer.py index d0bdd4cbd4d..12946fee64b 100644 --- a/python/plugins/processing/algs/qgis/ExtentFromLayer.py +++ b/python/plugins/processing/algs/qgis/ExtentFromLayer.py @@ -77,7 +77,7 @@ class ExtentFromLayer(QgisAlgorithm): return 'polygonfromlayerextent' def displayName(self): - return self.tr('Polygon from layer extent') + return self.tr('Polygon from vector extent') def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) From 8dde180f3fa25cc9d4140a080e36eb220d0be969 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 14:42:19 +1000 Subject: [PATCH 234/364] Rename parameter for consistency with other algs --- python/plugins/processing/algs/qgis/ExtentFromLayer.py | 6 +++--- .../processing/tests/testdata/qgis_algorithm_tests.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/plugins/processing/algs/qgis/ExtentFromLayer.py b/python/plugins/processing/algs/qgis/ExtentFromLayer.py index 12946fee64b..61a21426d53 100644 --- a/python/plugins/processing/algs/qgis/ExtentFromLayer.py +++ b/python/plugins/processing/algs/qgis/ExtentFromLayer.py @@ -49,7 +49,7 @@ pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] class ExtentFromLayer(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' BY_FEATURE = 'BY_FEATURE' OUTPUT = 'OUTPUT' @@ -67,7 +67,7 @@ class ExtentFromLayer(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER, self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE, self.tr('Calculate extent for each feature separately'), False)) @@ -80,7 +80,7 @@ class ExtentFromLayer(QgisAlgorithm): return self.tr('Polygon from vector extent') def processAlgorithm(self, parameters, context, feedback): - source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) + source = self.parameterAsSource(parameters, self.INPUT, context) byFeature = self.parameterAsBool(parameters, self.BY_FEATURE, context) fields = QgsFields() diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index e115d432c57..5bf8a1696c2 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2845,7 +2845,7 @@ tests: name: Standard polygon from layer extent params: BY_FEATURE: false - INPUT_LAYER: + INPUT: name: polys.gml type: vector results: From bb5a499c235673cb956e807d9031fab4155dfb53 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 15:25:11 +1000 Subject: [PATCH 235/364] Fix generation of processing tests on Windows --- python/plugins/processing/gui/TestTools.py | 4 +++- .../processing/tests/testdata/qgis_algorithm_tests.yaml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index 1d8bf7cdd10..ca54882ef47 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -27,6 +27,7 @@ __copyright__ = '(C) 2013, Victor Olaya' __revision__ = '$Format:%H$' import os +import posixpath import re import yaml import hashlib @@ -81,7 +82,8 @@ def extractSchemaPath(filepath): if part == 'testdata' and not localpath: localparts = parts localparts.reverse() - localpath = os.path.join(*localparts) + # we always want posix style paths here + localpath = posixpath.join(*localparts) parts.append(part) diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 5bf8a1696c2..570ba2d95a3 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3237,5 +3237,5 @@ tests: type: raster results: OUTPUT: - name: expected\raster_extent.gml + name: expected/raster_extent.gml type: vector From d0fea64b236759ed7c7e2952d507798721764109 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 17:44:52 +1000 Subject: [PATCH 236/364] Port GDAL info alg to new API --- .../algs/gdal/GdalAlgorithmProvider.py | 6 ++-- .../processing/algs/gdal/information.py | 36 ++++++++++--------- .../tests/testdata/gdal_algorithm_tests.yaml | 34 +++++++++--------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py index 28fa0e391c2..627c32a9e95 100644 --- a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py +++ b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py @@ -37,10 +37,10 @@ from .AssignProjection import AssignProjection from .aspect import aspect from .buildvrt import buildvrt from .ColorRelief import ColorRelief +from .information import information from .tri import tri from .warp import warp from .nearblack import nearblack -# from .information import information # from .rgb2pct import rgb2pct # from .translate import translate # from .pct2rgb import pct2rgb @@ -140,12 +140,12 @@ class GdalAlgorithmProvider(QgsProcessingProvider): def loadAlgorithms(self): self.algs = [ - nearblack(), - # information(), AssignProjection(), aspect(), buildvrt(), ColorRelief(), + information(), + nearblack(), tri(), warp(), # translate(), diff --git a/python/plugins/processing/algs/gdal/information.py b/python/plugins/processing/algs/gdal/information.py index e10d187dd93..cc31f603eac 100644 --- a/python/plugins/processing/algs/gdal/information.py +++ b/python/plugins/processing/algs/gdal/information.py @@ -29,10 +29,11 @@ __revision__ = '$Format:%H$' import os from qgis.PyQt.QtGui import QIcon - +from qgis.core import (QgsProcessingParameterRasterLayer, + QgsProcessingParameterBoolean, + QgsProcessingParameterFileDestination, + QgsProcessingOutputHtml) from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm -from processing.core.parameters import ParameterRaster -from processing.core.parameters import ParameterBoolean from processing.core.outputs import OutputHTML from processing.algs.gdal.GdalUtils import GdalUtils @@ -53,14 +54,15 @@ class information(GdalAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterRaster(information.INPUT, - self.tr('Input layer'), False)) - self.addParameter(ParameterBoolean(information.NOGCP, - self.tr('Suppress GCP info'), False)) - self.addParameter(ParameterBoolean(information.NOMETADATA, - self.tr('Suppress metadata info'), False)) - self.addOutput(OutputHTML(information.OUTPUT, - self.tr('Layer information'))) + self.addParameter(QgsProcessingParameterRasterLayer(information.INPUT, + self.tr('Input layer'), optional=False)) + self.addParameter(QgsProcessingParameterBoolean(information.NOGCP, + self.tr('Suppress GCP info'), defaultValue=False)) + self.addParameter(QgsProcessingParameterBoolean(information.NOMETADATA, + self.tr('Suppress metadata info'), defaultValue=False)) + + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Layer information'), self.tr('HTML files (*.html)'))) + self.addOutput(QgsProcessingOutputHtml(self.OUTPUT, self.tr('Layer information'))) def name(self): return 'gdalinfo' @@ -73,18 +75,20 @@ class information(GdalAlgorithm): def getConsoleCommands(self, parameters, context, feedback): arguments = [] - if self.getParameterValue(information.NOGCP): + if self.parameterAsBool(parameters, information.NOGCP, context): arguments.append('-nogcp') - if self.getParameterValue(information.NOMETADATA): + if self.parameterAsBool(parameters, information.NOMETADATA, context): arguments.append('-nomd') - arguments.append(self.getParameterValue(information.INPUT)) + arguments.append(self.parameterAsRasterLayer(parameters, information.INPUT, context).source()) return ['gdalinfo', GdalUtils.escapeAndJoin(arguments)] def processAlgorithm(self, parameters, context, feedback): - GdalUtils.runGdal(self.getConsoleCommands(parameters), feedback) - output = self.getOutputValue(information.OUTPUT) + GdalUtils.runGdal(self.getConsoleCommands(parameters, context, feedback), feedback) + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) with open(output, 'w') as f: f.write('
      ')
                   for s in GdalUtils.getConsoleOutput()[1:]:
                       f.write(str(s))
                   f.write('
      ') + + return {self.OUTPUT: output} diff --git a/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml index 456453e4802..eacf36f2115 100644 --- a/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml @@ -1,23 +1,23 @@ # See ../README.md for a description of the file format tests: -# - algorithm: gdal:gdalinfo -# name: gdalinfo -# params: -# INPUT: -# name: raster.tif -# type: raster -# NOGCP: false -# NOMETADATA: false -# results: -# OUTPUT: -# name: expected/gdal/raster_info.html -# type: regex -# rules: -# - 'Origin = \(270736.067325068172067,4459029.574521748349071\)' -# - 'Band 1 Block=16x14 Type=Float32, ColorInterp=Gray' -# - ' NoData Value=-32768' -# + - algorithm: gdal:gdalinfo + name: gdalinfo + params: + INPUT: + name: raster.tif + type: raster + NOGCP: false + NOMETADATA: false + results: + OUTPUT: + name: expected/gdal/raster_info.html + type: regex + rules: + - 'Origin = \(270736.067325068172067,4459029.574521748349071\)' + - 'Band 1 Block=16x14 Type=Float32, ColorInterp=Gray' + - ' NoData Value=-32768' + # - algorithm: gdal:ogrinfo # name: ogrinfo # params: From a29e3780cdfa4e1d1f738812dcf00b5491b230c9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 17:53:14 +1000 Subject: [PATCH 237/364] Port rgb2pct to new API --- .../algs/gdal/GdalAlgorithmProvider.py | 6 +++-- .../plugins/processing/algs/gdal/rgb2pct.py | 22 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py index 627c32a9e95..14ef9df584c 100644 --- a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py +++ b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py @@ -38,10 +38,11 @@ from .aspect import aspect from .buildvrt import buildvrt from .ColorRelief import ColorRelief from .information import information +from .rgb2pct import rgb2pct from .tri import tri from .warp import warp from .nearblack import nearblack -# from .rgb2pct import rgb2pct + # from .translate import translate # from .pct2rgb import pct2rgb # from .merge import merge @@ -146,10 +147,11 @@ class GdalAlgorithmProvider(QgsProcessingProvider): ColorRelief(), information(), nearblack(), + rgb2pct(), tri(), warp(), # translate(), - # rgb2pct(), + # # pct2rgb(), # merge(), # polygonize(), diff --git a/python/plugins/processing/algs/gdal/rgb2pct.py b/python/plugins/processing/algs/gdal/rgb2pct.py index f10a1080127..91af4f79f3b 100644 --- a/python/plugins/processing/algs/gdal/rgb2pct.py +++ b/python/plugins/processing/algs/gdal/rgb2pct.py @@ -31,10 +31,10 @@ import os from qgis.PyQt.QtGui import QIcon +from qgis.core import (QgsProcessingParameterRasterLayer, + QgsProcessingParameterNumber, + QgsProcessingParameterRasterDestination) from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm -from processing.core.parameters import ParameterRaster -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputRaster from processing.tools.system import isWindows from processing.algs.gdal.GdalUtils import GdalUtils @@ -57,11 +57,11 @@ class rgb2pct(GdalAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterRaster(rgb2pct.INPUT, - self.tr('Input layer'), False)) - self.addParameter(ParameterNumber(rgb2pct.NCOLORS, - self.tr('Number of colors'), 1, None, 2)) - self.addOutput(OutputRaster(rgb2pct.OUTPUT, self.tr('RGB to PCT'))) + self.addParameter(QgsProcessingParameterRasterLayer(rgb2pct.INPUT, + self.tr('Input layer'), optional=False)) + self.addParameter(QgsProcessingParameterNumber(rgb2pct.NCOLORS, + self.tr('Number of colors'), minValue=1, defaultValue=2)) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('RGB to PCT'))) def name(self): return 'rgbtopct' @@ -72,11 +72,11 @@ class rgb2pct(GdalAlgorithm): def getConsoleCommands(self, parameters, context, feedback): arguments = [] arguments.append('-n') - arguments.append(str(self.getParameterValue(rgb2pct.NCOLORS))) + arguments.append(str(self.parameterAsInt(parameters, rgb2pct.NCOLORS, context))) arguments.append('-of') - out = self.getOutputValue(rgb2pct.OUTPUT) + out = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) arguments.append(GdalUtils.getFormatShortNameFromFilename(out)) - arguments.append(self.getParameterValue(rgb2pct.INPUT)) + arguments.append(self.parameterAsRasterLayer(parameters, self.INPUT, context).source()) arguments.append(out) if isWindows(): From d46317802b6bebdb6fe5cf273d5b379a219b0359 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 18:13:51 +1000 Subject: [PATCH 238/364] Port gdal translate alg to new api --- .../algs/gdal/GdalAlgorithmProvider.py | 5 +- .../plugins/processing/algs/gdal/GdalUtils.py | 2 + .../plugins/processing/algs/gdal/translate.py | 110 +++++++++--------- 3 files changed, 60 insertions(+), 57 deletions(-) diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py index 14ef9df584c..a91f46da819 100644 --- a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py +++ b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py @@ -39,11 +39,11 @@ from .buildvrt import buildvrt from .ColorRelief import ColorRelief from .information import information from .rgb2pct import rgb2pct +from .translate import translate from .tri import tri from .warp import warp from .nearblack import nearblack -# from .translate import translate # from .pct2rgb import pct2rgb # from .merge import merge # from .polygonize import polygonize @@ -148,10 +148,9 @@ class GdalAlgorithmProvider(QgsProcessingProvider): information(), nearblack(), rgb2pct(), + translate(), tri(), warp(), - # translate(), - # # pct2rgb(), # merge(), # polygonize(), diff --git a/python/plugins/processing/algs/gdal/GdalUtils.py b/python/plugins/processing/algs/gdal/GdalUtils.py index f5e5923ca7a..35fb933e569 100755 --- a/python/plugins/processing/algs/gdal/GdalUtils.py +++ b/python/plugins/processing/algs/gdal/GdalUtils.py @@ -193,6 +193,8 @@ class GdalUtils(object): def escapeAndJoin(strList): joined = '' for s in strList: + if not isinstance(s, str): + s = str(s) if s and s[0] != '-' and ' ' in s: escaped = '"' + s.replace('\\', '\\\\').replace('"', '\\"') \ + '"' diff --git a/python/plugins/processing/algs/gdal/translate.py b/python/plugins/processing/algs/gdal/translate.py index d017d694694..ec6634edf11 100644 --- a/python/plugins/processing/algs/gdal/translate.py +++ b/python/plugins/processing/algs/gdal/translate.py @@ -30,15 +30,16 @@ import os from qgis.PyQt.QtGui import QIcon +from qgis.core import (QgsProcessingParameterRasterLayer, + QgsProcessingParameterNumber, + QgsProcessingParameterBoolean, + QgsProcessingParameterString, + QgsProcessingParameterEnum, + QgsProcessingParameterCrs, + QgsProcessingParameterExtent, + QgsProcessingParameterRasterDestination, + QgsProcessingUtils) from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm -from processing.core.parameters import (ParameterRaster, - ParameterString, - ParameterNumber, - ParameterBoolean, - ParameterSelection, - ParameterExtent, - ParameterCrs) -from processing.core.outputs import OutputRaster from processing.algs.gdal.GdalUtils import GdalUtils @@ -67,34 +68,35 @@ class translate(GdalAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterRaster(self.INPUT, self.tr('Input layer'))) - self.addParameter(ParameterNumber(self.OUTSIZE, - self.tr('Set the size of the output file (In pixels or %)'), - 1, None, 100)) - self.addParameter(ParameterBoolean(self.OUTSIZE_PERC, - self.tr('Output size is a percentage of input size'), True)) - self.addParameter(ParameterString(self.NO_DATA, - self.tr("Nodata value, leave blank to take the nodata value from input"), - '', optional=True)) - self.addParameter(ParameterSelection(self.EXPAND, - self.tr('Expand'), ['none', 'gray', 'rgb', 'rgba'], default=0)) - self.addParameter(ParameterCrs(self.SRS, - self.tr('Output projection for output file [leave blank to use input projection]'), None, optional=True)) - self.addParameter(ParameterExtent(self.PROJWIN, - self.tr('Subset based on georeferenced coordinates'), optional=True)) - self.addParameter(ParameterBoolean(self.SDS, - self.tr('Copy all subdatasets of this file to individual output files'), - False)) + self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterNumber(self.OUTSIZE, + self.tr('Set the size of the output file (In pixels or %)'), + minValue=1, defaultValue=100)) + self.addParameter(QgsProcessingParameterBoolean(self.OUTSIZE_PERC, + self.tr('Output size is a percentage of input size'), defaultValue=True)) + self.addParameter(QgsProcessingParameterString(self.NO_DATA, + self.tr("Nodata value, leave blank to take the nodata value from input"), + defaultValue='', optional=True)) + self.addParameter(QgsProcessingParameterEnum(self.EXPAND, + self.tr('Expand'), options=['none', 'gray', 'rgb', 'rgba'], defaultValue=0)) + self.addParameter(QgsProcessingParameterCrs(self.SRS, + self.tr('Output projection for output file [leave blank to use input projection]'), defaultValue=None, optional=True)) + self.addParameter(QgsProcessingParameterExtent(self.PROJWIN, + self.tr('Subset based on georeferenced coordinates'), optional=True)) + self.addParameter(QgsProcessingParameterBoolean(self.SDS, + self.tr('Copy all subdatasets of this file to individual output files'), + defaultValue=False)) - self.addParameter(ParameterString(self.OPTIONS, - self.tr('Additional creation options'), - optional=True, - metadata={'widget_wrapper': 'processing.algs.gdal.ui.RasterOptionsWidget.RasterOptionsWidgetWrapper'})) - self.addParameter(ParameterSelection(self.RTYPE, - self.tr('Output raster type'), - self.TYPE, 5)) + create_options_param = QgsProcessingParameterString(self.OPTIONS, + self.tr('Additional creation options'), + optional=True) + create_options_param.setMetadata({'widget_wrapper': 'processing.algs.gdal.ui.RasterOptionsWidget.RasterOptionsWidgetWrapper'}) + self.addParameter(create_options_param) + self.addParameter(QgsProcessingParameterEnum(self.RTYPE, + self.tr('Output raster type'), + options=self.TYPE, defaultValue=5)) - self.addOutput(OutputRaster(self.OUTPUT, self.tr('Converted'))) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Converted'))) def name(self): return 'translate' @@ -106,18 +108,19 @@ class translate(GdalAlgorithm): return self.tr('Raster conversion') def getConsoleCommands(self, parameters, context, feedback): - inLayer = self.getParameterValue(self.INPUT) - out = self.getOutputValue(translate.OUTPUT) - outsize = str(self.getParameterValue(self.OUTSIZE)) - outsizePerc = str(self.getParameterValue(self.OUTSIZE_PERC)) - noData = self.getParameterValue(self.NO_DATA) - expand = parameters[self.EXPAND].options[self.getParameterValue(self.EXPAND)][1] - projwin = str(self.getParameterValue(self.PROJWIN)) - if not projwin: - projwin = QgsProcessingUtils.combineLayerExtents([inLayer]) - crsId = self.getParameterValue(self.SRS) - sds = self.getParameterValue(self.SDS) - opts = self.getParameterValue(self.OPTIONS) + inLayer = self.parameterAsRasterLayer(parameters, self.INPUT, context) + out = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) + outsize = str(self.parameterAsInt(parameters, self.OUTSIZE, context)) + outsizePerc = self.parameterAsBool(parameters, self.OUTSIZE_PERC, context) + noData = self.parameterAsString(parameters, self.NO_DATA, context) + expand = self.parameterDefinition(self.EXPAND).options()[self.parameterAsEnum(parameters, self.EXPAND, context)] + + proj_extent = self.parameterAsExtent(parameters, self.PROJWIN, context) + if proj_extent.isNull(): + proj_extent = QgsProcessingUtils.combineLayerExtents([inLayer]) + crsId = self.parameterAsCrs(parameters, self.SRS, context).authid() + sds = self.parameterAsBool(parameters, self.SDS, context) + opts = self.parameterAsString(parameters, self.OPTIONS, context) if noData is not None: noData = str(noData) @@ -126,8 +129,8 @@ class translate(GdalAlgorithm): arguments.append('-of') arguments.append(GdalUtils.getFormatShortNameFromFilename(out)) arguments.append('-ot') - arguments.append(self.TYPE[self.getParameterValue(self.RTYPE)]) - if outsizePerc == 'True': + arguments.append(self.TYPE[self.parameterAsEnum(parameters, self.RTYPE, context)]) + if outsizePerc: arguments.append('-outsize') arguments.append(outsize + '%') arguments.append(outsize + '%') @@ -141,14 +144,13 @@ class translate(GdalAlgorithm): if expand != 'none': arguments.append('-expand') arguments.append(expand) - regionCoords = projwin.split(',') try: projwin = [] projwin.append('-projwin') - projwin.append(regionCoords[0]) - projwin.append(regionCoords[3]) - projwin.append(regionCoords[1]) - projwin.append(regionCoords[2]) + projwin.append(proj_extent.xMinimum()) + projwin.append(proj_extent.yMaximum()) + projwin.append(proj_extent.xMaximum()) + projwin.append(proj_extent.yMinimum()) except IndexError: projwin = [] if projwin: @@ -163,7 +165,7 @@ class translate(GdalAlgorithm): arguments.append('-co') arguments.append(opts) - arguments.append(self.getParameterValue(self.INPUT)) + arguments.append(inLayer.source()) arguments.append(out) return ['gdal_translate', GdalUtils.escapeAndJoin(arguments)] From 6fe6394fc7e666ea723da67e6db3622858fc16a7 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 30 Aug 2017 12:43:30 +0200 Subject: [PATCH 239/364] Make crssync quiet by default We only need it to show up in the build log if something has gone wrong. By default we will just happily assume that it's doing a great job in all conscience. --- src/core/qgsapplication.cpp | 6 +++--- src/core/qgscoordinatereferencesystem.cpp | 10 +++++----- src/crssync/main.cpp | 17 ++++++++++++++--- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 04c42400bf5..f0a2a6bfa80 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -167,9 +167,9 @@ void QgsApplication::init( QString profileFolder ) ABISYM( mRunningFromBuildDir ) = true; ABISYM( mBuildSourcePath ) = f.readLine().trimmed(); ABISYM( mBuildOutputPath ) = f.readLine().trimmed(); - qDebug( "Running from build directory!" ); - qDebug( "- source directory: %s", ABISYM( mBuildSourcePath ).toUtf8().data() ); - qDebug( "- output directory of the build: %s", ABISYM( mBuildOutputPath ).toUtf8().data() ); + QgsDebugMsgLevel( QStringLiteral( "Running from build directory!" ), 4 ); + QgsDebugMsgLevel( QStringLiteral( "- source directory: %1" ).arg( ABISYM( mBuildSourcePath ).toUtf8().data() ), 4 ); + QgsDebugMsgLevel( QStringLiteral( "- output directory of the build: %1" ).arg( ABISYM( mBuildOutputPath ).toUtf8().data() ), 4 ); #ifdef _MSC_VER ABISYM( mCfgIntDir ) = prefixPath.split( '/', QString::SkipEmptyParts ).last(); qDebug( "- cfg: %s", ABISYM( mCfgIntDir ).toUtf8().data() ); diff --git a/src/core/qgscoordinatereferencesystem.cpp b/src/core/qgscoordinatereferencesystem.cpp index c5da999aaee..89e25c51cff 100644 --- a/src/core/qgscoordinatereferencesystem.cpp +++ b/src/core/qgscoordinatereferencesystem.cpp @@ -1715,7 +1715,7 @@ QString QgsCoordinateReferenceSystem::quotedValue( QString value ) // adapted from gdal/ogr/ogr_srs_dict.cpp bool QgsCoordinateReferenceSystem::loadWkts( QHash &wkts, const char *filename ) { - qDebug( "Loading %s", filename ); + QgsDebugMsgLevel( QStringLiteral( "Loading %1" ).arg( filename ), 4 ); const char *pszFilename = CPLFindFile( "gdal", filename ); if ( !pszFilename ) return false; @@ -1826,7 +1826,7 @@ bool QgsCoordinateReferenceSystem::loadIds( QHash &wkts ) f.close(); - qDebug( "Loaded %d/%d from %s", n, l, filename.toUtf8().constData() ); + QgsDebugMsgLevel( QStringLiteral( "Loaded %1/%2 from %3" ).arg( QString::number( n ), QString::number( l ), filename.toUtf8().constData() ), 4 ); } OSRDestroySpatialReference( crs ); @@ -1841,7 +1841,7 @@ int QgsCoordinateReferenceSystem::syncDatabase() int inserted = 0, updated = 0, deleted = 0, errors = 0; - qDebug( "Load srs db from: %s", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData() ); + QgsDebugMsgLevel( QStringLiteral( "Load srs db from: %1" ).arg( QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData() ), 4 ); sqlite3 *database = nullptr; if ( sqlite3_open( dbFilePath.toUtf8().constData(), &database ) != SQLITE_OK ) @@ -1874,7 +1874,7 @@ int QgsCoordinateReferenceSystem::syncDatabase() loadIds( wkts ); loadWkts( wkts, "epsg.wkt" ); - qDebug( "%d WKTs loaded", wkts.count() ); + QgsDebugMsgLevel( QStringLiteral( "%1 WKTs loaded" ).arg( wkts.count() ), 4 ); for ( QHash::const_iterator it = wkts.constBegin(); it != wkts.constEnd(); ++it ) { @@ -2114,7 +2114,7 @@ int QgsCoordinateReferenceSystem::syncDatabase() sqlite3_close( database ); - qWarning( "CRS update (inserted:%d updated:%d deleted:%d errors:%d)", inserted, updated, deleted, errors ); + QgsDebugMsgLevel( QStringLiteral( "CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( QString::number( inserted ), QString::number( updated ), QString::number( deleted ), QString::number( errors ) ), 4 ); if ( errors > 0 ) return -errors; diff --git a/src/crssync/main.cpp b/src/crssync/main.cpp index 4bb915f2f38..99de960bef2 100644 --- a/src/crssync/main.cpp +++ b/src/crssync/main.cpp @@ -40,6 +40,16 @@ int main( int argc, char **argv ) { QCoreApplication app( argc, argv ); + const QStringList args = QCoreApplication::arguments(); + + bool verbose = false; + + for ( const QString &arg : args ) + { + if ( arg == QLatin1String( "--verbose" ) ) + verbose = true; + } + QgsApplication::init(); if ( !QgsApplication::isRunningFromBuildDir() ) @@ -48,7 +58,8 @@ int main( int argc, char **argv ) QgsApplication::setPrefixPath( prefixPath ? prefixPath : CMAKE_INSTALL_PREFIX, TRUE ); } - std::cout << "Synchronizing CRS database with GDAL/PROJ definitions." << std::endl; + if ( verbose ) + std::cout << "Synchronizing CRS database with GDAL/PROJ definitions." << std::endl; CPLPushErrorHandler( showError ); @@ -56,11 +67,11 @@ int main( int argc, char **argv ) CPLPopErrorHandler(); - if ( res == 0 ) + if ( res == 0 && verbose ) { std::cout << "No CRS updates were necessary." << std::endl; } - else if ( res > 0 ) + else if ( res > 0 && verbose ) { std::cout << res << " CRSs updated." << std::endl; } From 0346fb839a41dae1bf01317169e3693828434cf0 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 30 Aug 2017 17:41:19 +0200 Subject: [PATCH 240/364] [server] Rely on the provider to get the correct wkbType This commit removes a check for OGR provider that always returned gml:GeometryPropertyType in describefeaturetype. This is a partial fix for #17019 Needs backporting --- .../wfs/qgswfsdescribefeaturetype.cpp | 69 ++++++++----------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/src/server/services/wfs/qgswfsdescribefeaturetype.cpp b/src/server/services/wfs/qgswfsdescribefeaturetype.cpp index bbc1db54f9f..cafe0f217da 100644 --- a/src/server/services/wfs/qgswfsdescribefeaturetype.cpp +++ b/src/server/services/wfs/qgswfsdescribefeaturetype.cpp @@ -200,46 +200,37 @@ namespace QgsWfs { QDomElement geomElem = doc.createElement( QStringLiteral( "element" )/*xsd:element*/ ); geomElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "geometry" ) ); - if ( provider->name() == QLatin1String( "ogr" ) ) + + QgsWkbTypes::Type wkbType = layer->wkbType(); + switch ( wkbType ) { - // because some ogr drivers (e.g. ESRI ShapeFile, GML) - // are not able to determine the geometry type of a layer. - // we set to GeometryType - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:GeometryPropertyType" ) ); - } - else - { - QgsWkbTypes::Type wkbType = layer->wkbType(); - switch ( wkbType ) - { - case QgsWkbTypes::Point25D: - case QgsWkbTypes::Point: - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:PointPropertyType" ) ); - break; - case QgsWkbTypes::LineString25D: - case QgsWkbTypes::LineString: - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:LineStringPropertyType" ) ); - break; - case QgsWkbTypes::Polygon25D: - case QgsWkbTypes::Polygon: - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:PolygonPropertyType" ) ); - break; - case QgsWkbTypes::MultiPoint25D: - case QgsWkbTypes::MultiPoint: - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiPointPropertyType" ) ); - break; - case QgsWkbTypes::MultiLineString25D: - case QgsWkbTypes::MultiLineString: - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiLineStringPropertyType" ) ); - break; - case QgsWkbTypes::MultiPolygon25D: - case QgsWkbTypes::MultiPolygon: - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiPolygonPropertyType" ) ); - break; - default: - geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:GeometryPropertyType" ) ); - break; - } + case QgsWkbTypes::Point25D: + case QgsWkbTypes::Point: + geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:PointPropertyType" ) ); + break; + case QgsWkbTypes::LineString25D: + case QgsWkbTypes::LineString: + geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:LineStringPropertyType" ) ); + break; + case QgsWkbTypes::Polygon25D: + case QgsWkbTypes::Polygon: + geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:PolygonPropertyType" ) ); + break; + case QgsWkbTypes::MultiPoint25D: + case QgsWkbTypes::MultiPoint: + geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiPointPropertyType" ) ); + break; + case QgsWkbTypes::MultiLineString25D: + case QgsWkbTypes::MultiLineString: + geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiLineStringPropertyType" ) ); + break; + case QgsWkbTypes::MultiPolygon25D: + case QgsWkbTypes::MultiPolygon: + geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:MultiPolygonPropertyType" ) ); + break; + default: + geomElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "gml:GeometryPropertyType" ) ); + break; } geomElem.setAttribute( QStringLiteral( "minOccurs" ), QStringLiteral( "0" ) ); geomElem.setAttribute( QStringLiteral( "maxOccurs" ), QStringLiteral( "1" ) ); From d8607222e0f6b0a50b0ba676bd80cf49fcb191c2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 07:57:12 +1000 Subject: [PATCH 241/364] Expose GEOS Hausdorff distance calculations to QgsGeometry --- python/core/geometry/qgsgeometry.sip | 41 +++++++++++++++++++++++ src/core/geometry/qgsgeometry.cpp | 22 ++++++++++++ src/core/geometry/qgsgeometry.h | 39 ++++++++++++++++++++++ src/core/geometry/qgsgeos.cpp | 50 ++++++++++++++++++++++++++-- src/core/geometry/qgsgeos.h | 36 ++++++++++++++++++++ tests/src/python/test_qgsgeometry.py | 28 ++++++++++++++++ 6 files changed, 213 insertions(+), 3 deletions(-) diff --git a/python/core/geometry/qgsgeometry.sip b/python/core/geometry/qgsgeometry.sip index 1c18e2b1267..759cd0ca526 100644 --- a/python/core/geometry/qgsgeometry.sip +++ b/python/core/geometry/qgsgeometry.sip @@ -241,6 +241,47 @@ Returns true if WKB of the geometry is of WKBMulti* type :rtype: float %End + double hausdorffDistance( const QgsGeometry &geom ) const; +%Docstring + Returns the Hausdorff distance between this geometry and ``geom``. This is basically a measure of how similar or dissimilar 2 geometries are. + + This algorithm is an approximation to the standard Hausdorff distance. This approximation is exact or close enough for a large + subset of useful cases. Examples of these are: + + - computing distance between Linestrings that are roughly parallel to each other, + and roughly equal in length. This occurs in matching linear networks. + - Testing similarity of geometries. + + If the default approximate provided by this method is insufficient, use hausdorffDistanceDensify() instead. + + In case of error -1 will be returned. + +.. versionadded:: 3.0 +.. seealso:: hausdorffDistanceDensify() + :rtype: float +%End + + double hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const; +%Docstring + Returns the Hausdorff distance between this geometry and ``geom``. This is basically a measure of how similar or dissimilar 2 geometries are. + + This function accepts a ``densifyFraction`` argument. The function performs a segment + densification before computing the discrete Hausdorff distance. The ``densifyFraction`` parameter + sets the fraction by which to densify each segment. Each segment will be split into a + number of equal-length subsegments, whose fraction of the total length is + closest to the given fraction. + + This method can be used when the default approximation provided by hausdorffDistance() + is not sufficient. Decreasing the ``densifyFraction`` parameter will make the + distance returned approach the true Hausdorff distance for the geometries. + + In case of error -1 will be returned. + +.. versionadded:: 3.0 +.. seealso:: hausdorffDistance() + :rtype: float +%End + QgsPointXY closestVertex( const QgsPointXY &point, int &atVertex /Out/, int &beforeVertex /Out/, int &afterVertex /Out/, double &sqrDist /Out/ ) const; %Docstring :rtype: QgsPointXY diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 203d1ad68ed..2a9b4f323a0 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -1447,6 +1447,28 @@ double QgsGeometry::distance( const QgsGeometry &geom ) const return g.distance( geom.d->geometry ); } +double QgsGeometry::hausdorffDistance( const QgsGeometry &geom ) const +{ + if ( !d->geometry || !geom.d->geometry ) + { + return -1.0; + } + + QgsGeos g( d->geometry ); + return g.hausdorffDistance( geom.d->geometry ); +} + +double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const +{ + if ( !d->geometry || !geom.d->geometry ) + { + return -1.0; + } + + QgsGeos g( d->geometry ); + return g.hausdorffDistanceDensify( geom.d->geometry, densifyFraction ); +} + QgsGeometry QgsGeometry::buffer( double distance, int segments ) const { if ( !d->geometry ) diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index 30c433f544e..76f2fce8315 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -271,6 +271,45 @@ class CORE_EXPORT QgsGeometry */ double distance( const QgsGeometry &geom ) const; + /** + * Returns the Hausdorff distance between this geometry and \a geom. This is basically a measure of how similar or dissimilar 2 geometries are. + * + * This algorithm is an approximation to the standard Hausdorff distance. This approximation is exact or close enough for a large + * subset of useful cases. Examples of these are: + * + * - computing distance between Linestrings that are roughly parallel to each other, + * and roughly equal in length. This occurs in matching linear networks. + * - Testing similarity of geometries. + * + * If the default approximate provided by this method is insufficient, use hausdorffDistanceDensify() instead. + * + * In case of error -1 will be returned. + * + * \since QGIS 3.0 + * \see hausdorffDistanceDensify() + */ + double hausdorffDistance( const QgsGeometry &geom ) const; + + /** + * Returns the Hausdorff distance between this geometry and \a geom. This is basically a measure of how similar or dissimilar 2 geometries are. + * + * This function accepts a \a densifyFraction argument. The function performs a segment + * densification before computing the discrete Hausdorff distance. The \a densifyFraction parameter + * sets the fraction by which to densify each segment. Each segment will be split into a + * number of equal-length subsegments, whose fraction of the total length is + * closest to the given fraction. + * + * This method can be used when the default approximation provided by hausdorffDistance() + * is not sufficient. Decreasing the \a densifyFraction parameter will make the + * distance returned approach the true Hausdorff distance for the geometries. + * + * In case of error -1 will be returned. + * + * \since QGIS 3.0 + * \see hausdorffDistance() + */ + double hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const; + /** * Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap point / target point * and the indices of the vertices before and after the closest vertex. diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index b7f754d2d22..477ff4d2456 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -369,7 +369,7 @@ double QgsGeos::distance( const QgsAbstractGeometry *geom, QString *errorMsg ) c return distance; } - GEOSGeometry *otherGeosGeom = asGeos( geom, mPrecision ); + GEOSGeomScopedPtr otherGeosGeom( asGeos( geom, mPrecision ) ); if ( !otherGeosGeom ) { return distance; @@ -377,11 +377,55 @@ double QgsGeos::distance( const QgsAbstractGeometry *geom, QString *errorMsg ) c try { - GEOSDistance_r( geosinit.ctxt, mGeos, otherGeosGeom, &distance ); + GEOSDistance_r( geosinit.ctxt, mGeos, otherGeosGeom.get(), &distance ); } CATCH_GEOS_WITH_ERRMSG( -1.0 ) - GEOSGeom_destroy_r( geosinit.ctxt, otherGeosGeom ); + return distance; +} + +double QgsGeos::hausdorffDistance( const QgsAbstractGeometry *geom, QString *errorMsg ) const +{ + double distance = -1.0; + if ( !mGeos ) + { + return distance; + } + + GEOSGeomScopedPtr otherGeosGeom( asGeos( geom, mPrecision ) ); + if ( !otherGeosGeom ) + { + return distance; + } + + try + { + GEOSHausdorffDistance_r( geosinit.ctxt, mGeos, otherGeosGeom.get(), &distance ); + } + CATCH_GEOS_WITH_ERRMSG( -1.0 ) + + return distance; +} + +double QgsGeos::hausdorffDistanceDensify( const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg ) const +{ + double distance = -1.0; + if ( !mGeos ) + { + return distance; + } + + GEOSGeomScopedPtr otherGeosGeom( asGeos( geom, mPrecision ) ); + if ( !otherGeosGeom ) + { + return distance; + } + + try + { + GEOSHausdorffDistanceDensify_r( geosinit.ctxt, mGeos, otherGeosGeom.get(), densifyFraction, &distance ); + } + CATCH_GEOS_WITH_ERRMSG( -1.0 ) return distance; } diff --git a/src/core/geometry/qgsgeos.h b/src/core/geometry/qgsgeos.h index cf07861c02c..e20346368f0 100644 --- a/src/core/geometry/qgsgeos.h +++ b/src/core/geometry/qgsgeos.h @@ -84,6 +84,42 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine QgsPoint *pointOnSurface( QString *errorMsg = nullptr ) const override; QgsAbstractGeometry *convexHull( QString *errorMsg = nullptr ) const override; double distance( const QgsAbstractGeometry *geom, QString *errorMsg = nullptr ) const override; + + /** + * Returns the Hausdorff distance between this geometry and \a geom. This is basically a measure of how similar or dissimilar 2 geometries are. + * + * This algorithm is an approximation to the standard Hausdorff distance. This approximation is exact or close enough for a large + * subset of useful cases. Examples of these are: + * + * - computing distance between Linestrings that are roughly parallel to each other, + * and roughly equal in length. This occurs in matching linear networks. + * - Testing similarity of geometries. + * + * If the default approximate provided by this method is insufficient, use hausdorffDistanceDensify() instead. + * + * \since QGIS 3.0 + * \see hausdorffDistanceDensify() + */ + double hausdorffDistance( const QgsAbstractGeometry *geom, QString *errorMsg = nullptr ) const; + + /** + * Returns the Hausdorff distance between this geometry and \a geom. This is basically a measure of how similar or dissimilar 2 geometries are. + * + * This function accepts a \a densifyFraction argument. The function performs a segment + * densification before computing the discrete Hausdorff distance. The \a densifyFraction parameter + * sets the fraction by which to densify each segment. Each segment will be split into a + * number of equal-length subsegments, whose fraction of the total length is + * closest to the given fraction. + * + * This method can be used when the default approximation provided by hausdorffDistance() + * is not sufficient. Decreasing the \a densifyFraction parameter will make the + * distance returned approach the true Hausdorff distance for the geometries. + * + * \since QGIS 3.0 + * \see hausdorffDistance() + */ + double hausdorffDistanceDensify( const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg = nullptr ) const; + bool intersects( const QgsAbstractGeometry *geom, QString *errorMsg = nullptr ) const override; bool touches( const QgsAbstractGeometry *geom, QString *errorMsg = nullptr ) const override; bool crosses( const QgsAbstractGeometry *geom, QString *errorMsg = nullptr ) const override; diff --git a/tests/src/python/test_qgsgeometry.py b/tests/src/python/test_qgsgeometry.py index 8a2a214c711..ccaf8affc63 100644 --- a/tests/src/python/test_qgsgeometry.py +++ b/tests/src/python/test_qgsgeometry.py @@ -4223,6 +4223,34 @@ class TestQgsGeometry(unittest.TestCase): self.assertTrue(compareWkt(result, exp, 0.00001), "clipped: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result)) + def testHausdorff(self): + tests = [["POLYGON((0 0, 0 2, 1 2, 2 2, 2 0, 0 0))", "POLYGON((0.5 0.5, 0.5 2.5, 1.5 2.5, 2.5 2.5, 2.5 0.5, 0.5 0.5))", 0.707106781186548], + ["LINESTRING (0 0, 2 1)", "LINESTRING (0 0, 2 0)", 1], + ["LINESTRING (0 0, 2 0)", "LINESTRING (0 1, 1 2, 2 1)", 2], + ["LINESTRING (0 0, 2 0)", "MULTIPOINT (0 1, 1 0, 2 1)", 1], + ["LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 14.142135623730951] + ] + for t in tests: + g1 = QgsGeometry.fromWkt(t[0]) + g2 = QgsGeometry.fromWkt(t[1]) + o = g1.hausdorffDistance(g2) + exp = t[2] + self.assertAlmostEqual(o, exp, 5, + "mismatch for {} to {}, expected:\n{}\nGot:\n{}\n".format(t[0], t[1], exp, o)) + + def testHausdorffDensify(self): + tests = [ + ["LINESTRING (130 0, 0 0, 0 150)", "LINESTRING (10 10, 10 150, 130 10)", 0.5, 70.0] + ] + for t in tests: + g1 = QgsGeometry.fromWkt(t[0]) + g2 = QgsGeometry.fromWkt(t[1]) + densify = t[2] + o = g1.hausdorffDistanceDensify(g2, densify) + exp = t[3] + self.assertAlmostEqual(o, exp, 5, + "mismatch for {} to {}, expected:\n{}\nGot:\n{}\n".format(t[0], t[1], exp, o)) + if __name__ == '__main__': unittest.main() From 0a9e9944b4f28254d71ccfcaa6e364fd2ffe38fb Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 08:05:06 +1000 Subject: [PATCH 242/364] [FEATURE] Expression function for hausdorff distance Allows calculation of the Hausdorff distance between two geometries --- .../function_help/json/hausdorff_distance | 12 ++++++++++ src/core/expression/qgsexpressionfunction.cpp | 23 +++++++++++++++++++ tests/src/core/testqgsexpression.cpp | 5 ++++ 3 files changed, 40 insertions(+) create mode 100644 resources/function_help/json/hausdorff_distance diff --git a/resources/function_help/json/hausdorff_distance b/resources/function_help/json/hausdorff_distance new file mode 100644 index 00000000000..88b836958c7 --- /dev/null +++ b/resources/function_help/json/hausdorff_distance @@ -0,0 +1,12 @@ +{ + "name": "hausdorff_distance", + "type": "function", + "description": "Returns the Hausdorff distance between two geometries. This is basically a measure of how similar or dissimilar 2 geometries are, with a lower distance indicating more similar geometries.
      The function can be executed with an optional densify fraction argument. If not specified, an appoximation to the standard Hausdorff distance is used. This approximation is exact or close enough for a large subset of useful cases. Examples of these are:

    4. computing distance between Linestrings that are roughly parallel to each other, and roughly equal in length. This occurs in matching linear networks.
    5. Testing similarity of geometries.


    6. If the default approximate provided by this method is insufficient, specify the optional densify fraction argument. Specifying this argument performs a segment densification before computing the discrete Hausdorff distance. The parameter sets the fraction by which to densify each segment. Each segment will be split into a number of equal-length subsegments, whose fraction of the total length is closest to the given fraction. Decreasing the densify fraction parameter will make the distance returned approach the true Hausdorff distance for the geometries.", + "arguments": [ {"arg":"geometry a","description":"a geometry"}, + {"arg":"geometry b","description":"a geometry"}, + {"arg":"densify_fraction","description":"densify fraction amount", "optional":true}], + "examples": [ { "expression":"hausdorff_distance( geometry1:= geom_from_wkt('LINESTRING (0 0, 2 1)'),geometry2:=geom_from_wkt('LINESTRING (0 0, 2 0)'))", "returns":"2"}, + { "expression":"hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)'))", "returns":"14.142135623"}, + { "expression":"hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)'),0.5)", "returns":"70.0"} + ] +} diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 4138956cf30..ab4c851762c 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -2574,6 +2574,27 @@ static QVariant fcnDistance( const QVariantList &values, const QgsExpressionCont QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent ); return QVariant( fGeom.distance( sGeom ) ); } + +static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent ); + QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent ); + + double res = -1; + if ( values.length() == 3 && values.at( 2 ).isValid() ) + { + double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ); + densify = qBound( 0.0, densify, 1.0 ); + res = g1.hausdorffDistanceDensify( g2, densify ); + } + else + { + res = g1.hausdorffDistance( g2 ); + } + + return res > -1 ? QVariant( res ) : QVariant(); +} + static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent ); @@ -4113,6 +4134,8 @@ const QList &QgsExpression::Functions() << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), 1, fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "convexHull" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), 2, fcnDifference, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), 2, fcnDistance, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ), fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), 2, fcnIntersection, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), 2, fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "symDifference" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), 2, fcnCombine, QStringLiteral( "GeometryGroup" ) ) diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index bdc5f39ba2f..45936998e4a 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -872,6 +872,11 @@ class TestQgsExpression: public QObject QTest::newRow( "smooth point" ) << "geom_to_wkt(smooth(geom_from_wkt('POINT(1 2)'),10))" << false << QVariant( "Point (1 2)" ); QTest::newRow( "smooth line" ) << "geom_to_wkt(smooth(geometry:=geom_from_wkt('LineString(0 0, 5 0, 5 5)'),iterations:=1,offset:=0.2,min_length:=-1,max_angle:=180))" << false << QVariant( "LineString (0 0, 4 0, 5 1, 5 5)" ); QTest::newRow( "transform invalid" ) << "transform(make_point(500,500),'EPSG:4326','EPSG:28356')" << false << QVariant(); + QTest::newRow( "hausdorff line to line" ) << " hausdorff_distance( geometry1:= geom_from_wkt('LINESTRING (0 0, 2 1)'),geometry2:=geom_from_wkt('LINESTRING (0 0, 2 0)'))" << false << QVariant( 1.0 ); + QTest::newRow( "hausdorff line to line default" ) << " round(hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)')))" << false << QVariant( 14 ); + QTest::newRow( "hausdorff line to line densify" ) << " round(hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)'),0.5))" << false << QVariant( 70 ); + QTest::newRow( "hausdorff not geom 1" ) << " hausdorff_distance( 'a',geom_from_wkt('LINESTRING (0 0, 2 0)'))" << true << QVariant(); + QTest::newRow( "hausdorff not geom 2" ) << " hausdorff_distance( geom_from_wkt('LINESTRING (0 0, 2 0)'), 'b')" << true << QVariant(); // string functions QTest::newRow( "lower" ) << "lower('HeLLo')" << false << QVariant( "hello" ); From a73c099835503250ef0c6018cb2a2c8bd2e7207a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 30 Aug 2017 13:59:20 +1000 Subject: [PATCH 243/364] Fix evaluation of expression functions which use optional arguments with null default values --- src/core/expression/qgsexpressionfunction.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index ab4c851762c..f5beb949252 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -53,6 +53,7 @@ QVariant QgsExpressionFunction::run( QgsExpressionNode::NodeList *args, const Qg QVariantList argValues; if ( args ) { + int arg = 0; Q_FOREACH ( QgsExpressionNode *n, args->list() ) { QVariant v; @@ -65,10 +66,12 @@ QVariant QgsExpressionFunction::run( QgsExpressionNode::NodeList *args, const Qg { v = n->eval( parent, context ); ENSURE_NO_EVAL_ERROR; - if ( QgsExpressionUtils::isNull( v ) && !handlesNull() ) + bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid(); + if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() ) return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal) } argValues.append( v ); + arg++; } } From c2f8a8224b6b24332acaa65146f221342271066c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 31 Aug 2017 08:36:01 +1000 Subject: [PATCH 244/364] Geometry error string is not shared Instead move it to a private QgsGeometry member. It's copied with QgsGeometry, but not the underlying private geometry data. This means the error string can be changed without detaching (and copying) the geometry data. Also make greater use of error string for all use of GEOS functions. --- python/core/geometry/qgsgeometry.sip | 7 +- src/core/geometry/qgsgeometry.cpp | 224 ++++++++++++++++++--------- src/core/geometry/qgsgeometry.h | 10 +- 3 files changed, 163 insertions(+), 78 deletions(-) diff --git a/python/core/geometry/qgsgeometry.sip b/python/core/geometry/qgsgeometry.sip index 759cd0ca526..ce610614ffa 100644 --- a/python/core/geometry/qgsgeometry.sip +++ b/python/core/geometry/qgsgeometry.sip @@ -1292,10 +1292,11 @@ Returns an extruded version of this geometry. :rtype: int %End - QString error() const; + QString lastError() const; %Docstring - Returns an error string referring to an error that was produced - when this geometry was created. + Returns an error string referring to the last error encountered + either when this geometry was created or when an operation + was performed on the geometry. .. versionadded:: 3.0 :rtype: str diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 2a9b4f323a0..a4b48a2ce13 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -51,10 +51,10 @@ struct QgsGeometryPrivate ~QgsGeometryPrivate() { delete geometry; } QAtomicInt ref; QgsAbstractGeometry *geometry = nullptr; - QString error; }; -QgsGeometry::QgsGeometry(): d( new QgsGeometryPrivate() ) +QgsGeometry::QgsGeometry() + : d( new QgsGeometryPrivate() ) { } @@ -73,6 +73,7 @@ QgsGeometry::QgsGeometry( QgsAbstractGeometry *geom ): d( new QgsGeometryPrivate QgsGeometry::QgsGeometry( const QgsGeometry &other ) { d = other.d; + mLastError = other.mLastError; d->ref.ref(); } @@ -83,6 +84,7 @@ QgsGeometry &QgsGeometry::operator=( QgsGeometry const &other ) delete d; } + mLastError = other.mLastError; d = other.d; d->ref.ref(); return *this; @@ -571,13 +573,19 @@ double QgsGeometry::sqrDistToVertexAt( QgsPointXY &point, int atVertex ) const QgsGeometry QgsGeometry::nearestPoint( const QgsGeometry &other ) const { QgsGeos geos( d->geometry ); - return geos.closestPoint( other ); + mLastError.clear(); + QgsGeometry result = geos.closestPoint( other ); + result.mLastError = mLastError; + return result; } QgsGeometry QgsGeometry::shortestLine( const QgsGeometry &other ) const { QgsGeos geos( d->geometry ); - return geos.shortestLine( other ); + mLastError.clear(); + QgsGeometry result = geos.shortestLine( other, &mLastError ); + result.mLastError = mLastError; + return result; } double QgsGeometry::closestVertexWithContext( const QgsPointXY &point, int &atVertex ) const @@ -806,7 +814,8 @@ QgsGeometry::OperationResult QgsGeometry::splitGeometry( const QList QgsPointSequence tp; QgsGeos geos( d->geometry ); - QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, tp ); + mLastError.clear(); + QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, tp, &mLastError ); if ( result == QgsGeometryEngine::Success ) { @@ -855,7 +864,8 @@ QgsGeometry::OperationResult QgsGeometry::reshapeGeometry( const QgsLineString & QgsGeos geos( d->geometry ); QgsGeometryEngine::EngineOperationResult errorCode = QgsGeometryEngine::Success; - QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode ); + mLastError.clear(); + QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ); if ( errorCode == QgsGeometryEngine::Success && geom ) { detach( false ); @@ -895,7 +905,8 @@ int QgsGeometry::makeDifferenceInPlace( const QgsGeometry &other ) QgsGeos geos( d->geometry ); - QgsAbstractGeometry *diffGeom = geos.intersection( other.geometry() ); + mLastError.clear(); + QgsAbstractGeometry *diffGeom = geos.intersection( other.geometry(), &mLastError ); if ( !diffGeom ) { return 1; @@ -917,10 +928,13 @@ QgsGeometry QgsGeometry::makeDifference( const QgsGeometry &other ) const QgsGeos geos( d->geometry ); - QgsAbstractGeometry *diffGeom = geos.intersection( other.geometry() ); + mLastError.clear(); + QgsAbstractGeometry *diffGeom = geos.intersection( other.geometry(), &mLastError ); if ( !diffGeom ) { - return QgsGeometry(); + QgsGeometry result; + result.mLastError = mLastError; + return result; } return QgsGeometry( diffGeom ); @@ -1015,7 +1029,8 @@ bool QgsGeometry::intersects( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.intersects( geometry.d->geometry ); + mLastError.clear(); + return geos.intersects( geometry.d->geometry, &mLastError ); } bool QgsGeometry::contains( const QgsPointXY *p ) const @@ -1027,7 +1042,8 @@ bool QgsGeometry::contains( const QgsPointXY *p ) const QgsPoint pt( p->x(), p->y() ); QgsGeos geos( d->geometry ); - return geos.contains( &pt ); + mLastError.clear(); + return geos.contains( &pt, &mLastError ); } bool QgsGeometry::contains( const QgsGeometry &geometry ) const @@ -1038,7 +1054,8 @@ bool QgsGeometry::contains( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.contains( geometry.d->geometry ); + mLastError.clear(); + return geos.contains( geometry.d->geometry, &mLastError ); } bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const @@ -1049,7 +1066,8 @@ bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.disjoint( geometry.d->geometry ); + mLastError.clear(); + return geos.disjoint( geometry.d->geometry, &mLastError ); } bool QgsGeometry::equals( const QgsGeometry &geometry ) const @@ -1060,7 +1078,8 @@ bool QgsGeometry::equals( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.isEqual( geometry.d->geometry ); + mLastError.clear(); + return geos.isEqual( geometry.d->geometry, &mLastError ); } bool QgsGeometry::touches( const QgsGeometry &geometry ) const @@ -1071,7 +1090,8 @@ bool QgsGeometry::touches( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.touches( geometry.d->geometry ); + mLastError.clear(); + return geos.touches( geometry.d->geometry, &mLastError ); } bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const @@ -1082,7 +1102,8 @@ bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.overlaps( geometry.d->geometry ); + mLastError.clear(); + return geos.overlaps( geometry.d->geometry, &mLastError ); } bool QgsGeometry::within( const QgsGeometry &geometry ) const @@ -1093,7 +1114,8 @@ bool QgsGeometry::within( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.within( geometry.d->geometry ); + mLastError.clear(); + return geos.within( geometry.d->geometry, &mLastError ); } bool QgsGeometry::crosses( const QgsGeometry &geometry ) const @@ -1104,7 +1126,8 @@ bool QgsGeometry::crosses( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - return geos.crosses( geometry.d->geometry ); + mLastError.clear(); + return geos.crosses( geometry.d->geometry, &mLastError ); } QString QgsGeometry::exportToWkt( int precision ) const @@ -1423,7 +1446,8 @@ double QgsGeometry::area() const } #endif - return g.area(); + mLastError.clear(); + return g.area( &mLastError ); } double QgsGeometry::length() const @@ -1433,7 +1457,8 @@ double QgsGeometry::length() const return -1.0; } QgsGeos g( d->geometry ); - return g.length(); + mLastError.clear(); + return g.length( &mLastError ); } double QgsGeometry::distance( const QgsGeometry &geom ) const @@ -1444,7 +1469,8 @@ double QgsGeometry::distance( const QgsGeometry &geom ) const } QgsGeos g( d->geometry ); - return g.distance( geom.d->geometry ); + mLastError.clear(); + return g.distance( geom.d->geometry, &mLastError ); } double QgsGeometry::hausdorffDistance( const QgsGeometry &geom ) const @@ -1455,7 +1481,8 @@ double QgsGeometry::hausdorffDistance( const QgsGeometry &geom ) const } QgsGeos g( d->geometry ); - return g.hausdorffDistance( geom.d->geometry ); + mLastError.clear(); + return g.hausdorffDistance( geom.d->geometry, &mLastError ); } double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const @@ -1466,7 +1493,8 @@ double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double de } QgsGeos g( d->geometry ); - return g.hausdorffDistanceDensify( geom.d->geometry, densifyFraction ); + mLastError.clear(); + return g.hausdorffDistanceDensify( geom.d->geometry, densifyFraction, &mLastError ); } QgsGeometry QgsGeometry::buffer( double distance, int segments ) const @@ -1477,7 +1505,14 @@ QgsGeometry QgsGeometry::buffer( double distance, int segments ) const } QgsGeos g( d->geometry ); - std::unique_ptr geom( g.buffer( distance, segments ) ); + mLastError.clear(); + std::unique_ptr geom( g.buffer( distance, segments, &mLastError ) ); + if ( !geom ) + { + QgsGeometry result; + result.mLastError = mLastError; + return result; + } return QgsGeometry( geom.release() ); } @@ -1489,10 +1524,13 @@ QgsGeometry QgsGeometry::buffer( double distance, int segments, EndCapStyle endC } QgsGeos g( d->geometry ); - QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit ); + mLastError.clear(); + QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError ); if ( !geom ) { - return QgsGeometry(); + QgsGeometry result; + result.mLastError = mLastError; + return result; } return QgsGeometry( geom ); } @@ -1527,10 +1565,13 @@ QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, JoinStyle j else { QgsGeos geos( d->geometry ); - QgsAbstractGeometry *offsetGeom = geos.offsetCurve( distance, segments, joinStyle, miterLimit ); + mLastError.clear(); + QgsAbstractGeometry *offsetGeom = geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ); if ( !offsetGeom ) { - return QgsGeometry(); + QgsGeometry result; + result.mLastError = mLastError; + return result; } return QgsGeometry( offsetGeom ); } @@ -1566,11 +1607,14 @@ QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Buffe else { QgsGeos geos( d->geometry ); + mLastError.clear(); QgsAbstractGeometry *bufferGeom = geos.singleSidedBuffer( distance, segments, side, - joinStyle, miterLimit ); + joinStyle, miterLimit, &mLastError ); if ( !bufferGeom ) { - return QgsGeometry(); + QgsGeometry result; + result.mLastError = mLastError; + return result; } return QgsGeometry( bufferGeom ); } @@ -1623,10 +1667,13 @@ QgsGeometry QgsGeometry::simplify( double tolerance ) const } QgsGeos geos( d->geometry ); - QgsAbstractGeometry *simplifiedGeom = geos.simplify( tolerance ); + mLastError.clear(); + QgsAbstractGeometry *simplifiedGeom = geos.simplify( tolerance, &mLastError ); if ( !simplifiedGeom ) { - return QgsGeometry(); + QgsGeometry result; + result.mLastError = mLastError; + return result; } return QgsGeometry( simplifiedGeom ); } @@ -1654,7 +1701,10 @@ QgsGeometry QgsGeometry::centroid() const QgsGeos geos( d->geometry ); - return QgsGeometry( geos.centroid( &d->error ) ); + mLastError.clear(); + QgsGeometry result( geos.centroid( &mLastError ) ); + result.mLastError = mLastError; + return result; } QgsGeometry QgsGeometry::pointOnSurface() const @@ -1666,7 +1716,10 @@ QgsGeometry QgsGeometry::pointOnSurface() const QgsGeos geos( d->geometry ); - return QgsGeometry( geos.pointOnSurface( &d->error ) ); + mLastError.clear(); + QgsGeometry result( geos.pointOnSurface( &mLastError ) ); + result.mLastError = mLastError; + return result; } QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const @@ -1683,12 +1736,12 @@ QgsGeometry QgsGeometry::convexHull() const return QgsGeometry(); } QgsGeos geos( d->geometry ); - QString error; - QgsAbstractGeometry *cHull = geos.convexHull( &error ); + mLastError.clear(); + QgsAbstractGeometry *cHull = geos.convexHull( &mLastError ); if ( !cHull ) { QgsGeometry geom; - geom.d->error = error; + geom.mLastError = mLastError; return geom; } return QgsGeometry( cHull ); @@ -1702,7 +1755,10 @@ QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double toler } QgsGeos geos( d->geometry ); - return geos.voronoiDiagram( extent.geometry(), tolerance, edgesOnly ); + mLastError.clear(); + QgsGeometry result = geos.voronoiDiagram( extent.geometry(), tolerance, edgesOnly, &mLastError ); + result.mLastError = mLastError; + return result; } QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const @@ -1713,7 +1769,10 @@ QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly } QgsGeos geos( d->geometry ); - return geos.delaunayTriangulation( tolerance, edgesOnly ); + mLastError.clear(); + QgsGeometry result = geos.delaunayTriangulation( tolerance, edgesOnly ); + result.mLastError = mLastError; + return result; } QgsGeometry QgsGeometry::subdivide( int maxNodes ) const @@ -1731,13 +1790,13 @@ QgsGeometry QgsGeometry::subdivide( int maxNodes ) const geom = segmentizedCopy.get(); } - QString error; QgsGeos geos( geom ); - QgsAbstractGeometry *result = geos.subdivide( maxNodes, &error ); + mLastError.clear(); + QgsAbstractGeometry *result = geos.subdivide( maxNodes, &mLastError ); if ( !result ) { QgsGeometry geom; - geom.d->error = error; + geom.mLastError = mLastError; return geom; } return QgsGeometry( result ); @@ -1755,12 +1814,12 @@ QgsGeometry QgsGeometry::interpolate( double distance ) const line = QgsGeometry( d->geometry->boundary() ); QgsGeos geos( line.geometry() ); - QString error; - QgsAbstractGeometry *result = geos.interpolate( distance, &error ); + mLastError.clear(); + QgsAbstractGeometry *result = geos.interpolate( distance, &mLastError ); if ( !result ) { QgsGeometry geom; - geom.d->error = error; + geom.mLastError = mLastError; return geom; } return QgsGeometry( result ); @@ -1781,7 +1840,8 @@ double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const } QgsGeos geos( d->geometry ); - return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry ) ) ); + mLastError.clear(); + return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry ) ), &mLastError ); } double QgsGeometry::interpolateAngle( double distance ) const @@ -1847,13 +1907,13 @@ QgsGeometry QgsGeometry::intersection( const QgsGeometry &geometry ) const QgsGeos geos( d->geometry ); - QString error; - QgsAbstractGeometry *resultGeom = geos.intersection( geometry.d->geometry, &error ); + mLastError.clear(); + QgsAbstractGeometry *resultGeom = geos.intersection( geometry.d->geometry, &mLastError ); if ( !resultGeom ) { QgsGeometry geom; - geom.d->error = error; + geom.mLastError = mLastError; return geom; } @@ -1868,13 +1928,12 @@ QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry ) const } QgsGeos geos( d->geometry ); - QString error; - - QgsAbstractGeometry *resultGeom = geos.combine( geometry.d->geometry, &error ); + mLastError.clear(); + QgsAbstractGeometry *resultGeom = geos.combine( geometry.d->geometry, &mLastError ); if ( !resultGeom ) { QgsGeometry geom; - geom.d->error = error; + geom.mLastError = mLastError; return geom; } return QgsGeometry( resultGeom ); @@ -1894,7 +1953,10 @@ QgsGeometry QgsGeometry::mergeLines() const } QgsGeos geos( d->geometry ); - return geos.mergeLines(); + mLastError.clear(); + QgsGeometry result = geos.mergeLines( &mLastError ); + result.mLastError = mLastError; + return result; } QgsGeometry QgsGeometry::difference( const QgsGeometry &geometry ) const @@ -1906,12 +1968,12 @@ QgsGeometry QgsGeometry::difference( const QgsGeometry &geometry ) const QgsGeos geos( d->geometry ); - QString error; - QgsAbstractGeometry *resultGeom = geos.difference( geometry.d->geometry, &error ); + mLastError.clear(); + QgsAbstractGeometry *resultGeom = geos.difference( geometry.d->geometry, &mLastError ); if ( !resultGeom ) { QgsGeometry geom; - geom.d->error = error; + geom.mLastError = mLastError; return geom; } return QgsGeometry( resultGeom ); @@ -1926,13 +1988,12 @@ QgsGeometry QgsGeometry::symDifference( const QgsGeometry &geometry ) const QgsGeos geos( d->geometry ); - QString error; - - QgsAbstractGeometry *resultGeom = geos.symDifference( geometry.d->geometry, &error ); + mLastError.clear(); + QgsAbstractGeometry *resultGeom = geos.symDifference( geometry.d->geometry, &mLastError ); if ( !resultGeom ) { QgsGeometry geom; - geom.d->error = error; + geom.mLastError = mLastError; return geom; } return QgsGeometry( resultGeom ); @@ -2063,9 +2124,12 @@ QgsGeometry QgsGeometry::makeValid() if ( !d->geometry ) return QgsGeometry(); - QgsAbstractGeometry *g = _qgis_lwgeom_make_valid( d->geometry, d->error ); + mLastError.clear(); + QgsAbstractGeometry *g = _qgis_lwgeom_make_valid( d->geometry, mLastError ); - return QgsGeometry( g ); + QgsGeometry result = QgsGeometry( g ); + result.mLastError = mLastError; + return result; } @@ -2082,7 +2146,8 @@ bool QgsGeometry::isGeosValid() const } QgsGeos geos( d->geometry ); - return geos.isValid(); + mLastError.clear(); + return geos.isValid( &mLastError ); } bool QgsGeometry::isSimple() const @@ -2091,7 +2156,8 @@ bool QgsGeometry::isSimple() const return false; QgsGeos geos( d->geometry ); - return geos.isSimple(); + mLastError.clear(); + return geos.isSimple( &mLastError ); } bool QgsGeometry::isGeosEqual( const QgsGeometry &g ) const @@ -2102,7 +2168,8 @@ bool QgsGeometry::isGeosEqual( const QgsGeometry &g ) const } QgsGeos geos( d->geometry ); - return geos.isEqual( g.d->geometry ); + mLastError.clear(); + return geos.isEqual( g.d->geometry, &mLastError ); } QgsGeometry QgsGeometry::unaryUnion( const QList &geometries ) @@ -2119,8 +2186,11 @@ QgsGeometry QgsGeometry::unaryUnion( const QList &geometries ) } } - QgsAbstractGeometry *geom = geos.combine( geomV2List ); - return QgsGeometry( geom ); + QString error; + QgsAbstractGeometry *geom = geos.combine( geomV2List, &error ); + QgsGeometry result( geom ); + result.mLastError = error; + return result; } QgsGeometry QgsGeometry::polygonize( const QList &geometryList ) @@ -2137,7 +2207,10 @@ QgsGeometry QgsGeometry::polygonize( const QList &geometryList ) } } - return geos.polygonize( geomV2List ); + QString error; + QgsGeometry result = geos.polygonize( geomV2List, &error ); + result.mLastError = error; + return result; } void QgsGeometry::convertToStraightSegment() @@ -2204,7 +2277,14 @@ QgsGeometry QgsGeometry::clipped( const QgsRectangle &rectangle ) } QgsGeos geos( d->geometry ); - QgsAbstractGeometry *resultGeom = geos.clip( rectangle ); + mLastError.clear(); + QgsAbstractGeometry *resultGeom = geos.clip( rectangle, &mLastError ); + if ( !resultGeom ) + { + QgsGeometry result; + result.mLastError = mLastError; + return result; + } return QgsGeometry( resultGeom ); } @@ -2358,9 +2438,9 @@ int QgsGeometry::vertexNrFromVertexId( QgsVertexId id ) const return -1; } -QString QgsGeometry::error() const +QString QgsGeometry::lastError() const { - return d->error; + return mLastError; } void QgsGeometry::convertPointList( const QList &input, QgsPointSequence &output ) diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index 76f2fce8315..0103cbc14ca 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -1230,12 +1230,13 @@ class CORE_EXPORT QgsGeometry int vertexNrFromVertexId( QgsVertexId i ) const; /** - * Returns an error string referring to an error that was produced - * when this geometry was created. + * Returns an error string referring to the last error encountered + * either when this geometry was created or when an operation + * was performed on the geometry. * * \since QGIS 3.0 */ - QString error() const; + QString lastError() const; /** * Return GEOS context handle @@ -1483,6 +1484,9 @@ class CORE_EXPORT QgsGeometry QgsGeometryPrivate *d; //implicitly shared data pointer + //! Last error encountered + mutable QString mLastError; + void detach( bool cloneGeom = true ); //make sure mGeometry only referenced from this instance static void convertToPolyline( const QgsPointSequence &input, QgsPolyline &output ); From 4810c73e4d2cf8b4f42ca9d7ae4b76b1658519f3 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 31 Aug 2017 15:14:47 +0200 Subject: [PATCH 245/364] Keep XYZ connections synchronized in browser(s) and data source manager dialog --- src/providers/wms/qgswmsdataitems.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/providers/wms/qgswmsdataitems.cpp b/src/providers/wms/qgswmsdataitems.cpp index 2c24e3cd5f8..61269148542 100644 --- a/src/providers/wms/qgswmsdataitems.cpp +++ b/src/providers/wms/qgswmsdataitems.cpp @@ -496,8 +496,7 @@ void QgsXyzTileRootItem::newConnection() return; QgsXyzConnectionUtils::addConnection( dlg.connection() ); - - refresh(); + refreshConnections(); } #endif @@ -537,13 +536,13 @@ void QgsXyzLayerItem::editConnection() QgsXyzConnectionUtils::deleteConnection( mName ); QgsXyzConnectionUtils::addConnection( dlg.connection() ); - mParent->refresh(); + mParent->refreshConnections(); } void QgsXyzLayerItem::deleteConnection() { QgsXyzConnectionUtils::deleteConnection( mName ); - mParent->refresh(); + mParent->refreshConnections(); } #endif From b6d86753e27afca3e2caa870764fa5bf1c1271b4 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 31 Aug 2017 18:01:18 +0200 Subject: [PATCH 246/364] Fix broken qgscolorramp button For a misterious reason ( the docs are not completely clear ) calling text() on the ramp QAction returned all string prefixed with '&' causing the whole button to miss the changed ramp. By calling iconText() instead we get the right name for the ramp. --- src/gui/qgscolorrampbutton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/qgscolorrampbutton.cpp b/src/gui/qgscolorrampbutton.cpp index ca789d462e1..c9b89be95c8 100644 --- a/src/gui/qgscolorrampbutton.cpp +++ b/src/gui/qgscolorrampbutton.cpp @@ -337,7 +337,7 @@ void QgsColorRampButton::loadColorRamp() QAction *selectedItem = qobject_cast( sender() ); if ( selectedItem ) { - QString name = selectedItem->text(); + QString name = selectedItem->iconText(); setColorRampName( name ); setColorRampFromName( name ); } From 9a07cd513a69a6ff5da66e53a405623eab8c467c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 07:50:53 +1000 Subject: [PATCH 247/364] Add guard against nullptr dereference (Coverity warning) --- src/core/qgsmaptopixelgeometrysimplifier.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/qgsmaptopixelgeometrysimplifier.cpp b/src/core/qgsmaptopixelgeometrysimplifier.cpp index 6ea505a442c..ba9520d3882 100644 --- a/src/core/qgsmaptopixelgeometrysimplifier.cpp +++ b/src/core/qgsmaptopixelgeometrysimplifier.cpp @@ -107,7 +107,9 @@ static QgsGeometry generalizeWkbGeometryByBoundingBox( template static T *createEmptySameTypeGeom( const T &geom ) { + // TODO - this is inefficient - we clone the geometry's content only to throw it away immediately T *output( qgsgeometry_cast( geom.clone() ) ); + Q_ASSERT( output ); output->clear(); return output; } From e7ddfff09d9da3d4a05af1f8cd9c7badce2ba435 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 07:54:20 +1000 Subject: [PATCH 248/364] Fix some Coverity null pointer dereference warnings --- src/gui/qgsattributeform.cpp | 2 +- src/providers/ogr/qgsgeopackagedataitems.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index 11cbc283c32..14d23500310 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -2037,7 +2037,7 @@ bool QgsAttributeForm::fieldIsEditable( int fieldIndex ) const int srcFieldIndex; const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( fieldIndex, mLayer->fields(), srcFieldIndex ); - if ( !info->hasUpsertOnEdit() && mMode == QgsAttributeForm::AddFeatureMode ) + if ( info && !info->hasUpsertOnEdit() && mMode == QgsAttributeForm::AddFeatureMode ) editable = false; else if ( info && info->isEditable() && info->joinLayer()->isEditable() ) editable = fieldIsEditable( *( info->joinLayer() ), srcFieldIndex, mFeature.id() ); diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 38fc088745d..72e71aa8500 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -383,7 +383,7 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct { if ( isVector ) // Import vectors and aspatial { - QgsVectorLayer *vectorSrcLayer = dynamic_cast < QgsVectorLayer * >( srcLayer ); + QgsVectorLayer *vectorSrcLayer = qobject_cast < QgsVectorLayer * >( srcLayer ); QVariantMap options; options.insert( QStringLiteral( "driverName" ), QStringLiteral( "GPKG" ) ); options.insert( QStringLiteral( "update" ), true ); From ed5e4b69c720e901d5531cdf1de7f39814a20970 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 07:56:22 +1000 Subject: [PATCH 249/364] Switch some dynamic_casts to qobject_casts --- src/app/qgsdxfexportdialog.cpp | 8 ++++---- src/app/qgsstatisticalsummarydockwidget.cpp | 2 +- src/core/expression/qgsexpressionfunction.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/qgsdxfexportdialog.cpp b/src/app/qgsdxfexportdialog.cpp index 37fe1fd77e0..350375b38de 100644 --- a/src/app/qgsdxfexportdialog.cpp +++ b/src/app/qgsdxfexportdialog.cpp @@ -79,7 +79,7 @@ void FieldSelectorDelegate::setEditorData( QWidget *editor, const QModelIndex &i void FieldSelectorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const { - QgsVectorLayerAndAttributeModel *m = dynamic_cast< QgsVectorLayerAndAttributeModel *>( model ); + QgsVectorLayerAndAttributeModel *m = qobject_cast< QgsVectorLayerAndAttributeModel *>( model ); if ( !m ) return; @@ -492,7 +492,7 @@ QgsDxfExportDialog::~QgsDxfExportDialog() void QgsDxfExportDialog::on_mVisibilityPresets_currentIndexChanged( int index ) { Q_UNUSED( index ); - QgsVectorLayerAndAttributeModel *model = dynamic_cast< QgsVectorLayerAndAttributeModel * >( mTreeView->model() ); + QgsVectorLayerAndAttributeModel *model = qobject_cast< QgsVectorLayerAndAttributeModel * >( mTreeView->model() ); Q_ASSERT( model ); model->applyVisibilityPreset( mVisibilityPresets->currentText() ); } @@ -525,14 +525,14 @@ void QgsDxfExportDialog::cleanGroup( QgsLayerTreeNode *node ) void QgsDxfExportDialog::selectAll() { - QgsVectorLayerAndAttributeModel *model = dynamic_cast< QgsVectorLayerAndAttributeModel *>( mTreeView->model() ); + QgsVectorLayerAndAttributeModel *model = qobject_cast< QgsVectorLayerAndAttributeModel *>( mTreeView->model() ); Q_ASSERT( model ); model->selectAll(); } void QgsDxfExportDialog::deSelectAll() { - QgsVectorLayerAndAttributeModel *model = dynamic_cast< QgsVectorLayerAndAttributeModel *>( mTreeView->model() ); + QgsVectorLayerAndAttributeModel *model = qobject_cast< QgsVectorLayerAndAttributeModel *>( mTreeView->model() ); Q_ASSERT( model ); model->deSelectAll(); } diff --git a/src/app/qgsstatisticalsummarydockwidget.cpp b/src/app/qgsstatisticalsummarydockwidget.cpp index f74c31ab45c..7d161eb648f 100644 --- a/src/app/qgsstatisticalsummarydockwidget.cpp +++ b/src/app/qgsstatisticalsummarydockwidget.cpp @@ -241,7 +241,7 @@ void QgsStatisticalSummaryDockWidget::updateStringStatistics( bool selectedOnly void QgsStatisticalSummaryDockWidget::layerChanged( QgsMapLayer *layer ) { - QgsVectorLayer *newLayer = dynamic_cast< QgsVectorLayer * >( layer ); + QgsVectorLayer *newLayer = qobject_cast< QgsVectorLayer * >( layer ); if ( mLayer && mLayer != newLayer ) { disconnect( mLayer, &QgsVectorLayer::selectionChanged, this, &QgsStatisticalSummaryDockWidget::layerSelectionChanged ); diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index f5beb949252..ed7d148975d 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -3455,7 +3455,7 @@ static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpres else { //vector layer methods - QgsVectorLayer *vLayer = dynamic_cast< QgsVectorLayer * >( layer ); + QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer ); if ( vLayer ) { if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 ) From 6bddc3827637cd06a3ec397834a832939717d534 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 07:57:16 +1000 Subject: [PATCH 250/364] Fix possible divide by 0 (Thanks to Coverity) --- src/analysis/interpolation/qgstininterpolator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analysis/interpolation/qgstininterpolator.cpp b/src/analysis/interpolation/qgstininterpolator.cpp index 119a8e3db62..4a1bfcf513d 100644 --- a/src/analysis/interpolation/qgstininterpolator.cpp +++ b/src/analysis/interpolation/qgstininterpolator.cpp @@ -125,7 +125,8 @@ void QgsTINInterpolator::initialize() { break; } - mFeedback->setProgress( 100.0 * static_cast< double >( nProcessedFeatures ) / nFeatures ); + if ( nFeatures > 0 ) + mFeedback->setProgress( 100.0 * static_cast< double >( nProcessedFeatures ) / nFeatures ); } insertData( &f, layer.zCoordInterpolation, layer.interpolationAttribute, layer.mInputType ); ++nProcessedFeatures; From e68835f12e618826f0341ec0454d16c53e5a97eb Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:00:59 +1000 Subject: [PATCH 251/364] Fix some possible leaks in tests --- tests/src/core/testqgsgeometry.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 18c5972d3ed..5a8ef17b3e8 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -4592,8 +4592,8 @@ void TestQgsGeometry::multiPolygon() polygon1.setExteriorRing( ring1.clone() ); multiPolygon1.addGeometry( polygon1.clone() ); - QgsAbstractGeometry *boundary = multiPolygon1.boundary(); - QgsMultiLineString *lineBoundary = dynamic_cast< QgsMultiLineString * >( boundary ); + std::unique_ptr< QgsAbstractGeometry > boundary( multiPolygon1.boundary() ); + QgsMultiLineString *lineBoundary = dynamic_cast< QgsMultiLineString * >( boundary.get() ); QVERIFY( lineBoundary ); QCOMPARE( lineBoundary->numGeometries(), 1 ); QCOMPARE( dynamic_cast< QgsLineString * >( lineBoundary->geometryN( 0 ) )->numPoints(), 4 ); @@ -4605,7 +4605,6 @@ void TestQgsGeometry::multiPolygon() QCOMPARE( dynamic_cast< QgsLineString * >( lineBoundary->geometryN( 0 ) )->yAt( 1 ), 0.0 ); QCOMPARE( dynamic_cast< QgsLineString * >( lineBoundary->geometryN( 0 ) )->yAt( 2 ), 1.0 ); QCOMPARE( dynamic_cast< QgsLineString * >( lineBoundary->geometryN( 0 ) )->yAt( 3 ), 0.0 ); - delete boundary; // add polygon with interior rings QgsLineString ring2; @@ -4619,8 +4618,8 @@ void TestQgsGeometry::multiPolygon() polygon2.setInteriorRings( QList< QgsCurve * >() << boundaryRing1.clone() << boundaryRing2.clone() ); multiPolygon1.addGeometry( polygon2.clone() ); - boundary = multiPolygon1.boundary(); - QgsMultiLineString *multiLineBoundary = dynamic_cast< QgsMultiLineString * >( boundary ); + boundary.reset( multiPolygon1.boundary() ); + QgsMultiLineString *multiLineBoundary( static_cast< QgsMultiLineString * >( boundary.get() ) ); QVERIFY( multiLineBoundary ); QCOMPARE( multiLineBoundary->numGeometries(), 4 ); QCOMPARE( dynamic_cast< QgsLineString * >( multiLineBoundary->geometryN( 0 ) )->numPoints(), 4 ); @@ -4659,7 +4658,6 @@ void TestQgsGeometry::multiPolygon() QCOMPARE( dynamic_cast< QgsLineString * >( multiLineBoundary->geometryN( 3 ) )->yAt( 1 ), 10.8 ); QCOMPARE( dynamic_cast< QgsLineString * >( multiLineBoundary->geometryN( 3 ) )->yAt( 2 ), 10.9 ); QCOMPARE( dynamic_cast< QgsLineString * >( multiLineBoundary->geometryN( 3 ) )->yAt( 3 ), 10.8 ); - delete boundary; } void TestQgsGeometry::geometryCollection() From 0be4c673465a070a6a3709800daa7c83522680b0 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:12:40 +1000 Subject: [PATCH 252/364] Avoid Coverity unchecked return value warnings --- src/providers/ogr/qgsgeopackagedataitems.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 72e71aa8500..0b99c06da42 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -541,7 +541,7 @@ bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri char *sql = sqlite3_mprintf( "DELETE FROM %w WHERE table_name = '%q'", tableName.toUtf8().constData(), layerName.toUtf8().constData() ); - sqlite3_exec( + ( void )sqlite3_exec( handle, /* An open database */ sql, /* SQL to be evaluated */ NULL, /* Callback function */ @@ -554,7 +554,7 @@ bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri { char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_coverage_ancillary WHERE tile_matrix_set_name = '%q'", layerName.toUtf8().constData() ); - sqlite3_exec( + ( void )sqlite3_exec( handle, /* An open database */ sql, /* SQL to be evaluated */ NULL, /* Callback function */ @@ -566,7 +566,7 @@ bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri { char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_tile_ancillary WHERE tpudt_name = '%q'", layerName.toUtf8().constData() ); - sqlite3_exec( + ( void )sqlite3_exec( handle, /* An open database */ sql, /* SQL to be evaluated */ NULL, /* Callback function */ @@ -577,7 +577,7 @@ bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri } // Vacuum { - sqlite3_exec( + ( void )sqlite3_exec( handle, /* An open database */ "VACUUM", /* SQL to be evaluated */ NULL, /* Callback function */ From 75c65c590c593e274dc5ae61d2fd82afa170a656 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:14:41 +1000 Subject: [PATCH 253/364] Fix uninitialized/not copied member variable --- src/core/dxf/qgsdxfexport.cpp | 3 +-- src/core/dxf/qgsdxfexport.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/dxf/qgsdxfexport.cpp b/src/core/dxf/qgsdxfexport.cpp index 55a78847b49..e1eea467401 100644 --- a/src/core/dxf/qgsdxfexport.cpp +++ b/src/core/dxf/qgsdxfexport.cpp @@ -374,8 +374,6 @@ QgsDxfExport::QgsDxfExport() , mSymbolLayerCounter( 0 ) , mNextHandleId( DXF_HANDSEED ) , mBlockCounter( 0 ) - , mFactor( 1 ) - , mForce2d( false ) { } @@ -397,6 +395,7 @@ QgsDxfExport &QgsDxfExport::operator=( const QgsDxfExport &dxfExport ) mBlockCounter = 0; mCrs = QgsCoordinateReferenceSystem(); mFactor = dxfExport.mFactor; + mForce2d = dxfExport.mForce2d; return *this; } diff --git a/src/core/dxf/qgsdxfexport.h b/src/core/dxf/qgsdxfexport.h index 0f27dc78fc7..f9235dd658f 100644 --- a/src/core/dxf/qgsdxfexport.h +++ b/src/core/dxf/qgsdxfexport.h @@ -416,8 +416,8 @@ class CORE_EXPORT QgsDxfExport QgsCoordinateReferenceSystem mCrs; QgsMapSettings mMapSettings; QHash mLayerNameAttribute; - double mFactor; - bool mForce2d; + double mFactor = 1.0; + bool mForce2d = false; }; #endif // QGSDXFEXPORT_H From e84543d0fc1f002332a4d7f5911241221fdc7bd3 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:40:22 +1000 Subject: [PATCH 254/364] Fix memory leak --- src/core/qgsuserprofilemanager.cpp | 4 ++-- src/core/qgsuserprofilemanager.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/qgsuserprofilemanager.cpp b/src/core/qgsuserprofilemanager.cpp index 0e69be28dc9..a09a86db4f1 100644 --- a/src/core/qgsuserprofilemanager.cpp +++ b/src/core/qgsuserprofilemanager.cpp @@ -71,7 +71,7 @@ void QgsUserProfileManager::setRootLocation( QString rootProfileLocation ) { mWatcher->addPath( mRootProfilePath ); } - mSettings = new QSettings( settingsFile(), QSettings::IniFormat ); + mSettings.reset( new QSettings( settingsFile(), QSettings::IniFormat ) ); } bool QgsUserProfileManager::rootLocationIsSet() const @@ -172,7 +172,7 @@ QgsError QgsUserProfileManager::deleteProfile( const QString name ) return error; } -QString QgsUserProfileManager::settingsFile() +QString QgsUserProfileManager::settingsFile() const { return mRootProfilePath + QDir::separator() + "profiles.ini"; } diff --git a/src/core/qgsuserprofilemanager.h b/src/core/qgsuserprofilemanager.h index d991d635dde..76dabb308b9 100644 --- a/src/core/qgsuserprofilemanager.h +++ b/src/core/qgsuserprofilemanager.h @@ -179,9 +179,9 @@ class CORE_EXPORT QgsUserProfileManager : public QObject std::unique_ptr mUserProfile; - QString settingsFile(); + QString settingsFile() const; - QSettings *mSettings = nullptr; + std::unique_ptr< QSettings > mSettings; }; #endif // QGSUSERPROFILEMANAGER_H From 42b3e0bb103a85eba505eab404649cd7c741cb8f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:48:15 +1000 Subject: [PATCH 255/364] Fix another small leak --- src/app/qgisapp.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 78c8cee15c4..3e60c57e903 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -2278,14 +2278,13 @@ void QgisApp::refreshProfileMenu() QString activeName = profile->name(); mConfigMenu->setTitle( tr( "&User Profiles" ) ); - QActionGroup *profileGroup = new QActionGroup( this ); + QActionGroup *profileGroup = new QActionGroup( mConfigMenu ); profileGroup->setExclusive( true ); Q_FOREACH ( const QString &name, userProfileManager()->allProfiles() ) { profile = userProfileManager()->profileForName( name ); - // Qt 5.5 has no parent default as nullptr - QAction *action = new QAction( profile->icon(), profile->alias(), nullptr ); + QAction *action = new QAction( profile->icon(), profile->alias(), mConfigMenu ); action->setToolTip( profile->folder() ); action->setCheckable( true ); profileGroup->addAction( action ); From 7ba1b1734bb6e81070dc43459bf2be604766933e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:56:57 +1000 Subject: [PATCH 256/364] Add missing SIP_FACTORY --- python/core/qgsuserprofilemanager.sip | 2 +- src/app/qgisapp.cpp | 7 +++---- src/core/qgsuserprofilemanager.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/python/core/qgsuserprofilemanager.sip b/python/core/qgsuserprofilemanager.sip index 9321123e5f4..5b0437e0aab 100644 --- a/python/core/qgsuserprofilemanager.sip +++ b/python/core/qgsuserprofilemanager.sip @@ -118,7 +118,7 @@ class QgsUserProfileManager : QObject Set the default profile name from the current active profile. %End - QgsUserProfile *profileForName( const QString name ) const; + QgsUserProfile *profileForName( const QString &name ) const /Factory/; %Docstring Return the profile found for a given name. \param name The name of the profile to return. diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 3e60c57e903..c1218f5b9ae 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -2283,13 +2283,12 @@ void QgisApp::refreshProfileMenu() Q_FOREACH ( const QString &name, userProfileManager()->allProfiles() ) { - profile = userProfileManager()->profileForName( name ); - QAction *action = new QAction( profile->icon(), profile->alias(), mConfigMenu ); - action->setToolTip( profile->folder() ); + std::unique_ptr< QgsUserProfile > namedProfile( userProfileManager()->profileForName( name ) ); + QAction *action = new QAction( namedProfile->icon(), namedProfile->alias(), mConfigMenu ); + action->setToolTip( namedProfile->folder() ); action->setCheckable( true ); profileGroup->addAction( action ); mConfigMenu->addAction( action ); - delete profile; if ( name == activeName ) { diff --git a/src/core/qgsuserprofilemanager.h b/src/core/qgsuserprofilemanager.h index 76dabb308b9..5373df7d522 100644 --- a/src/core/qgsuserprofilemanager.h +++ b/src/core/qgsuserprofilemanager.h @@ -126,7 +126,7 @@ class CORE_EXPORT QgsUserProfileManager : public QObject * \param name The name of the profile to return. * \return A QgsUserprofile pointing to the location of the user profile. */ - QgsUserProfile *profileForName( const QString name ) const; + QgsUserProfile *profileForName( const QString &name ) const SIP_FACTORY; /** * Create a user profile given by the name From 15a48df6449651e204cd73b47538a5672e693795 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:57:24 +1000 Subject: [PATCH 257/364] Pass QString by const ref --- python/core/qgsuserprofilemanager.sip | 6 +++--- src/core/qgsuserprofilemanager.cpp | 6 +++--- src/core/qgsuserprofilemanager.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/python/core/qgsuserprofilemanager.sip b/python/core/qgsuserprofilemanager.sip index 5b0437e0aab..8380a3e8558 100644 --- a/python/core/qgsuserprofilemanager.sip +++ b/python/core/qgsuserprofilemanager.sip @@ -34,7 +34,7 @@ class QgsUserProfileManager : QObject User profile manager used to manage user profiles for the instance of QGIS. %End - static QString resolveProfilesFolder( const QString &basePath = QString() ); + static QString resolveProfilesFolder( const QString &basePath = QString() ); %Docstring Resolves the profiles folder for the given path. Path will have \\profiles appended to the path \param basePath The base path to resolve the path from to append the \\profiles folder to. @@ -58,7 +58,7 @@ class QgsUserProfileManager : QObject :rtype: QgsUserProfile %End - void setRootLocation( QString rootProfileLocation ); + void setRootLocation( const QString &rootProfileLocation ); %Docstring Set the root profile location for the profile manager. All profiles are loaded from this location. Will also contain a profiles.ini for holding profile settings. @@ -134,7 +134,7 @@ class QgsUserProfileManager : QObject :rtype: QgsError %End - QgsError deleteProfile( const QString name ); + QgsError deleteProfile( const QString &name ); %Docstring Deletes a profile from the root profiles folder. .. note:: diff --git a/src/core/qgsuserprofilemanager.cpp b/src/core/qgsuserprofilemanager.cpp index a09a86db4f1..83306ee874c 100644 --- a/src/core/qgsuserprofilemanager.cpp +++ b/src/core/qgsuserprofilemanager.cpp @@ -58,7 +58,7 @@ QgsUserProfile *QgsUserProfileManager::getProfile( const QString &defaultProfile return profile; } -void QgsUserProfileManager::setRootLocation( QString rootProfileLocation ) +void QgsUserProfileManager::setRootLocation( const QString &rootProfileLocation ) { mRootProfilePath = rootProfileLocation; @@ -115,7 +115,7 @@ bool QgsUserProfileManager::profileExists( const QString &name ) const return allProfiles().contains( name ); } -QgsUserProfile *QgsUserProfileManager::profileForName( const QString name ) const +QgsUserProfile *QgsUserProfileManager::profileForName( const QString &name ) const { QString profilePath = mRootProfilePath + QDir::separator() + name; return new QgsUserProfile( profilePath ); @@ -154,7 +154,7 @@ QgsError QgsUserProfileManager::createUserProfile( const QString &name ) return error; } -QgsError QgsUserProfileManager::deleteProfile( const QString name ) +QgsError QgsUserProfileManager::deleteProfile( const QString &name ) { QgsError error; QDir folder( mRootProfilePath + QDir::separator() + name ); diff --git a/src/core/qgsuserprofilemanager.h b/src/core/qgsuserprofilemanager.h index 5373df7d522..fe238e36929 100644 --- a/src/core/qgsuserprofilemanager.h +++ b/src/core/qgsuserprofilemanager.h @@ -53,7 +53,7 @@ class CORE_EXPORT QgsUserProfileManager : public QObject * \param basePath The base path to resolve the path from to append the \\profiles folder to. * \return The root path to store user profiles. */ - static QString resolveProfilesFolder( const QString &basePath = QString() ); + static QString resolveProfilesFolder( const QString &basePath = QString() ); /** * Return the profile from the given root profile location. @@ -73,7 +73,7 @@ class CORE_EXPORT QgsUserProfileManager : public QObject * location. Will also contain a profiles.ini for holding profile settings. * \param rootProfileLocation Path to the top level profile folder which contains folders for each profile. */ - void setRootLocation( QString rootProfileLocation ); + void setRootLocation( const QString &rootProfileLocation ); /** * Returns the path to the root profiles location. @@ -141,7 +141,7 @@ class CORE_EXPORT QgsUserProfileManager : public QObject * \param name The name of the profile to delete. * \return A QgsError with a message if the profile failed to be deleted. */ - QgsError deleteProfile( const QString name ); + QgsError deleteProfile( const QString &name ); /** * The currently active user profile. From 1037d4752df51aefa3b8dec50664e5fddf6a6ad3 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 08:59:45 +1000 Subject: [PATCH 258/364] Fix QFileSystemWatcher debug warning on startup --- src/app/qgisapp.cpp | 2 +- src/core/qgsuserprofilemanager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index c1218f5b9ae..7c2a2dc48f8 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -652,7 +652,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh QgsRuntimeProfiler *profiler = QgsApplication::profiler(); startProfile( QStringLiteral( "User profile manager" ) ); - mUserProfileManager = new QgsUserProfileManager( "", this ); + mUserProfileManager = new QgsUserProfileManager( QString(), this ); mUserProfileManager->setRootLocation( rootProfileLocation ); mUserProfileManager->setActiveUserProfile( activeProfile ); connect( mUserProfileManager, &QgsUserProfileManager::profilesChanged, this, &QgisApp::refreshProfileMenu ); diff --git a/src/core/qgsuserprofilemanager.cpp b/src/core/qgsuserprofilemanager.cpp index 83306ee874c..24ed32d312a 100644 --- a/src/core/qgsuserprofilemanager.cpp +++ b/src/core/qgsuserprofilemanager.cpp @@ -67,7 +67,7 @@ void QgsUserProfileManager::setRootLocation( const QString &rootProfileLocation mWatcher->removePaths( mWatcher->directories() ); } - if ( QDir( mRootProfilePath ).exists() ) + if ( !mRootProfilePath.isEmpty() && QDir( mRootProfilePath ).exists() ) { mWatcher->addPath( mRootProfilePath ); } From 247d2657cafbf6608b8b037784b6e8191de3cbe8 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 09:00:57 +1000 Subject: [PATCH 259/364] Fix invalid regular expression warnings --- .../plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py | 4 ++-- python/plugins/processing/gui/BatchOutputSelectionPanel.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py b/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py index a17f2bfa5f4..d953162a581 100644 --- a/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py +++ b/python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py @@ -169,8 +169,8 @@ class FieldsCalculatorDialog(BASE, WIDGET): filename = str(files[0]) selectedFileFilter = str(fileDialog.selectedNameFilter()) if not filename.lower().endswith( - tuple(re.findall("\*(\.[a-z]{1,10})", fileFilter))): - ext = re.search("\*(\.[a-z]{1,10})", selectedFileFilter) + tuple(re.findall("\\*(\\.[a-z]{1,10})", fileFilter))): + ext = re.search("\\*(\\.[a-z]{1,10})", selectedFileFilter) if ext: filename = filename + ext.group(1) self.leOutputFile.setText(filename) diff --git a/python/plugins/processing/gui/BatchOutputSelectionPanel.py b/python/plugins/processing/gui/BatchOutputSelectionPanel.py index 66ba87b6685..c9d48bc3252 100644 --- a/python/plugins/processing/gui/BatchOutputSelectionPanel.py +++ b/python/plugins/processing/gui/BatchOutputSelectionPanel.py @@ -84,8 +84,8 @@ class BatchOutputSelectionPanel(QWidget): self.tr('Save file'), path, filefilter) if filename: if not filename.lower().endswith( - tuple(re.findall("\*(\.[a-z]{1,10})", filefilter))): - ext = re.search("\*(\.[a-z]{1,10})", selectedFileFilter) + tuple(re.findall("\\*(\\.[a-z]{1,10})", filefilter))): + ext = re.search("\\*(\\.[a-z]{1,10})", selectedFileFilter) if ext: filename += ext.group(1) settings.setValue('/Processing/LastBatchOutputPath', os.path.dirname(filename)) From 7ca0b2deb3380691454ce481794afb5d38f715a6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 20:44:24 +1000 Subject: [PATCH 260/364] [composer] Allow finer setting of scalebar height and widths While the settings are stored as double, they were being rounded off in the UI to the nearest mm --- .../composer/qgscomposerscalebarwidget.cpp | 10 ++- src/app/composer/qgscomposerscalebarwidget.h | 6 +- src/core/composer/qgscomposerscalebar.cpp | 4 +- .../composer/qgscomposerscalebarwidgetbase.ui | 87 +++++++++---------- 4 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/app/composer/qgscomposerscalebarwidget.cpp b/src/app/composer/qgscomposerscalebarwidget.cpp index e972b27674e..d10eda3731e 100644 --- a/src/app/composer/qgscomposerscalebarwidget.cpp +++ b/src/app/composer/qgscomposerscalebarwidget.cpp @@ -233,7 +233,7 @@ void QgsComposerScaleBarWidget::on_mNumberOfSegmentsSpinBox_valueChanged( int i mComposerScaleBar->endCommand(); } -void QgsComposerScaleBarWidget::on_mHeightSpinBox_valueChanged( int i ) +void QgsComposerScaleBarWidget::on_mHeightSpinBox_valueChanged( double d ) { if ( !mComposerScaleBar ) { @@ -241,7 +241,7 @@ void QgsComposerScaleBarWidget::on_mHeightSpinBox_valueChanged( int i ) } mComposerScaleBar->beginCommand( tr( "Scalebar height changed" ), QgsComposerMergeCommand::ScaleBarHeight ); disconnectUpdateSignal(); - mComposerScaleBar->setHeight( i ); + mComposerScaleBar->setHeight( d ); mComposerScaleBar->update(); connectUpdateSignal(); mComposerScaleBar->endCommand(); @@ -562,6 +562,8 @@ void QgsComposerScaleBarWidget::blockMemberSignals( bool block ) mSegmentSizeRadioGroup.blockSignals( block ); mMapItemComboBox->blockSignals( block ); mFontButton->blockSignals( block ); + mMinWidthSpinBox->blockSignals( block ); + mMaxWidthSpinBox->blockSignals( block ); } void QgsComposerScaleBarWidget::connectUpdateSignal() @@ -651,7 +653,7 @@ void QgsComposerScaleBarWidget::composerMapChanged( QgsComposerItem *item ) mComposerScaleBar->endCommand(); } -void QgsComposerScaleBarWidget::on_mMinWidthSpinBox_valueChanged( int ) +void QgsComposerScaleBarWidget::on_mMinWidthSpinBox_valueChanged( double ) { if ( !mComposerScaleBar ) { @@ -666,7 +668,7 @@ void QgsComposerScaleBarWidget::on_mMinWidthSpinBox_valueChanged( int ) mComposerScaleBar->endCommand(); } -void QgsComposerScaleBarWidget::on_mMaxWidthSpinBox_valueChanged( int ) +void QgsComposerScaleBarWidget::on_mMaxWidthSpinBox_valueChanged( double ) { if ( !mComposerScaleBar ) { diff --git a/src/app/composer/qgscomposerscalebarwidget.h b/src/app/composer/qgscomposerscalebarwidget.h index 0dac6ff68d0..7e59655be0c 100644 --- a/src/app/composer/qgscomposerscalebarwidget.h +++ b/src/app/composer/qgscomposerscalebarwidget.h @@ -35,7 +35,7 @@ class QgsComposerScaleBarWidget: public QgsComposerItemBaseWidget, private Ui::Q public slots: - void on_mHeightSpinBox_valueChanged( int i ); + void on_mHeightSpinBox_valueChanged( double d ); void on_mLineWidthSpinBox_valueChanged( double d ); void on_mSegmentSizeSpinBox_valueChanged( double d ); void on_mSegmentsLeftSpinBox_valueChanged( int i ); @@ -53,8 +53,8 @@ class QgsComposerScaleBarWidget: public QgsComposerItemBaseWidget, private Ui::Q void on_mUnitsComboBox_currentIndexChanged( int index ); void on_mLineJoinStyleCombo_currentIndexChanged( int index ); void on_mLineCapStyleCombo_currentIndexChanged( int index ); - void on_mMinWidthSpinBox_valueChanged( int i ); - void on_mMaxWidthSpinBox_valueChanged( int i ); + void on_mMinWidthSpinBox_valueChanged( double d ); + void on_mMaxWidthSpinBox_valueChanged( double d ); private slots: void setGuiElements(); diff --git a/src/core/composer/qgscomposerscalebar.cpp b/src/core/composer/qgscomposerscalebar.cpp index 32e4498c810..afb4cb00c16 100644 --- a/src/core/composer/qgscomposerscalebar.cpp +++ b/src/core/composer/qgscomposerscalebar.cpp @@ -728,8 +728,8 @@ bool QgsComposerScaleBar::readXml( const QDomElement &itemElem, const QDomDocume mSettings.setNumberOfSegmentsLeft( itemElem.attribute( QStringLiteral( "numSegmentsLeft" ), QStringLiteral( "0" ) ).toInt() ); mSettings.setUnitsPerSegment( itemElem.attribute( QStringLiteral( "numUnitsPerSegment" ), QStringLiteral( "1.0" ) ).toDouble() ); mSettings.setSegmentSizeMode( static_cast( itemElem.attribute( QStringLiteral( "segmentSizeMode" ), QStringLiteral( "0" ) ).toInt() ) ); - mSettings.setMinimumBarWidth( itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toInt() ); - mSettings.setMaximumBarWidth( itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toInt() ); + mSettings.setMinimumBarWidth( itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toDouble() ); + mSettings.setMaximumBarWidth( itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toDouble() ); mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble(); mSettings.setMapUnitsPerScaleBarUnit( itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble() ); mSettings.setLineWidth( itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "0.3" ) ).toDouble() ); diff --git a/src/ui/composer/qgscomposerscalebarwidgetbase.ui b/src/ui/composer/qgscomposerscalebarwidgetbase.ui index d347d34a0d2..e8bc3743f74 100755 --- a/src/ui/composer/qgscomposerscalebarwidgetbase.ui +++ b/src/ui/composer/qgscomposerscalebarwidgetbase.ui @@ -60,9 +60,9 @@ 0 - -121 - 437 - 764 + 0 + 440 + 1004 @@ -261,22 +261,6 @@ - - - - false - - - mm - - - max - - - 999 - - - @@ -287,35 +271,6 @@ - - - - false - - - mm - - - min - - - 999 - - - - - - - mm - - - - - - false - - - @@ -333,6 +288,42 @@ + + + + mm + + + 999.990000000000009 + + + + + + + false + + + mm + + + 999.990000000000009 + + + + + + + false + + + mm + + + 999.990000000000009 + + + From 9d3c9e1963d77da32effc5366bdc0b5bc6d17a34 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 20:59:25 +1000 Subject: [PATCH 261/364] [FEATURE] Expose "justify" text alignment as an option for composer label alignment It's supported by Qt already, just not shown as an option in the config widget! --- src/app/composer/qgscomposerlabelwidget.cpp | 14 ++ src/app/composer/qgscomposerlabelwidget.h | 1 + src/core/composer/qgscomposerlabel.cpp | 2 +- src/ui/composer/qgscomposerlabelwidgetbase.ui | 129 +++++++++--------- 4 files changed, 79 insertions(+), 67 deletions(-) diff --git a/src/app/composer/qgscomposerlabelwidget.cpp b/src/app/composer/qgscomposerlabelwidget.cpp index 0f34cbf6241..002ff2397de 100644 --- a/src/app/composer/qgscomposerlabelwidget.cpp +++ b/src/app/composer/qgscomposerlabelwidget.cpp @@ -50,6 +50,7 @@ QgsComposerLabelWidget::QgsComposerLabelWidget( QgsComposerLabel *label ): QgsCo } connect( mFontButton, &QgsFontButton::changed, this, &QgsComposerLabelWidget::fontChanged ); + connect( mJustifyRadioButton, &QRadioButton::clicked, this, &QgsComposerLabelWidget::justifyClicked ); } void QgsComposerLabelWidget::on_mHtmlCheckBox_stateChanged( int state ) @@ -96,6 +97,17 @@ void QgsComposerLabelWidget::fontChanged() } } +void QgsComposerLabelWidget::justifyClicked() +{ + if ( mComposerLabel ) + { + mComposerLabel->beginCommand( tr( "Label alignment changed" ) ); + mComposerLabel->setHAlign( Qt::AlignJustify ); + mComposerLabel->update(); + mComposerLabel->endCommand(); + } +} + void QgsComposerLabelWidget::on_mMarginXDoubleSpinBox_valueChanged( double d ) { if ( mComposerLabel ) @@ -240,6 +252,7 @@ void QgsComposerLabelWidget::setGuiElementValues() mMiddleRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignVCenter ); mBottomRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignBottom ); mLeftRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignLeft ); + mJustifyRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignJustify ); mCenterRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignHCenter ); mRightRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignRight ); mFontColorButton->setColor( mComposerLabel->fontColor() ); @@ -264,6 +277,7 @@ void QgsComposerLabelWidget::blockAllSignals( bool block ) mLeftRadioButton->blockSignals( block ); mCenterRadioButton->blockSignals( block ); mRightRadioButton->blockSignals( block ); + mJustifyRadioButton->blockSignals( block ); mFontColorButton->blockSignals( block ); mFontButton->blockSignals( block ); } diff --git a/src/app/composer/qgscomposerlabelwidget.h b/src/app/composer/qgscomposerlabelwidget.h index a0c4bb37876..23fab501944 100644 --- a/src/app/composer/qgscomposerlabelwidget.h +++ b/src/app/composer/qgscomposerlabelwidget.h @@ -49,6 +49,7 @@ class QgsComposerLabelWidget: public QgsComposerItemBaseWidget, private Ui::QgsC private slots: void setGuiElementValues(); void fontChanged(); + void justifyClicked(); private: QgsComposerLabel *mComposerLabel = nullptr; diff --git a/src/core/composer/qgscomposerlabel.cpp b/src/core/composer/qgscomposerlabel.cpp index 339745eb2e2..e2558ee39fe 100644 --- a/src/core/composer/qgscomposerlabel.cpp +++ b/src/core/composer/qgscomposerlabel.cpp @@ -612,7 +612,7 @@ QUrl QgsComposerLabel::createStylesheetUrl() const stylesheet += QStringLiteral( "body { margin: %1 %2;" ).arg( std::max( mMarginY * mHtmlUnitsToMM, 0.0 ) ).arg( std::max( mMarginX * mHtmlUnitsToMM, 0.0 ) ); stylesheet += QgsFontUtils::asCSS( mFont, 0.352778 * mHtmlUnitsToMM ); stylesheet += QStringLiteral( "color: %1;" ).arg( mFontColor.name() ); - stylesheet += QStringLiteral( "text-align: %1; }" ).arg( mHAlignment == Qt::AlignLeft ? "left" : mHAlignment == Qt::AlignRight ? "right" : "center" ); + stylesheet += QStringLiteral( "text-align: %1; }" ).arg( mHAlignment == Qt::AlignLeft ? QStringLiteral( "left" ) : mHAlignment == Qt::AlignRight ? QStringLiteral( "right" ) : mHAlignment == Qt::AlignHCenter ? QStringLiteral( "center" ) : QStringLiteral( "justify" ) ); QByteArray ba; ba.append( stylesheet.toUtf8() ); diff --git a/src/ui/composer/qgscomposerlabelwidgetbase.ui b/src/ui/composer/qgscomposerlabelwidgetbase.ui index a9d78236efb..42c0beb479a 100755 --- a/src/ui/composer/qgscomposerlabelwidgetbase.ui +++ b/src/ui/composer/qgscomposerlabelwidgetbase.ui @@ -61,8 +61,8 @@ 0 0 - 302 - 686 + 446 + 665 @@ -126,14 +126,24 @@ false - - + + - Font color + Horizontal alignment - + + + + mm + + + -99.989999999999995 + + + + @@ -180,6 +190,20 @@ + + + + Vertical margin + + + + + + + Horizontal margin + + + @@ -193,9 +217,16 @@ - - - + + + + Font color + + + + + + Left @@ -205,17 +236,17 @@ - - + + - Center + Justify buttonGroup_2 - + Right @@ -225,39 +256,35 @@ - - - - Qt::Horizontal + + + + Center - - - 0 - 20 - - - + + buttonGroup_2 + + - - - - Horizontal alignment - - - - + Vertical alignment - - + + + + + 0 + 0 + + - Horizontal margin + Font @@ -297,36 +324,6 @@ - - - - mm - - - -99.989999999999995 - - - - - - - Vertical margin - - - - - - - - 0 - 0 - - - - Font - - - @@ -388,7 +385,7 @@ - + From cd1c9b1e280785310021c8ecd38fcabeb3ed2a2a Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 1 Sep 2017 14:30:34 +0200 Subject: [PATCH 262/364] Add class QgsSourceSelectProvider and tests This is the first step for QEP 101 --- python/core/core_auto.sip | 1 + python/gui/gui_auto.sip | 2 + python/gui/qgssourceselectprovider.sip | 59 ++++++++++++++++ src/gui/CMakeLists.txt | 2 + src/gui/qgssourceselectprovider.cpp | 20 ++++++ src/gui/qgssourceselectprovider.h | 53 +++++++++++++++ tests/src/python/CMakeLists.txt | 1 + .../python/test_qgssourceselectprovider.py | 68 +++++++++++++++++++ 8 files changed, 206 insertions(+) create mode 100644 python/gui/qgssourceselectprovider.sip create mode 100644 src/gui/qgssourceselectprovider.cpp create mode 100644 src/gui/qgssourceselectprovider.h create mode 100644 tests/src/python/test_qgssourceselectprovider.py diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 37cfc8ee399..934e1e8a281 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -405,3 +405,4 @@ %Include symbology/qgsarrowsymbollayer.sip %Include composer/qgscomposerutils.sip %Include qgsuserprofile.sip + diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 071eb2153b1..840f1fd4e0d 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -297,3 +297,5 @@ %Include locator/qgslocatorfilter.sip %Include locator/qgslocatorwidget.sip %Include qgsadvanceddigitizingcanvasitem.sip +%Include qgssourceselectprovider.sip + diff --git a/python/gui/qgssourceselectprovider.sip b/python/gui/qgssourceselectprovider.sip new file mode 100644 index 00000000000..2e0d7fa8c0a --- /dev/null +++ b/python/gui/qgssourceselectprovider.sip @@ -0,0 +1,59 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssourceselectprovider.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsSourceSelectProvider +{ +%Docstring +.. seealso:: QgsDataSourceManagerDialog + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgssourceselectprovider.h" +%End + public: + virtual ~QgsSourceSelectProvider(); + + virtual QString providerKey() const = 0; +%Docstring +Provider key + :rtype: str +%End + + virtual QString text() const = 0; +%Docstring +Text for the menu item entry + :rtype: str +%End + + virtual QIcon icon() const = 0 /Factory/; +%Docstring +Caller takes responsibility of deleting created. + :rtype: QIcon +%End + + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( ) = 0 /Factory/; +%Docstring +Caller takes responsibility of deleting created. + :rtype: QgsAbstractDataSourceWidget +%End + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssourceselectprovider.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 8b98c620211..850ebca73ea 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -349,6 +349,7 @@ SET(QGIS_GUI_SRCS qgsfiledownloader.cpp qgsdatasourcemanagerdialog.cpp qgsabstractdatasourcewidget.cpp + qgssourceselectprovider.cpp ) SET(QGIS_GUI_MOC_HDRS @@ -711,6 +712,7 @@ SET(QGIS_GUI_HDRS qgsfiledownloader.h qgsdatasourcemanagerdialog.h qgsabstractdatasourcewidget.h + qgssourceselectprovider.h ogr/qgsogrhelperfunctions.h ogr/qgsnewogrconnection.h diff --git a/src/gui/qgssourceselectprovider.cpp b/src/gui/qgssourceselectprovider.cpp new file mode 100644 index 00000000000..915a198ffad --- /dev/null +++ b/src/gui/qgssourceselectprovider.cpp @@ -0,0 +1,20 @@ +/*************************************************************************** + qgssourceselectprovider.cpp - QgsSourceSelectProvider + + --------------------- + begin : 1.9.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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 "qgssourceselectprovider.h" + + +// no implementation currently + diff --git a/src/gui/qgssourceselectprovider.h b/src/gui/qgssourceselectprovider.h new file mode 100644 index 00000000000..8530f5d987a --- /dev/null +++ b/src/gui/qgssourceselectprovider.h @@ -0,0 +1,53 @@ +/*************************************************************************** + qgssourceselectprovider.h - QgsSourceSelectProvider + + --------------------- + begin : 1.9.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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 QGSSOURCESELECTPROVIDER_H +#define QGSSOURCESELECTPROVIDER_H + + +#include "qgis_gui.h" +#include "qgis.h" +#include "qgsabstractdatasourcewidget.h" + +class QString; + +/** \ingroup gui + * This is the interface for those who want to add entries to the \see QgsDataSourceManagerDialog + * + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsSourceSelectProvider +{ + public: + virtual ~QgsSourceSelectProvider() = default; + + //! Provider key + virtual QString providerKey() const = 0; + + //! Text for the menu item entry + virtual QString text() const = 0; + + //! Creates a new instance of an QIcon for the menu item entry + //! Caller takes responsibility of deleting created. + virtual QIcon icon() const = 0 SIP_FACTORY; + + //! Create a new instance of QgsAbstractDataSourceWidget (or null). + //! Caller takes responsibility of deleting created. + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( ) = 0 SIP_FACTORY; + +}; + + +#endif // QGSSOURCESELECTPROVIDER_H diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index ca0797d9edd..bbcee6788db 100755 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -176,6 +176,7 @@ ADD_PYTHON_TEST(PyQgsDBManagerGpkg test_db_manager_gpkg.py) ADD_PYTHON_TEST(PyQgsFileDownloader test_qgsfiledownloader.py) ADD_PYTHON_TEST(PyQgsSettings test_qgssettings.py) ADD_PYTHON_TEST(PyQgsZipUtils test_qgsziputils.py) +ADD_PYTHON_TEST(PyQgsSourceSelectProvider test_qgssourceselectprovider.py) IF (NOT WIN32) ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py) diff --git a/tests/src/python/test_qgssourceselectprovider.py b/tests/src/python/test_qgssourceselectprovider.py new file mode 100644 index 00000000000..c6f1184867d --- /dev/null +++ b/tests/src/python/test_qgssourceselectprovider.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +Test the QgsSourceSelectProvider class + +Run with: ctest -V -R PyQgsSourceSelectProvider + +.. note:: 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. +""" + +import os +import tempfile +from qgis.gui import (QgsSourceSelectProvider, QgsAbstractDataSourceWidget) +from qgis.testing import start_app, unittest +from qgis.PyQt.QtGui import QIcon + +__author__ = 'Alessandro Pasotti' +__date__ = '01/09/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + + +start_app() + + +class ConcreteDataSourceWidget(QgsAbstractDataSourceWidget): + pass + + +class ConcreteSourceSelectProvider(QgsSourceSelectProvider): + + def providerKey(self): + return "MyTestProviderKey" + + def text(self): + return "MyTestProviderText" + + def icon(self): + return QIcon() + + def createDataSourceWidget(self): + return ConcreteDataSourceWidget() + + +class TestQgsSourceSelectProvider(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def testConcreteClass(self): + + provider = ConcreteSourceSelectProvider() + self.assertTrue(isinstance(provider, ConcreteSourceSelectProvider)) + widget = provider.createDataSourceWidget() + self.assertTrue(isinstance(widget, ConcreteDataSourceWidget)) + self.assertEqual(provider.providerKey(), "MyTestProviderKey") + self.assertEqual(provider.text(), "MyTestProviderText") + self.assertTrue(isinstance(provider.icon(), QIcon)) + + +if __name__ == '__main__': + unittest.main() From 4c46f6464742bee648a301213a10224db0b20bb2 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 1 Sep 2017 17:47:55 +0200 Subject: [PATCH 263/364] Add QgsSourceSelectProviderRegistry with tests --- python/gui/gui_auto.sip | 1 + python/gui/qgssourceselectprovider.sip | 29 +++++- .../gui/qgssourceselectproviderregistry.sip | 68 ++++++++++++++ src/gui/CMakeLists.txt | 2 + src/gui/qgssourceselectprovider.h | 30 ++++-- src/gui/qgssourceselectproviderregistry.cpp | 93 +++++++++++++++++++ src/gui/qgssourceselectproviderregistry.h | 68 ++++++++++++++ .../python/test_qgssourceselectprovider.py | 59 +++++++++++- 8 files changed, 337 insertions(+), 13 deletions(-) create mode 100644 python/gui/qgssourceselectproviderregistry.sip create mode 100644 src/gui/qgssourceselectproviderregistry.cpp create mode 100644 src/gui/qgssourceselectproviderregistry.h diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 840f1fd4e0d..a6ff6baf17d 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -298,4 +298,5 @@ %Include locator/qgslocatorwidget.sip %Include qgsadvanceddigitizingcanvasitem.sip %Include qgssourceselectprovider.sip +%Include qgssourceselectproviderregistry.sip diff --git a/python/gui/qgssourceselectprovider.sip b/python/gui/qgssourceselectprovider.sip index 2e0d7fa8c0a..e6bcfcdf1e4 100644 --- a/python/gui/qgssourceselectprovider.sip +++ b/python/gui/qgssourceselectprovider.sip @@ -25,25 +25,44 @@ class QgsSourceSelectProvider virtual QString providerKey() const = 0; %Docstring -Provider key +Data Provider key + :rtype: str +%End + + virtual QString name() const; +%Docstring + Source select provider name, this is useful to retrieve + a particular source select in case the provider has more + than one, it should be unique among all providers. + + The default implementation returns the providerKey() :rtype: str %End virtual QString text() const = 0; %Docstring -Text for the menu item entry +Text for the menu item entry, it will be visible to the user so make sure it's translatable :rtype: str %End virtual QIcon icon() const = 0 /Factory/; %Docstring -Caller takes responsibility of deleting created. + Creates a new instance of an QIcon for the menu item entry + Caller takes responsibility of deleting created. :rtype: QIcon %End - virtual QgsAbstractDataSourceWidget *createDataSourceWidget( ) = 0 /Factory/; + virtual int ordering( ) const; %Docstring -Caller takes responsibility of deleting created. + Ordering: the source select provider registry will be able to sort + the source selects (ascending) using this integer value + :rtype: int +%End + + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = 0 ) const = 0 /Factory/; +%Docstring + Create a new instance of QgsAbstractDataSourceWidget (or null). + Caller takes responsibility of deleting created. :rtype: QgsAbstractDataSourceWidget %End diff --git a/python/gui/qgssourceselectproviderregistry.sip b/python/gui/qgssourceselectproviderregistry.sip new file mode 100644 index 00000000000..0aa676140b3 --- /dev/null +++ b/python/gui/qgssourceselectproviderregistry.sip @@ -0,0 +1,68 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssourceselectproviderregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsSourceSelectProviderRegistry +{ +%Docstring + This class keeps a list of source select providers that may add items to the QgsDataSourceManagerDialog + When created, it automatically adds providers from data provider plugins (e.g. PostGIS, WMS, ...) + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgssourceselectproviderregistry.h" +%End +public: + QgsSourceSelectProviderRegistry(); + + ~QgsSourceSelectProviderRegistry(); + + + QList< QgsSourceSelectProvider*> providers() const; +%Docstring +Get list of available providers + :rtype: list of QgsSourceSelectProvider +%End + + void addProvider( QgsSourceSelectProvider *provider /Transfer/ ); +%Docstring +Add a provider implementation. Takes ownership of the object. +%End + + void removeProvider( QgsSourceSelectProvider *provider ); +%Docstring +Remove provider implementation from the list (provider object is deleted) +%End + + QgsSourceSelectProvider *providerByName( const QString &name ) const; +%Docstring +Return a provider by name or None if not found + :rtype: QgsSourceSelectProvider +%End + + QList providersByKey( const QString &providerKey ) const; +%Docstring +Return a (possibly empty) list of providers by data provider's key + :rtype: list of QgsSourceSelectProvider +%End + + +private: + QgsSourceSelectProviderRegistry( const QgsSourceSelectProviderRegistry &rh ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssourceselectproviderregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 850ebca73ea..0030c090f4c 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -350,6 +350,7 @@ SET(QGIS_GUI_SRCS qgsdatasourcemanagerdialog.cpp qgsabstractdatasourcewidget.cpp qgssourceselectprovider.cpp + qgssourceselectproviderregistry.cpp ) SET(QGIS_GUI_MOC_HDRS @@ -713,6 +714,7 @@ SET(QGIS_GUI_HDRS qgsdatasourcemanagerdialog.h qgsabstractdatasourcewidget.h qgssourceselectprovider.h + qgssourceselectproviderregistry.h ogr/qgsogrhelperfunctions.h ogr/qgsnewogrconnection.h diff --git a/src/gui/qgssourceselectprovider.h b/src/gui/qgssourceselectprovider.h index 8530f5d987a..eeb12799184 100644 --- a/src/gui/qgssourceselectprovider.h +++ b/src/gui/qgssourceselectprovider.h @@ -22,6 +22,7 @@ #include "qgsabstractdatasourcewidget.h" class QString; +class QWidget; /** \ingroup gui * This is the interface for those who want to add entries to the \see QgsDataSourceManagerDialog @@ -33,19 +34,34 @@ class GUI_EXPORT QgsSourceSelectProvider public: virtual ~QgsSourceSelectProvider() = default; - //! Provider key + //! Data Provider key virtual QString providerKey() const = 0; - //! Text for the menu item entry + /** Source select provider name, this is useful to retrieve + * a particular source select in case the provider has more + * than one, it should be unique among all providers. + * + * The default implementation returns the providerKey() + */ + virtual QString name() const { return providerKey(); } + + //! Text for the menu item entry, it will be visible to the user so make sure it's translatable virtual QString text() const = 0; - //! Creates a new instance of an QIcon for the menu item entry - //! Caller takes responsibility of deleting created. + /** Creates a new instance of an QIcon for the menu item entry + * Caller takes responsibility of deleting created. + */ virtual QIcon icon() const = 0 SIP_FACTORY; - //! Create a new instance of QgsAbstractDataSourceWidget (or null). - //! Caller takes responsibility of deleting created. - virtual QgsAbstractDataSourceWidget *createDataSourceWidget( ) = 0 SIP_FACTORY; + /** Ordering: the source select provider registry will be able to sort + * the source selects (ascending) using this integer value + */ + virtual int ordering( ) const { return 0; } + + /** Create a new instance of QgsAbstractDataSourceWidget (or null). + * Caller takes responsibility of deleting created. + */ + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr ) const = 0 SIP_FACTORY; }; diff --git a/src/gui/qgssourceselectproviderregistry.cpp b/src/gui/qgssourceselectproviderregistry.cpp new file mode 100644 index 00000000000..76bc84a89d0 --- /dev/null +++ b/src/gui/qgssourceselectproviderregistry.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + qgssourceselectproviderregistry.cpp - QgsSourceSelectProviderRegistry + + --------------------- + begin : 1.9.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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 "qgssourceselectprovider.h" +#include "qgssourceselectproviderregistry.h" +#include "qgsproviderregistry.h" + +#include + +typedef QList *sourceSelectProviders_t(); + + +QgsSourceSelectProviderRegistry::QgsSourceSelectProviderRegistry() +{ + QStringList providersList = QgsProviderRegistry::instance()->providerList(); + + Q_FOREACH ( const QString &key, providersList ) + { + std::unique_ptr< QLibrary > library( QgsProviderRegistry::instance()->createProviderLibrary( key ) ); + if ( !library ) + continue; + + sourceSelectProviders_t *sourceSelectProvidersFn = reinterpret_cast< sourceSelectProviders_t * >( cast_to_fptr( library->resolve( "sourceSelectProviders" ) ) ); + if ( sourceSelectProvidersFn ) + { + QList *providerList = sourceSelectProvidersFn(); + // the function is a factory - we keep ownership of the returned providers + for ( auto provider : qgsAsConst( *providerList ) ) + { + addProvider( provider ); + } + delete providerList; + } + } +} + +QgsSourceSelectProviderRegistry::~QgsSourceSelectProviderRegistry() +{ + qDeleteAll( mProviders ); +} + +void QgsSourceSelectProviderRegistry::addProvider( QgsSourceSelectProvider *provider ) +{ + mProviders.append( provider ); + std::sort( mProviders.begin(), mProviders.end(), [ ]( const QgsSourceSelectProvider * first, const QgsSourceSelectProvider * second ) -> bool + { + return first->ordering() < second->ordering(); + } ); +} + +void QgsSourceSelectProviderRegistry::removeProvider( QgsSourceSelectProvider *provider ) +{ + int index = mProviders.indexOf( provider ); + if ( index >= 0 ) + delete mProviders.takeAt( index ); +} + +QgsSourceSelectProvider *QgsSourceSelectProviderRegistry::providerByName( const QString &name ) const +{ + for ( const auto provider : qgsAsConst( mProviders ) ) + { + if ( provider->name() == name ) + { + return provider; + } + } + return nullptr; +} + +QList QgsSourceSelectProviderRegistry::providersByKey( const QString &providerKey ) const +{ + QList providerList; + for ( const auto provider : qgsAsConst( mProviders ) ) + { + if ( provider->providerKey() == providerKey ) + { + providerList << provider; + } + } + return providerList; +} diff --git a/src/gui/qgssourceselectproviderregistry.h b/src/gui/qgssourceselectproviderregistry.h new file mode 100644 index 00000000000..b896b8a6cf4 --- /dev/null +++ b/src/gui/qgssourceselectproviderregistry.h @@ -0,0 +1,68 @@ +/*************************************************************************** + qgssourceselectproviderregistry.h - QgsSourceSelectProviderRegistry + + --------------------- + begin : 1.9.2017 + copyright : (C) 2017 by Alessandro Pasotti + email : apasotti at boundlessgeo 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 QGSSOURCESELECTPROVIDERREGISTRY_H +#define QGSSOURCESELECTPROVIDERREGISTRY_H + +#include "qgis_gui.h" +#include "qgis.h" + +class QgsSourceSelectProvider; + +/** \ingroup gui + * This class keeps a list of source select providers that may add items to the QgsDataSourceManagerDialog + * When created, it automatically adds providers from data provider plugins (e.g. PostGIS, WMS, ...) + * + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsSourceSelectProviderRegistry +{ + public: + QgsSourceSelectProviderRegistry(); + + ~QgsSourceSelectProviderRegistry(); + + //! QgsDataItemProviderRegistry cannot be copied. + QgsSourceSelectProviderRegistry( const QgsSourceSelectProviderRegistry &rh ) = delete; + //! QgsDataItemProviderRegistry cannot be copied. + QgsSourceSelectProviderRegistry &operator=( const QgsSourceSelectProviderRegistry &rh ) = delete; + + //! Get list of available providers + QList< QgsSourceSelectProvider *> providers() const { return mProviders; } + + //! Add a provider implementation. Takes ownership of the object. + void addProvider( QgsSourceSelectProvider *provider SIP_TRANSFER ); + + //! Remove provider implementation from the list (provider object is deleted) + void removeProvider( QgsSourceSelectProvider *provider ); + + //! Return a provider by name or nullptr if not found + QgsSourceSelectProvider *providerByName( const QString &name ) const; + + //! Return a (possibly empty) list of providers by data provider's key + QList providersByKey( const QString &providerKey ) const; + + + private: +#ifdef SIP_RUN + QgsSourceSelectProviderRegistry( const QgsSourceSelectProviderRegistry &rh ); +#endif + + //! available providers. this class owns the pointers + QList mProviders; + +}; + +#endif // QGSSOURCESELECTPROVIDERREGISTRY_H diff --git a/tests/src/python/test_qgssourceselectprovider.py b/tests/src/python/test_qgssourceselectprovider.py index c6f1184867d..293c5a009ae 100644 --- a/tests/src/python/test_qgssourceselectprovider.py +++ b/tests/src/python/test_qgssourceselectprovider.py @@ -12,9 +12,10 @@ the Free Software Foundation; either version 2 of the License, or import os import tempfile -from qgis.gui import (QgsSourceSelectProvider, QgsAbstractDataSourceWidget) +from qgis.gui import (QgsSourceSelectProvider, QgsSourceSelectProviderRegistry, QgsAbstractDataSourceWidget) from qgis.testing import start_app, unittest from qgis.PyQt.QtGui import QIcon +from qgis.PyQt.QtWidgets import QWidget __author__ = 'Alessandro Pasotti' __date__ = '01/09/2017' @@ -44,6 +45,30 @@ class ConcreteSourceSelectProvider(QgsSourceSelectProvider): def createDataSourceWidget(self): return ConcreteDataSourceWidget() + def ordering(self): + return 1 + + +class ConcreteSourceSelectProvider2(QgsSourceSelectProvider): + + def providerKey(self): + return "MyTestProviderKey2" + + def text(self): + return "MyTestProviderText2" + + def name(self): + return "MyName" + + def icon(self): + return QIcon() + + def createDataSourceWidget(self): + return ConcreteDataSourceWidget() + + def ordering(self): + return 2 + class TestQgsSourceSelectProvider(unittest.TestCase): @@ -60,9 +85,41 @@ class TestQgsSourceSelectProvider(unittest.TestCase): widget = provider.createDataSourceWidget() self.assertTrue(isinstance(widget, ConcreteDataSourceWidget)) self.assertEqual(provider.providerKey(), "MyTestProviderKey") + self.assertEqual(provider.name(), "MyTestProviderKey") self.assertEqual(provider.text(), "MyTestProviderText") + self.assertEqual(provider.ordering(), 1) self.assertTrue(isinstance(provider.icon(), QIcon)) + def testRegistry(self): + + registry = QgsSourceSelectProviderRegistry() + registry.addProvider(ConcreteSourceSelectProvider()) + registry.addProvider(ConcreteSourceSelectProvider2()) + + # Check order + self.assertEqual(['MyTestProviderKey', 'MyName'], [p.name() for p in registry.providers() if p.providerKey().startswith('MyTestProviderKey')]) + + registry = QgsSourceSelectProviderRegistry() + registry.addProvider(ConcreteSourceSelectProvider()) + registry.addProvider(ConcreteSourceSelectProvider2()) + + # Check order + self.assertEqual(['MyTestProviderKey', 'MyName'], [p.name() for p in registry.providers() if p.providerKey().startswith('MyTestProviderKey')]) + + # Get provider by name + self.assertTrue(registry.providerByName('MyTestProviderKey')) + self.assertTrue(registry.providerByName('MyName')) + + # Get not existent by name + self.assertFalse(registry.providerByName('Oh This Is Missing!')) + + # Get providers by provider key + self.assertGreater(len(registry.providersByKey('MyTestProviderKey')), 0) + self.assertGreater(len(registry.providersByKey('MyTestProviderKey2')), 0) + + # Get not existent by key + self.assertEqual(len(registry.providersByKey('Oh This Is Missing!')), 0) + if __name__ == '__main__': unittest.main() From d4868b3f826edca3d0856164213c0f921870d21f Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 1 Sep 2017 18:20:06 +0200 Subject: [PATCH 264/364] Update sip file --- .../gui/qgssourceselectproviderregistry.sip | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/python/gui/qgssourceselectproviderregistry.sip b/python/gui/qgssourceselectproviderregistry.sip index 0aa676140b3..3f108447f18 100644 --- a/python/gui/qgssourceselectproviderregistry.sip +++ b/python/gui/qgssourceselectproviderregistry.sip @@ -20,43 +20,43 @@ class QgsSourceSelectProviderRegistry %TypeHeaderCode #include "qgssourceselectproviderregistry.h" %End -public: - QgsSourceSelectProviderRegistry(); + public: + QgsSourceSelectProviderRegistry(); - ~QgsSourceSelectProviderRegistry(); + ~QgsSourceSelectProviderRegistry(); - QList< QgsSourceSelectProvider*> providers() const; + QList< QgsSourceSelectProvider *> providers() const; %Docstring Get list of available providers :rtype: list of QgsSourceSelectProvider %End - void addProvider( QgsSourceSelectProvider *provider /Transfer/ ); + void addProvider( QgsSourceSelectProvider *provider /Transfer/ ); %Docstring Add a provider implementation. Takes ownership of the object. %End - void removeProvider( QgsSourceSelectProvider *provider ); + void removeProvider( QgsSourceSelectProvider *provider ); %Docstring Remove provider implementation from the list (provider object is deleted) %End - QgsSourceSelectProvider *providerByName( const QString &name ) const; + QgsSourceSelectProvider *providerByName( const QString &name ) const; %Docstring Return a provider by name or None if not found :rtype: QgsSourceSelectProvider %End - QList providersByKey( const QString &providerKey ) const; + QList providersByKey( const QString &providerKey ) const; %Docstring Return a (possibly empty) list of providers by data provider's key :rtype: list of QgsSourceSelectProvider %End -private: - QgsSourceSelectProviderRegistry( const QgsSourceSelectProviderRegistry &rh ); + private: + QgsSourceSelectProviderRegistry( const QgsSourceSelectProviderRegistry &rh ); }; /************************************************************************ From 14ce8b5bc09228e49b51b7754495ef16fb39941e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 2 Sep 2017 12:46:08 +1000 Subject: [PATCH 265/364] [composer] Fix node based items (line/polygon) ignore exclude from export setting Fixes #17086 (forward port from 89c38ed) --- src/core/composer/qgscomposernodesitem.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/composer/qgscomposernodesitem.cpp b/src/core/composer/qgscomposernodesitem.cpp index 722f4b15848..6287b353c72 100644 --- a/src/core/composer/qgscomposernodesitem.cpp +++ b/src/core/composer/qgscomposernodesitem.cpp @@ -189,6 +189,11 @@ void QgsComposerNodesItem::paint( QPainter *painter, if ( !painter ) return; + if ( !shouldDrawItem() ) + { + return; + } + painter->save(); painter->setPen( Qt::NoPen ); painter->setBrush( Qt::NoBrush ); From 64489be468eff4362c2071ecf7d56776f0fe4700 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Sep 2017 09:11:34 +1000 Subject: [PATCH 266/364] Fix QSocketNotifier error on startup And avoid unnecessary creation of unused QFileSystemWatchers --- python/core/qgsuserprofilemanager.sip | 30 ++++++++++++++++++++- src/app/qgisapp.cpp | 1 + src/core/qgsuserprofilemanager.cpp | 38 ++++++++++++++++++--------- src/core/qgsuserprofilemanager.h | 30 ++++++++++++++++++++- 4 files changed, 84 insertions(+), 15 deletions(-) diff --git a/python/core/qgsuserprofilemanager.sip b/python/core/qgsuserprofilemanager.sip index 8380a3e8558..b39eadbfc9f 100644 --- a/python/core/qgsuserprofilemanager.sip +++ b/python/core/qgsuserprofilemanager.sip @@ -72,6 +72,28 @@ class QgsUserProfileManager : QObject :rtype: str %End + void setNewProfileNotificationEnabled( bool enabled ); +%Docstring + Sets whether the manager should watch for the creation of new user profiles and emit + the profilesChanged() signal when this occurs. By default new profile notification + is disabled. + + Before calling this, ensure that the correct root location has been set via + calling setRootLocation(). + +.. seealso:: isNewProfileNotificationEnabled() +%End + + bool isNewProfileNotificationEnabled() const; +%Docstring + Returns whether the manager is watching for the creation of new user profiles and emitting + the profilesChanged() signal when this occurs. By default new profile notification + is disabled. + +.. seealso:: setNewProfileNotificationEnabled() + :rtype: bool +%End + bool rootLocationIsSet() const; %Docstring Check if the root location has been set for the manager. @@ -169,9 +191,15 @@ class QgsUserProfileManager : QObject signals: - void profilesChanged( ); + void profilesChanged(); %Docstring Emitted when the list of profiles is changed. + + This signal will only be emitted when isNewProfileNotificationEnabled() is true. + By default By default new profile notification is disabled. + +.. seealso:: isNewProfileNotificationEnabled() +.. seealso:: setNewProfileNotificationEnabled() %End }; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 7c2a2dc48f8..8f89c058317 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -655,6 +655,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh mUserProfileManager = new QgsUserProfileManager( QString(), this ); mUserProfileManager->setRootLocation( rootProfileLocation ); mUserProfileManager->setActiveUserProfile( activeProfile ); + mUserProfileManager->setNewProfileNotificationEnabled( true ); connect( mUserProfileManager, &QgsUserProfileManager::profilesChanged, this, &QgisApp::refreshProfileMenu ); endProfile(); diff --git a/src/core/qgsuserprofilemanager.cpp b/src/core/qgsuserprofilemanager.cpp index 24ed32d312a..139b8626bfd 100644 --- a/src/core/qgsuserprofilemanager.cpp +++ b/src/core/qgsuserprofilemanager.cpp @@ -29,12 +29,7 @@ QgsUserProfileManager::QgsUserProfileManager( const QString &rootLocation, QObject *parent ) : QObject( parent ) { - mWatcher.reset( new QFileSystemWatcher() ); setRootLocation( rootLocation ); - connect( mWatcher.get(), &QFileSystemWatcher::directoryChanged, this, [this] - { - emit profilesChanged(); - } ); } QString QgsUserProfileManager::resolveProfilesFolder( const QString &basePath ) @@ -62,18 +57,35 @@ void QgsUserProfileManager::setRootLocation( const QString &rootProfileLocation { mRootProfilePath = rootProfileLocation; - if ( !mWatcher->directories().isEmpty() ) - { - mWatcher->removePaths( mWatcher->directories() ); - } + //updates (or removes) profile file watcher for new root location + setNewProfileNotificationEnabled( mWatchProfiles ); - if ( !mRootProfilePath.isEmpty() && QDir( mRootProfilePath ).exists() ) - { - mWatcher->addPath( mRootProfilePath ); - } mSettings.reset( new QSettings( settingsFile(), QSettings::IniFormat ) ); } +void QgsUserProfileManager::setNewProfileNotificationEnabled( bool enabled ) +{ + mWatchProfiles = enabled; + if ( mWatchProfiles && !mRootProfilePath.isEmpty() && QDir( mRootProfilePath ).exists() ) + { + mWatcher.reset( new QFileSystemWatcher() ); + mWatcher->addPath( mRootProfilePath ); + connect( mWatcher.get(), &QFileSystemWatcher::directoryChanged, this, [this] + { + emit profilesChanged(); + } ); + } + else + { + mWatcher.reset(); + } +} + +bool QgsUserProfileManager::isNewProfileNotificationEnabled() const +{ + return static_cast< bool >( mWatcher.get() ); +} + bool QgsUserProfileManager::rootLocationIsSet() const { return !mRootProfilePath.isEmpty(); diff --git a/src/core/qgsuserprofilemanager.h b/src/core/qgsuserprofilemanager.h index fe238e36929..843e9099266 100644 --- a/src/core/qgsuserprofilemanager.h +++ b/src/core/qgsuserprofilemanager.h @@ -81,6 +81,27 @@ class CORE_EXPORT QgsUserProfileManager : public QObject */ QString rootLocation() { return mRootProfilePath; } + /** + * Sets whether the manager should watch for the creation of new user profiles and emit + * the profilesChanged() signal when this occurs. By default new profile notification + * is disabled. + * + * Before calling this, ensure that the correct root location has been set via + * calling setRootLocation(). + * + * \see isNewProfileNotificationEnabled() + */ + void setNewProfileNotificationEnabled( bool enabled ); + + /** + * Returns whether the manager is watching for the creation of new user profiles and emitting + * the profilesChanged() signal when this occurs. By default new profile notification + * is disabled. + * + * \see setNewProfileNotificationEnabled() + */ + bool isNewProfileNotificationEnabled() const; + /** * Check if the root location has been set for the manager. * \return True if the root location has been set. @@ -168,11 +189,18 @@ class CORE_EXPORT QgsUserProfileManager : public QObject /** * Emitted when the list of profiles is changed. + * + * This signal will only be emitted when isNewProfileNotificationEnabled() is true. + * By default By default new profile notification is disabled. + * + * \see isNewProfileNotificationEnabled() + * \see setNewProfileNotificationEnabled() */ - void profilesChanged( ); + void profilesChanged(); private: + bool mWatchProfiles = false; std::unique_ptr mWatcher; QString mRootProfilePath; From bbd0beb50c0b4f62f62a38525ba2623fdec09de5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sat, 2 Sep 2017 16:12:51 +1000 Subject: [PATCH 267/364] Fix bad doc string [ci skip] --- python/core/qgsuserprofilemanager.sip | 2 +- src/core/qgsuserprofilemanager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/core/qgsuserprofilemanager.sip b/python/core/qgsuserprofilemanager.sip index b39eadbfc9f..2f7dd0c679f 100644 --- a/python/core/qgsuserprofilemanager.sip +++ b/python/core/qgsuserprofilemanager.sip @@ -196,7 +196,7 @@ class QgsUserProfileManager : QObject Emitted when the list of profiles is changed. This signal will only be emitted when isNewProfileNotificationEnabled() is true. - By default By default new profile notification is disabled. + By default new profile notification is disabled. .. seealso:: isNewProfileNotificationEnabled() .. seealso:: setNewProfileNotificationEnabled() diff --git a/src/core/qgsuserprofilemanager.h b/src/core/qgsuserprofilemanager.h index 843e9099266..567a2fe4311 100644 --- a/src/core/qgsuserprofilemanager.h +++ b/src/core/qgsuserprofilemanager.h @@ -191,7 +191,7 @@ class CORE_EXPORT QgsUserProfileManager : public QObject * Emitted when the list of profiles is changed. * * This signal will only be emitted when isNewProfileNotificationEnabled() is true. - * By default By default new profile notification is disabled. + * By default new profile notification is disabled. * * \see isNewProfileNotificationEnabled() * \see setNewProfileNotificationEnabled() From f51244c98b774cf0d78e0021cfbe32180464f97f Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Fri, 1 Sep 2017 15:46:09 +0200 Subject: [PATCH 268/364] Add Help button and connect it to user manual --- .../gui/symbology/qgssymbollevelsdialog.sip | 1 - src/app/openstreetmap/qgsosmexportdialog.cpp | 6 +++ src/app/openstreetmap/qgsosmexportdialog.h | 2 + src/app/openstreetmap/qgsosmimportdialog.cpp | 6 +++ src/app/openstreetmap/qgsosmimportdialog.h | 2 + src/app/qgsaddtaborgroup.cpp | 6 +++ src/app/qgsaddtaborgroup.h | 2 + src/app/qgsdiagramproperties.cpp | 8 +++- src/app/qgsdiagramproperties.h | 1 + src/app/qgssavestyletodbdialog.cpp | 7 +++ src/app/qgssavestyletodbdialog.h | 5 +++ src/gui/qgscolordialog.cpp | 6 +++ src/gui/qgscolordialog.h | 2 + src/gui/qgsexpressionbuilderdialog.cpp | 7 +++ src/gui/qgsexpressionbuilderdialog.h | 5 +++ src/gui/qgsexpressionselectiondialog.cpp | 7 +++ src/gui/qgsexpressionselectiondialog.h | 2 + src/gui/qgsgradientcolorrampdialog.cpp | 7 +++ src/gui/qgsgradientcolorrampdialog.h | 2 + src/gui/qgsnewmemorylayerdialog.cpp | 7 +++ src/gui/qgsnewmemorylayerdialog.h | 5 +++ src/gui/qgsorderbydialog.cpp | 6 +++ src/gui/qgsorderbydialog.h | 2 + src/gui/qgsrasterlayersaveasdialog.cpp | 6 +++ src/gui/qgsrasterlayersaveasdialog.h | 5 ++- .../symbology/qgsstyleexportimportdialog.cpp | 7 +++ .../symbology/qgsstyleexportimportdialog.h | 2 + src/gui/symbology/qgssymbollevelsdialog.cpp | 6 +++ src/gui/symbology/qgssymbollevelsdialog.h | 5 ++- src/gui/symbology/qgssymbolselectordialog.cpp | 12 ++++- src/gui/symbology/qgssymbolselectordialog.h | 4 ++ .../ui/qgsgeometrycheckerdialog.cpp | 8 +++- .../ui/qgsgeometrycheckerdialog.h | 3 ++ .../spatialquery/qgsspatialquerydialog.cpp | 8 ++++ .../spatialquery/qgsspatialquerydialog.h | 2 + .../spatialquery/qgsspatialquerydialogbase.ui | 2 +- src/providers/wcs/qgswcssourceselect.cpp | 8 ++++ src/providers/wcs/qgswcssourceselect.h | 7 +++ src/ui/qgsaddtaborgroupbase.ui | 20 ++++----- src/ui/qgscolordialog.ui | 17 ++++--- src/ui/qgsexpressionbuilderdialogbase.ui | 2 +- src/ui/qgsexpressionselectiondialogbase.ui | 44 +++++++++++++------ src/ui/qgsgradientcolorrampdialogbase.ui | 2 +- src/ui/qgsnewmemorylayerdialogbase.ui | 2 +- src/ui/qgsorderbydialogbase.ui | 2 +- src/ui/qgsosmexportdialog.ui | 2 +- src/ui/qgsosmimportdialog.ui | 4 +- src/ui/qgsrasterlayersaveasdialogbase.ui | 2 +- src/ui/qgssavetodbdialog.ui | 20 ++++----- src/ui/qgsstyleexportimportdialogbase.ui | 4 +- src/ui/qgssymbollevelsdialogbase.ui | 2 +- 51 files changed, 250 insertions(+), 62 deletions(-) diff --git a/python/gui/symbology/qgssymbollevelsdialog.sip b/python/gui/symbology/qgssymbollevelsdialog.sip index ab9d1a4634d..518cbe39e92 100644 --- a/python/gui/symbology/qgssymbollevelsdialog.sip +++ b/python/gui/symbology/qgssymbollevelsdialog.sip @@ -8,7 +8,6 @@ - class QgsSymbolLevelsDialog : QDialog { diff --git a/src/app/openstreetmap/qgsosmexportdialog.cpp b/src/app/openstreetmap/qgsosmexportdialog.cpp index 74fa3d4a16d..ddb89f43b3f 100644 --- a/src/app/openstreetmap/qgsosmexportdialog.cpp +++ b/src/app/openstreetmap/qgsosmexportdialog.cpp @@ -36,6 +36,7 @@ QgsOSMExportDialog::QgsOSMExportDialog( QWidget *parent ) connect( btnBrowseDb, &QAbstractButton::clicked, this, &QgsOSMExportDialog::onBrowse ); connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsOSMExportDialog::onOK ); connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsOSMExportDialog::onClose ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsOSMExportDialog::showHelp ); connect( editDbFileName, &QLineEdit::textChanged, this, &QgsOSMExportDialog::updateLayerName ); connect( radPoints, &QAbstractButton::clicked, this, &QgsOSMExportDialog::updateLayerName ); connect( radPolylines, &QAbstractButton::clicked, this, &QgsOSMExportDialog::updateLayerName ); @@ -212,3 +213,8 @@ void QgsOSMExportDialog::onDeselectAll() mTagsModel->item( i, 0 )->setCheckState( Qt::Unchecked ); } } + +void QgsOSMExportDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html#importing-openstreetmap-vectors" ) ); +} diff --git a/src/app/openstreetmap/qgsosmexportdialog.h b/src/app/openstreetmap/qgsosmexportdialog.h index 0295ea0e04c..9664dabf646 100644 --- a/src/app/openstreetmap/qgsosmexportdialog.h +++ b/src/app/openstreetmap/qgsosmexportdialog.h @@ -19,6 +19,7 @@ #include #include "ui_qgsosmexportdialog.h" +#include "qgshelp.h" class QgsOSMDatabase; @@ -43,6 +44,7 @@ class QgsOSMExportDialog : public QDialog, private Ui::QgsOSMExportDialog void onOK(); void onClose(); + void showHelp(); private: QgsOSMDatabase *mDatabase = nullptr; diff --git a/src/app/openstreetmap/qgsosmimportdialog.cpp b/src/app/openstreetmap/qgsosmimportdialog.cpp index cbf18f19877..92d3e496d00 100644 --- a/src/app/openstreetmap/qgsosmimportdialog.cpp +++ b/src/app/openstreetmap/qgsosmimportdialog.cpp @@ -34,6 +34,7 @@ QgsOSMImportDialog::QgsOSMImportDialog( QWidget *parent ) connect( editDbFileName, &QLineEdit::textChanged, this, &QgsOSMImportDialog::dbFileNameChanged ); connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsOSMImportDialog::onOK ); connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsOSMImportDialog::onClose ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsOSMImportDialog::showHelp ); connect( mImport, &QgsOSMXmlImport::progress, this, &QgsOSMImportDialog::onProgress ); } @@ -130,3 +131,8 @@ void QgsOSMImportDialog::onProgress( int percent ) progressBar->setValue( percent ); qApp->processEvents( QEventLoop::ExcludeSocketNotifiers ); } + +void QgsOSMImportDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html#importing-openstreetmap-vectors" ) ); +} diff --git a/src/app/openstreetmap/qgsosmimportdialog.h b/src/app/openstreetmap/qgsosmimportdialog.h index 9ff2282bbe6..aa02a74247a 100644 --- a/src/app/openstreetmap/qgsosmimportdialog.h +++ b/src/app/openstreetmap/qgsosmimportdialog.h @@ -19,6 +19,7 @@ #include #include "ui_qgsosmimportdialog.h" +#include "qgshelp.h" class QgsOSMXmlImport; @@ -38,6 +39,7 @@ class QgsOSMImportDialog : public QDialog, private Ui::QgsOSMImportDialog void onOK(); void onClose(); + void showHelp(); void onProgress( int percent ); diff --git a/src/app/qgsaddtaborgroup.cpp b/src/app/qgsaddtaborgroup.cpp index 4fd74c7ff09..a1df918e4ca 100644 --- a/src/app/qgsaddtaborgroup.cpp +++ b/src/app/qgsaddtaborgroup.cpp @@ -51,6 +51,7 @@ QgsAddTabOrGroup::QgsAddTabOrGroup( QgsVectorLayer *lyr, const QList < TabPair > connect( mTabButton, &QAbstractButton::toggled, this, &QgsAddTabOrGroup::on_mTabButton_toggled ); connect( mGroupButton, &QAbstractButton::toggled, this, &QgsAddTabOrGroup::on_mGroupButton_toggled ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsAddTabOrGroup::showHelp ); mColumnCountSpinBox->setValue( QgsSettings().value( QStringLiteral( "/qgis/attributeForm/defaultTabColumnCount" ), 1 ).toInt() ); @@ -115,3 +116,8 @@ void QgsAddTabOrGroup::on_mTabButton_toggled( bool checked ) if ( checked ) mColumnCountSpinBox->setValue( QgsSettings().value( QStringLiteral( "/qgis/attributeForm/defaultTabColumnCount" ), 1 ).toInt() ); } + +void QgsAddTabOrGroup::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#customize-a-form-for-your-data" ) ); +} diff --git a/src/app/qgsaddtaborgroup.h b/src/app/qgsaddtaborgroup.h index 68973689238..7f955def035 100644 --- a/src/app/qgsaddtaborgroup.h +++ b/src/app/qgsaddtaborgroup.h @@ -22,6 +22,7 @@ #include "ui_qgsaddtaborgroupbase.h" #include "qgsguiutils.h" #include "qgis_app.h" +#include "qgshelp.h" class QTreeWidgetItem; class QgsVectorLayer; @@ -50,6 +51,7 @@ class APP_EXPORT QgsAddTabOrGroup : public QDialog, private Ui::QgsAddTabOrGroup private slots: void on_mGroupButton_toggled( bool checked ); void on_mTabButton_toggled( bool checked ); + void showHelp(); protected: QgsVectorLayer *mLayer = nullptr; diff --git a/src/app/qgsdiagramproperties.cpp b/src/app/qgsdiagramproperties.cpp index c558d87277f..425fd0ec5fe 100644 --- a/src/app/qgsdiagramproperties.cpp +++ b/src/app/qgsdiagramproperties.cpp @@ -950,9 +950,15 @@ void QgsDiagramProperties::showSizeLegendDialog() dlg.setLayout( new QVBoxLayout() ); dlg.setWindowTitle( panel->panelTitle() ); dlg.layout()->addWidget( panel ); - QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok ); + QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Help | QDialogButtonBox::Ok ); connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsDiagramProperties::showHelp ); dlg.layout()->addWidget( buttonBox ); if ( dlg.exec() ) mSizeLegend.reset( panel->dataDefinedSizeLegend() ); } + +void QgsDiagramProperties::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#legend" ) ); +} diff --git a/src/app/qgsdiagramproperties.h b/src/app/qgsdiagramproperties.h index 466f810b174..d1748bcb9f1 100644 --- a/src/app/qgsdiagramproperties.h +++ b/src/app/qgsdiagramproperties.h @@ -90,6 +90,7 @@ class APP_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr private slots: void updateProperty(); + void showHelp(); }; class EditBlockerDelegate: public QStyledItemDelegate diff --git a/src/app/qgssavestyletodbdialog.cpp b/src/app/qgssavestyletodbdialog.cpp index 2e4588ce06a..9290b75e393 100644 --- a/src/app/qgssavestyletodbdialog.cpp +++ b/src/app/qgssavestyletodbdialog.cpp @@ -34,6 +34,8 @@ QgsSaveStyleToDbDialog::QgsSaveStyleToDbDialog( QWidget *parent ) QgsSettings settings; restoreGeometry( settings.value( QStringLiteral( "Windows/saveStyleToDb/geometry" ) ).toByteArray() ); + + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsSaveStyleToDbDialog::showHelp ); } QgsSaveStyleToDbDialog::~QgsSaveStyleToDbDialog() @@ -103,3 +105,8 @@ void QgsSaveStyleToDbDialog::on_mFilePickButton_clicked() mFileNameLabel->setText( myFI.fileName() ); } } + +void QgsSaveStyleToDbDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#save-and-share-layer-properties" ) ); +} diff --git a/src/app/qgssavestyletodbdialog.h b/src/app/qgssavestyletodbdialog.h index 44d1311dde1..51dfb215dc4 100644 --- a/src/app/qgssavestyletodbdialog.h +++ b/src/app/qgssavestyletodbdialog.h @@ -19,6 +19,7 @@ #include "ui_qgssavetodbdialog.h" #include "qgsguiutils.h" #include "qgis_app.h" +#include "qgshelp.h" class APP_EXPORT QgsSaveStyleToDbDialog : public QDialog, private Ui::QgsSaveToDBDialog { @@ -38,6 +39,10 @@ class APP_EXPORT QgsSaveStyleToDbDialog : public QDialog, private Ui::QgsSaveToD bool isDefault(); void on_mFilePickButton_clicked(); void accept() override; + + private slots: + void showHelp(); + }; #endif // QGSSAVESTYLETODBDIALOG_H diff --git a/src/gui/qgscolordialog.cpp b/src/gui/qgscolordialog.cpp index 06628f91b38..ec33712ce47 100644 --- a/src/gui/qgscolordialog.cpp +++ b/src/gui/qgscolordialog.cpp @@ -57,6 +57,7 @@ QgsColorDialog::QgsColorDialog( QWidget *parent, Qt::WindowFlags fl, const QColo connect( mColorWidget, &QgsCompoundColorWidget::currentColorChanged, this, &QgsColorDialog::currentColorChanged ); connect( this, &QDialog::rejected, this, &QgsColorDialog::discardColor ); + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsColorDialog::showHelp ); } QColor QgsColorDialog::color() const @@ -205,3 +206,8 @@ void QgsColorDialog::closeEvent( QCloseEvent *e ) saveSettings(); QDialog::closeEvent( e ); } + +void QgsColorDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#color-selector" ) ); +} diff --git a/src/gui/qgscolordialog.h b/src/gui/qgscolordialog.h index a2c9f706f0d..a896b3eb459 100644 --- a/src/gui/qgscolordialog.h +++ b/src/gui/qgscolordialog.h @@ -21,6 +21,7 @@ #include "ui_qgscolordialog.h" #include "qgis_gui.h" #include "qgis.h" +#include "qgshelp.h" class QColor; @@ -113,6 +114,7 @@ class GUI_EXPORT QgsColorDialog : public QDialog, private Ui::QgsColorDialogBase void on_mButtonBox_rejected(); void on_mButtonBox_clicked( QAbstractButton *button ); void discardColor(); + void showHelp(); private: diff --git a/src/gui/qgsexpressionbuilderdialog.cpp b/src/gui/qgsexpressionbuilderdialog.cpp index 35bd00a8970..8e65f714153 100644 --- a/src/gui/qgsexpressionbuilderdialog.cpp +++ b/src/gui/qgsexpressionbuilderdialog.cpp @@ -33,6 +33,8 @@ QgsExpressionBuilderDialog::QgsExpressionBuilderDialog( QgsVectorLayer *layer, c QgsSettings settings; restoreGeometry( settings.value( QStringLiteral( "Windows/ExpressionBuilderDialog/geometry" ) ).toByteArray() ); + + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsExpressionBuilderDialog::showHelp ); } QgsExpressionBuilderWidget *QgsExpressionBuilderDialog::expressionBuilder() @@ -79,3 +81,8 @@ void QgsExpressionBuilderDialog::setGeomCalculator( const QgsDistanceArea &da ) // Store in child widget only. builder->setGeomCalculator( da ); } + +void QgsExpressionBuilderDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/expression.html" ) ); +} diff --git a/src/gui/qgsexpressionbuilderdialog.h b/src/gui/qgsexpressionbuilderdialog.h index 9076033918c..e2a1d33d66f 100644 --- a/src/gui/qgsexpressionbuilderdialog.h +++ b/src/gui/qgsexpressionbuilderdialog.h @@ -18,6 +18,7 @@ #include #include "ui_qgsexpressionbuilderdialogbase.h" +#include "qgshelp.h" #include "qgis_gui.h" @@ -75,6 +76,10 @@ class GUI_EXPORT QgsExpressionBuilderDialog : public QDialog, private Ui::QgsExp private: QString mRecentKey; + + private slots: + void showHelp(); + }; #endif diff --git a/src/gui/qgsexpressionselectiondialog.cpp b/src/gui/qgsexpressionselectiondialog.cpp index 85e96020e2b..3b995c7d892 100644 --- a/src/gui/qgsexpressionselectiondialog.cpp +++ b/src/gui/qgsexpressionselectiondialog.cpp @@ -57,6 +57,8 @@ QgsExpressionSelectionDialog::QgsExpressionSelectionDialog( QgsVectorLayer *laye QgsSettings settings; restoreGeometry( settings.value( QStringLiteral( "Windows/ExpressionSelectionDialog/geometry" ) ).toByteArray() ); + + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsExpressionSelectionDialog::showHelp ); } QgsExpressionBuilderWidget *QgsExpressionSelectionDialog::expressionBuilder() @@ -194,3 +196,8 @@ void QgsExpressionSelectionDialog::saveRecent() { mExpressionBuilder->saveToRecent( QStringLiteral( "Selection" ) ); } + +void QgsExpressionSelectionDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#automatic-selection" ) ); +} diff --git a/src/gui/qgsexpressionselectiondialog.h b/src/gui/qgsexpressionselectiondialog.h index 62cf9575b74..a6f060ba626 100644 --- a/src/gui/qgsexpressionselectiondialog.h +++ b/src/gui/qgsexpressionselectiondialog.h @@ -21,6 +21,7 @@ #include "qgsmapcanvas.h" #include "qgsmessagebar.h" +#include "qgshelp.h" #include #include "qgis_gui.h" @@ -88,6 +89,7 @@ class GUI_EXPORT QgsExpressionSelectionDialog : public QDialog, private Ui::QgsE void on_mActionSelectIntersect_triggered(); void on_mButtonZoomToFeatures_clicked(); void on_mPbnClose_clicked(); + void showHelp(); protected: diff --git a/src/gui/qgsgradientcolorrampdialog.cpp b/src/gui/qgsgradientcolorrampdialog.cpp index 3e892fe37f8..3d4433be6bd 100644 --- a/src/gui/qgsgradientcolorrampdialog.cpp +++ b/src/gui/qgsgradientcolorrampdialog.cpp @@ -146,6 +146,8 @@ QgsGradientColorRampDialog::QgsGradientColorRampDialog( const QgsGradientColorRa connect( mStopEditor, &QgsGradientStopEditor::selectedStopChanged, this, &QgsGradientColorRampDialog::selectedStopChanged ); mStopEditor->selectStop( 0 ); + + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsGradientColorRampDialog::showHelp ); } QgsGradientColorRampDialog::~QgsGradientColorRampDialog() @@ -563,6 +565,11 @@ void QgsGradientColorRampDialog::setColor2( const QColor &color ) updateColorButtons(); } +void QgsGradientColorRampDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/style_library.html#color-ramp" ) ); +} + /// @cond PRIVATE diff --git a/src/gui/qgsgradientcolorrampdialog.h b/src/gui/qgsgradientcolorrampdialog.h index a80361bde9a..abd2111a17c 100644 --- a/src/gui/qgsgradientcolorrampdialog.h +++ b/src/gui/qgsgradientcolorrampdialog.h @@ -20,6 +20,7 @@ #include "qgis.h" #include "ui_qgsgradientcolorrampdialogbase.h" +#include "qgshelp.h" #include "qgis_gui.h" class QgsGradientColorRamp; @@ -91,6 +92,7 @@ class GUI_EXPORT QgsGradientColorRampDialog : public QDialog, private Ui::QgsGra void plotMousePress( QPointF point ); void plotMouseRelease( QPointF point ); void plotMouseMove( QPointF point ); + void showHelp(); private: diff --git a/src/gui/qgsnewmemorylayerdialog.cpp b/src/gui/qgsnewmemorylayerdialog.cpp index dee7b5dc01b..66e3546d082 100644 --- a/src/gui/qgsnewmemorylayerdialog.cpp +++ b/src/gui/qgsnewmemorylayerdialog.cpp @@ -56,6 +56,8 @@ QgsNewMemoryLayerDialog::QgsNewMemoryLayerDialog( QWidget *parent, Qt::WindowFla mPointRadioButton->setChecked( true ); mNameLineEdit->setText( tr( "New scratch layer" ) ); + + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsNewMemoryLayerDialog::showHelp ); } QgsNewMemoryLayerDialog::~QgsNewMemoryLayerDialog() @@ -116,3 +118,8 @@ QString QgsNewMemoryLayerDialog::layerName() const { return mNameLineEdit->text(); } + +void QgsNewMemoryLayerDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-temporary-scratch-layer" ) ); +} diff --git a/src/gui/qgsnewmemorylayerdialog.h b/src/gui/qgsnewmemorylayerdialog.h index fc389d40eee..2c2292d7d69 100644 --- a/src/gui/qgsnewmemorylayerdialog.h +++ b/src/gui/qgsnewmemorylayerdialog.h @@ -20,6 +20,7 @@ #include "ui_qgsnewmemorylayerdialogbase.h" #include "qgsguiutils.h" #include "qgis.h" +#include "qgshelp.h" #include "qgis_gui.h" class QgsVectorLayer; @@ -65,6 +66,10 @@ class GUI_EXPORT QgsNewMemoryLayerDialog: public QDialog, private Ui::QgsNewMemo private: QString mCrsId; + + private slots: + + void showHelp(); }; #endif //QGSNEWMEMORYLAYERDIALOG_H diff --git a/src/gui/qgsorderbydialog.cpp b/src/gui/qgsorderbydialog.cpp index 1a666020b7f..efddb60c5c6 100644 --- a/src/gui/qgsorderbydialog.cpp +++ b/src/gui/qgsorderbydialog.cpp @@ -34,6 +34,8 @@ QgsOrderByDialog::QgsOrderByDialog( QgsVectorLayer *layer, QWidget *parent ) mOrderByTableWidget->horizontalHeader()->setResizeMode( 2, QHeaderView::ResizeToContents ); mOrderByTableWidget->installEventFilter( this ); + + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsOrderByDialog::showHelp ); } void QgsOrderByDialog::setOrderBy( const QgsFeatureRequest::OrderBy &orderBy ) @@ -150,3 +152,7 @@ bool QgsOrderByDialog::eventFilter( QObject *obj, QEvent *e ) return false; } +void QgsOrderByDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#layer-rendering" ) ); +} diff --git a/src/gui/qgsorderbydialog.h b/src/gui/qgsorderbydialog.h index e5b11b81b94..f28c64add45 100644 --- a/src/gui/qgsorderbydialog.h +++ b/src/gui/qgsorderbydialog.h @@ -22,6 +22,7 @@ #include "ui_qgsorderbydialogbase.h" #include "qgsfeaturerequest.h" +#include "qgshelp.h" #include "qgis_gui.h" class QgsVectorLayer; @@ -62,6 +63,7 @@ class GUI_EXPORT QgsOrderByDialog : public QDialog, private Ui::OrderByDialogBas private slots: void onExpressionChanged( const QString &expression ); + void showHelp(); private: diff --git a/src/gui/qgsrasterlayersaveasdialog.cpp b/src/gui/qgsrasterlayersaveasdialog.cpp index 69611dffe9c..1096a429c24 100644 --- a/src/gui/qgsrasterlayersaveasdialog.cpp +++ b/src/gui/qgsrasterlayersaveasdialog.cpp @@ -137,6 +137,8 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa okButton->setEnabled( false ); } + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterLayerSaveAsDialog::showHelp ); + mExtentGroupBox->setOutputCrs( outputCrs() ); mExtentGroupBox->setOriginalExtent( mDataProvider->extent(), mLayerCrs ); mExtentGroupBox->setCurrentExtent( mCurrentExtent, mCurrentCrs ); @@ -737,3 +739,7 @@ bool QgsRasterLayerSaveAsDialog::validate() const return true; } +void QgsRasterLayerSaveAsDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#save-layer-from-an-existing-file" ) ); +} diff --git a/src/gui/qgsrasterlayersaveasdialog.h b/src/gui/qgsrasterlayersaveasdialog.h index b763018f086..d6ff4cbbe1c 100644 --- a/src/gui/qgsrasterlayersaveasdialog.h +++ b/src/gui/qgsrasterlayersaveasdialog.h @@ -20,6 +20,7 @@ #include "qgscoordinatereferencesystem.h" #include "qgsrasterrange.h" #include "qgis_gui.h" +#include "qgshelp.h" class QgsRasterLayer; class QgsRasterDataProvider; @@ -108,6 +109,8 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast void on_mPyramidsGroupBox_toggled( bool toggled ); void populatePyramidsLevels(); void extentChanged(); + void crsChanged(); + void showHelp(); private: QgsRasterLayer *mRasterLayer = nullptr; @@ -135,8 +138,6 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast void adjustNoDataCellWidth( int row, int column ); bool validate() const; - private slots: - void crsChanged(); }; diff --git a/src/gui/symbology/qgsstyleexportimportdialog.cpp b/src/gui/symbology/qgsstyleexportimportdialog.cpp index 895a9cc53d8..8f80bab6d80 100644 --- a/src/gui/symbology/qgsstyleexportimportdialog.cpp +++ b/src/gui/symbology/qgsstyleexportimportdialog.cpp @@ -113,6 +113,8 @@ QgsStyleExportImportDialog::QgsStyleExportImportDialog( QgsStyle *style, QWidget disconnect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept ); connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsStyleExportImportDialog::doExportImport ); buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); + + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsStyleExportImportDialog::showHelp ); } void QgsStyleExportImportDialog::doExportImport() @@ -585,3 +587,8 @@ void QgsStyleExportImportDialog::selectionChanged( const QItemSelection &selecte bool nothingSelected = listItems->selectionModel()->selectedIndexes().empty(); buttonBox->button( QDialogButtonBox::Ok )->setDisabled( nothingSelected ); } + +void QgsStyleExportImportDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/style_library.html#share-symbols" ) ); +} diff --git a/src/gui/symbology/qgsstyleexportimportdialog.h b/src/gui/symbology/qgsstyleexportimportdialog.h index 76189704e5b..ba0ff0f98de 100644 --- a/src/gui/symbology/qgsstyleexportimportdialog.h +++ b/src/gui/symbology/qgsstyleexportimportdialog.h @@ -28,6 +28,7 @@ #include "ui_qgsstyleexportimportdialogbase.h" #include "qgis_gui.h" #include "qgis_sip.h" +#include "qgshelp.h" class QgsStyle; class QgsStyleGroupSelectionDialog; @@ -114,6 +115,7 @@ class GUI_EXPORT QgsStyleExportImportDialog : public QDialog, private Ui::QgsSty void updateProgress( qint64, qint64 ); void downloadCanceled(); void selectionChanged( const QItemSelection &selected, const QItemSelection &deselected ); + void showHelp(); private: void downloadStyleXml( const QUrl &url ); diff --git a/src/gui/symbology/qgssymbollevelsdialog.cpp b/src/gui/symbology/qgssymbollevelsdialog.cpp index f58d45ebb2e..ee8c1394844 100644 --- a/src/gui/symbology/qgssymbollevelsdialog.cpp +++ b/src/gui/symbology/qgssymbollevelsdialog.cpp @@ -43,6 +43,7 @@ QgsSymbolLevelsDialog::QgsSymbolLevelsDialog( const QgsLegendSymbolList &list, b chkEnable->setChecked( usingSymbolLevels ); connect( chkEnable, &QAbstractButton::clicked, this, &QgsSymbolLevelsDialog::updateUi ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolLevelsDialog::showHelp ); // only consider entries with symbols Q_FOREACH ( const QgsLegendSymbolItem &item, list ) @@ -167,6 +168,11 @@ void QgsSymbolLevelsDialog::setForceOrderingEnabled( bool enabled ) chkEnable->show(); } +void QgsSymbolLevelsDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#symbols-levels" ) ); +} + /// @cond PRIVATE diff --git a/src/gui/symbology/qgssymbollevelsdialog.h b/src/gui/symbology/qgssymbollevelsdialog.h index 08b8a71581c..55c9352b5a2 100644 --- a/src/gui/symbology/qgssymbollevelsdialog.h +++ b/src/gui/symbology/qgssymbollevelsdialog.h @@ -21,7 +21,7 @@ #include #include "qgsrenderer.h" - +#include "qgshelp.h" #include "ui_qgssymbollevelsdialogbase.h" #include "qgis_gui.h" @@ -47,6 +47,9 @@ class GUI_EXPORT QgsSymbolLevelsDialog : public QDialog, private Ui::QgsSymbolLe void renderingPassChanged( int row, int column ); + private slots: + void showHelp(); + protected: //! \note not available in Python bindings void populateTable() SIP_SKIP; diff --git a/src/gui/symbology/qgssymbolselectordialog.cpp b/src/gui/symbology/qgssymbolselectordialog.cpp index c95c2c89a59..e89a87fb70d 100644 --- a/src/gui/symbology/qgssymbolselectordialog.cpp +++ b/src/gui/symbology/qgssymbolselectordialog.cpp @@ -680,10 +680,11 @@ QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *s { setLayout( new QVBoxLayout() ); mSelectorWidget = new QgsSymbolSelectorWidget( symbol, style, vl, this ); - mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); + mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok ); connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept ); connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject ); + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsSymbolSelectorDialog::showHelp ); layout()->addWidget( mSelectorWidget ); layout()->addWidget( mButtonBox ); @@ -697,6 +698,10 @@ QgsSymbolSelectorDialog::QgsSymbolSelectorDialog( QgsSymbol *symbol, QgsStyle *s mButtonBox->hide(); layout()->setContentsMargins( 0, 0, 0, 0 ); } + else + { + setWindowTitle( tr( "Symbol Selector" ) ); + } mSelectorWidget->setDockMode( embedded ); } @@ -833,3 +838,8 @@ void QgsSymbolSelectorDialog::changeLayer( QgsSymbolLayer *layer ) { mSelectorWidget->changeLayer( layer ); } + +void QgsSymbolSelectorDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_vector/style_library.html#the-symbol-selector" ) ); +} diff --git a/src/gui/symbology/qgssymbolselectordialog.h b/src/gui/symbology/qgssymbolselectordialog.h index 97c9910975a..a06b8f82bdd 100644 --- a/src/gui/symbology/qgssymbolselectordialog.h +++ b/src/gui/symbology/qgssymbolselectordialog.h @@ -25,6 +25,7 @@ #include "qgspanelwidget.h" #include "qgssymbolwidgetcontext.h" #include "qgsproperty.h" +#include "qgshelp.h" #include #include @@ -328,6 +329,9 @@ class GUI_EXPORT QgsSymbolSelectorDialog : public QDialog QgsSymbolSelectorWidget *mSelectorWidget = nullptr; QDialogButtonBox *mButtonBox = nullptr; QgsSymbolWidgetContext mContext; + + private slots: + void showHelp(); }; #endif diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp index 4998a4850d3..37be5c7ede5 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp @@ -35,7 +35,7 @@ QgsGeometryCheckerDialog::QgsGeometryCheckerDialog( QgisInterface *iface, QWidge restoreGeometry( s.value( QStringLiteral( "/Plugin-GeometryChecker/Window/geometry" ) ).toByteArray() ); mTabWidget = new QTabWidget(); - mButtonBox = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal ); + mButtonBox = new QDialogButtonBox( QDialogButtonBox::Close | QDialogButtonBox::Help, Qt::Horizontal ); QVBoxLayout *layout = new QVBoxLayout( this ); layout->addWidget( mTabWidget ); @@ -46,6 +46,7 @@ QgsGeometryCheckerDialog::QgsGeometryCheckerDialog( QgisInterface *iface, QWidge mTabWidget->setTabEnabled( 1, false ); connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject ); + connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsGeometryCheckerDialog::showHelp ); connect( dynamic_cast< QgsGeometryCheckerSetupTab * >( mTabWidget->widget( 0 ) ), &QgsGeometryCheckerSetupTab::checkerStarted, this, &QgsGeometryCheckerDialog::onCheckerStarted ); connect( dynamic_cast< QgsGeometryCheckerSetupTab * >( mTabWidget->widget( 0 ) ), &QgsGeometryCheckerSetupTab::checkerFinished, this, &QgsGeometryCheckerDialog::onCheckerFinished ); } @@ -97,3 +98,8 @@ void QgsGeometryCheckerDialog::closeEvent( QCloseEvent *ev ) QDialog::closeEvent( ev ); } } + +void QgsGeometryCheckerDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "plugins/plugins_geometry_checker.html" ) ); +} diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h index 7c3f117be4c..b39b87bac52 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h @@ -17,6 +17,8 @@ #ifndef QGS_GEOMETRY_CHECKER_DIALOG_H #define QGS_GEOMETRY_CHECKER_DIALOG_H +#include "qgshelp.h" + #include #include #include @@ -44,6 +46,7 @@ class QgsGeometryCheckerDialog : public QDialog private slots: void onCheckerStarted( QgsGeometryChecker *checker, QgsFeaturePool *featurePool ); void onCheckerFinished( bool successful ); + void showHelp(); }; #endif // QGS_GEOMETRY_CHECKER_DIALOG_H diff --git a/src/plugins/spatialquery/qgsspatialquerydialog.cpp b/src/plugins/spatialquery/qgsspatialquerydialog.cpp index 1bf2acae248..0528afe7a17 100644 --- a/src/plugins/spatialquery/qgsspatialquerydialog.cpp +++ b/src/plugins/spatialquery/qgsspatialquerydialog.cpp @@ -782,6 +782,9 @@ void QgsSpatialQueryDialog::on_bbMain_clicked( QAbstractButton *button ) case QDialogButtonBox::RejectRole: reject(); break; + case QDialogButtonBox::HelpRole: + showHelp(); + break; default: return; } @@ -1072,3 +1075,8 @@ void QgsSpatialQueryDialog::signal_layerReference_selectionFeaturesChanged() { evaluateCheckBoxLayer( false ); } // void QgsSpatialQueryDialog::signal_layerReference_selectionFeaturesChanged() + +void QgsSpatialQueryDialog::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "plugins/plugins_spatial_query.html" ) ); +} diff --git a/src/plugins/spatialquery/qgsspatialquerydialog.h b/src/plugins/spatialquery/qgsspatialquerydialog.h index 080233ec4c7..da76292069b 100644 --- a/src/plugins/spatialquery/qgsspatialquerydialog.h +++ b/src/plugins/spatialquery/qgsspatialquerydialog.h @@ -24,6 +24,7 @@ #include "ui_qgsspatialquerydialogbase.h" #include "qgisinterface.h" #include "qgsvectorlayer.h" +#include "qgshelp.h" /** * \class QgsSpatialQueryDialog @@ -61,6 +62,7 @@ class QgsSpatialQueryDialog : public QDialog, private Ui::QgsSpatialQueryDialogB void on_ckbUsingSelectedTarget_toggled(); void on_ckbLogProcessing_clicked( bool checked ); void on_ckbZoomItem_clicked( bool checked ); + void showHelp(); //! Slots for signs of QGIS void signal_qgis_layerWasAdded( QgsMapLayer *mapLayer ); diff --git a/src/plugins/spatialquery/qgsspatialquerydialogbase.ui b/src/plugins/spatialquery/qgsspatialquerydialogbase.ui index 5a54bd3e3a3..5acd1f64ea2 100644 --- a/src/plugins/spatialquery/qgsspatialquerydialogbase.ui +++ b/src/plugins/spatialquery/qgsspatialquerydialogbase.ui @@ -404,7 +404,7 @@ p, li { white-space: pre-wrap; } Run query or close the window - QDialogButtonBox::Apply|QDialogButtonBox::Close + QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help diff --git a/src/providers/wcs/qgswcssourceselect.cpp b/src/providers/wcs/qgswcssourceselect.cpp index b6c260af975..b1ddebc6edd 100644 --- a/src/providers/wcs/qgswcssourceselect.cpp +++ b/src/providers/wcs/qgswcssourceselect.cpp @@ -40,6 +40,8 @@ QgsWCSSourceSelect::QgsWCSSourceSelect( QWidget *parent, Qt::WindowFlags fl, Qgs mAddDefaultButton->hide(); mLayersTreeWidget->setSelectionMode( QAbstractItemView::SingleSelection ); + + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsWCSSourceSelect::showHelp ); } QgsWCSSourceSelect::~QgsWCSSourceSelect() @@ -255,3 +257,9 @@ void QgsWCSSourceSelect::enableLayersForCrs( QTreeWidgetItem * ) { // TODO: I am not convinced to disable layers according to selected CRS } + + +void QgsWCSSourceSelect::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "working_with_ogc/ogc_client_support.html" ) ); +} diff --git a/src/providers/wcs/qgswcssourceselect.h b/src/providers/wcs/qgswcssourceselect.h index c39d69ba4d2..35da8bfdfff 100644 --- a/src/providers/wcs/qgswcssourceselect.h +++ b/src/providers/wcs/qgswcssourceselect.h @@ -23,6 +23,8 @@ #include "qgsowssourceselect.h" #include "qgsdatasourceuri.h" #include "qgsguiutils.h" +#include "qgshelp.h" + #include "qgswcscapabilities.h" #include "qgsproviderregistry.h" #include "qgsdataprovider.h" @@ -78,6 +80,11 @@ class QgsWCSSourceSelect : public QgsOWSSourceSelect QStringList selectedLayersFormats() override; QStringList selectedLayersCrses() override; QStringList selectedLayersTimes() override; + + private slots: + + //! Open help browser + void showHelp(); }; #endif // QGSWCSSOURCESELECT_H diff --git a/src/ui/qgsaddtaborgroupbase.ui b/src/ui/qgsaddtaborgroupbase.ui index 7c7ac350966..ef86e29f2eb 100644 --- a/src/ui/qgsaddtaborgroupbase.ui +++ b/src/ui/qgsaddtaborgroupbase.ui @@ -81,16 +81,6 @@ - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - @@ -108,6 +98,16 @@ + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + diff --git a/src/ui/qgscolordialog.ui b/src/ui/qgscolordialog.ui index 7a62889209f..ac74f8dba42 100644 --- a/src/ui/qgscolordialog.ui +++ b/src/ui/qgscolordialog.ui @@ -26,7 +26,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok @@ -89,14 +89,13 @@ - - QgsCompoundColorWidget - QWidget -
      qgscompoundcolorwidget.h
      - 1 -
      -
      - + + QgsCompoundColorWidget + QWidget +
      qgscompoundcolorwidget.h
      + 1 +
      + diff --git a/src/ui/qgsexpressionbuilderdialogbase.ui b/src/ui/qgsexpressionbuilderdialogbase.ui index e687901dabc..9ef6f746740 100644 --- a/src/ui/qgsexpressionbuilderdialogbase.ui +++ b/src/ui/qgsexpressionbuilderdialogbase.ui @@ -33,7 +33,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsexpressionselectiondialogbase.ui b/src/ui/qgsexpressionselectiondialogbase.ui index 11a15fea5d3..f640c60e0de 100644 --- a/src/ui/qgsexpressionselectiondialogbase.ui +++ b/src/ui/qgsexpressionselectiondialogbase.ui @@ -14,9 +14,32 @@ Select By Expression - + 3 + + 3 + + + 3 + + + 3 + + + + + Close + + + + + + + Zoom to features + + + @@ -30,12 +53,8 @@ - - - - Close - - + + @@ -50,16 +69,13 @@ - - - - Zoom to features + + + + QDialogButtonBox::Close|QDialogButtonBox::Help - - - diff --git a/src/ui/qgsgradientcolorrampdialogbase.ui b/src/ui/qgsgradientcolorrampdialogbase.ui index abca7c720e0..fbbcc4ec695 100644 --- a/src/ui/qgsgradientcolorrampdialogbase.ui +++ b/src/ui/qgsgradientcolorrampdialogbase.ui @@ -321,7 +321,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsnewmemorylayerdialogbase.ui b/src/ui/qgsnewmemorylayerdialogbase.ui index 763ddd65c1b..dc9d271e195 100644 --- a/src/ui/qgsnewmemorylayerdialogbase.ui +++ b/src/ui/qgsnewmemorylayerdialogbase.ui @@ -141,7 +141,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsorderbydialogbase.ui b/src/ui/qgsorderbydialogbase.ui index deacc61b888..ce99ac8551c 100644 --- a/src/ui/qgsorderbydialogbase.ui +++ b/src/ui/qgsorderbydialogbase.ui @@ -20,7 +20,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsosmexportdialog.ui b/src/ui/qgsosmexportdialog.ui index ff01f96bbe6..24bd69742ad 100644 --- a/src/ui/qgsosmexportdialog.ui +++ b/src/ui/qgsosmexportdialog.ui @@ -157,7 +157,7 @@ Qt::Horizontal - QDialogButtonBox::Close|QDialogButtonBox::Ok + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsosmimportdialog.ui b/src/ui/qgsosmimportdialog.ui index 160211e8e6e..10d8b1bbde3 100644 --- a/src/ui/qgsosmimportdialog.ui +++ b/src/ui/qgsosmimportdialog.ui @@ -7,7 +7,7 @@ 0 0 396 - 257 + 260 @@ -102,7 +102,7 @@ Qt::Horizontal - QDialogButtonBox::Close|QDialogButtonBox::Ok + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgsrasterlayersaveasdialogbase.ui b/src/ui/qgsrasterlayersaveasdialogbase.ui index 732b6680b5b..63ed1e57aec 100644 --- a/src/ui/qgsrasterlayersaveasdialogbase.ui +++ b/src/ui/qgsrasterlayersaveasdialogbase.ui @@ -674,7 +674,7 @@ datasets with maximum width and height specified below. Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgssavetodbdialog.ui b/src/ui/qgssavetodbdialog.ui index 26e2cba283f..769c21c3f89 100644 --- a/src/ui/qgssavetodbdialog.ui +++ b/src/ui/qgssavetodbdialog.ui @@ -50,16 +50,6 @@ - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - false - - - @@ -107,6 +97,16 @@ + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + false + + + diff --git a/src/ui/qgsstyleexportimportdialogbase.ui b/src/ui/qgsstyleexportimportdialogbase.ui index e74bd95451b..1b5d7dc092b 100644 --- a/src/ui/qgsstyleexportimportdialogbase.ui +++ b/src/ui/qgsstyleexportimportdialogbase.ui @@ -6,7 +6,7 @@ 0 0 - 400 + 413 379 @@ -144,7 +144,7 @@ Qt::Horizontal - QDialogButtonBox::Close|QDialogButtonBox::Ok + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok diff --git a/src/ui/qgssymbollevelsdialogbase.ui b/src/ui/qgssymbollevelsdialogbase.ui index 584f6db8c93..589530ee4eb 100644 --- a/src/ui/qgssymbollevelsdialogbase.ui +++ b/src/ui/qgssymbollevelsdialogbase.ui @@ -47,7 +47,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok From e30f7044c9af22096dda8722d188d452228d420c Mon Sep 17 00:00:00 2001 From: lbartoletti Date: Sat, 2 Sep 2017 23:37:53 +0200 Subject: [PATCH 269/364] [FEATURE][Processing] Minimal enclosing circle --- python/core/geometry/qgscircle.sip | 18 +++ python/core/geometry/qgsgeometry.sip | 10 ++ python/plugins/processing/algs/help/qgis.yaml | 5 + .../algs/qgis/MinimalEnclosingCircle.py | 142 ++++++++++++++++++ .../algs/qgis/QGISAlgorithmProvider.py | 2 + .../expected/enclosing_circles_all.gfs | 31 ++++ .../expected/enclosing_circles_all.gml | 22 +++ .../expected/enclosing_circles_each.gfs | 47 ++++++ .../expected/enclosing_circles_each.gml | 77 ++++++++++ .../tests/testdata/qgis_algorithm_tests.yaml | 30 ++++ src/core/geometry/qgscircle.cpp | 23 ++- src/core/geometry/qgscircle.h | 14 ++ src/core/geometry/qgsgeometry.cpp | 76 ++++++++++ src/core/geometry/qgsgeometry.h | 9 ++ tests/src/core/testqgsgeometry.cpp | 92 +++++++++++- 15 files changed, 595 insertions(+), 3 deletions(-) create mode 100644 python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py create mode 100644 python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gml create mode 100644 python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml diff --git a/python/core/geometry/qgscircle.sip b/python/core/geometry/qgscircle.sip index 29dbf3dd767..b88835e6e69 100644 --- a/python/core/geometry/qgscircle.sip +++ b/python/core/geometry/qgscircle.sip @@ -112,6 +112,19 @@ class QgsCircle : QgsEllipse :rtype: QgsCircle %End + static QgsCircle minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon = 1E-8 ); +%Docstring + Constructs the smallest circle from 3 points. + Z and m values are dropped for the center point. + The azimuth always takes the default value. + If the points are colinear an empty circle is returned. + \param pt1 First point. + \param pt2 Second point. + \param pt3 Third point. + \param epsilon Value used to compare point. + :rtype: QgsCircle +%End + virtual double area() const; virtual double perimeter() const; @@ -159,6 +172,11 @@ Set the radius of the circle :rtype: QgsCircularString %End + bool contains( const QgsPoint &point, double epsilon = 1E-8 ) const; +%Docstring +Returns true if the circle contains the ``point``. + :rtype: bool +%End virtual QgsRectangle boundingBox() const; diff --git a/python/core/geometry/qgsgeometry.sip b/python/core/geometry/qgsgeometry.sip index ce610614ffa..b575a037630 100644 --- a/python/core/geometry/qgsgeometry.sip +++ b/python/core/geometry/qgsgeometry.sip @@ -577,6 +577,16 @@ Returns true if WKB of the geometry is of WKBMulti* type :rtype: QgsGeometry %End + QgsGeometry minimalEnclosingCircle( QgsPointXY ¢er /Out/, double &radius /Out/, unsigned int segments = 36 ) const; +%Docstring + Returns the minimal enclosing circle for the geometry. + \param center Center of the minimal enclosing circle returneds + \param radius Radius of the minimal enclosing circle returned +.. seealso:: QgsEllipse.toPolygon() +.. versionadded:: 3.0 + :rtype: QgsGeometry +%End + QgsGeometry orthogonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const; %Docstring Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index e2afbfd3174..9fa8531bd82 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -354,6 +354,11 @@ qgis:orientedminimumboundingbox: > As an alternative, the output layer can contain not just a single rectangle, but one for each input feature, representing the minimum rectangle that covers each of them. +qgis:minimalenclosingcircle: > + This algorithm takes a vector layer and generate a new one with the minimum enclosing circle that covers all the input features. + + As an alternative, the output layer can contain not just a single circle, but one for each input feature, representing the minimum enclosing circle that covers each of them. + qgis:orthogonalize: > This algorithm takes a line or polygon layer and attempts to orthogonalize all the geometries in the layer. This process shifts the nodes in the geometries to try to make every angle in the geometry either a right angle or a straight line. diff --git a/python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py b/python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py new file mode 100644 index 00000000000..a31e8b48526 --- /dev/null +++ b/python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + MinimalEnclosingCircle.py + --------------------- + Date : September 2017 + Copyright : (C) 2017, Loïc BARTOLETTI + Email : lbartoletti at tuxfamily dot org +*************************************************************************** +* * +* 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. * +* * +*************************************************************************** +""" + +__author__ = 'Loïc BARTOLETTI' +__date__ = 'September 2017' +__copyright__ = '(C) 2017, Loïc BARTOLETTI' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from qgis.PyQt.QtCore import QVariant +from qgis.core import (QgsField, + QgsFields, + QgsFeatureSink, + QgsGeometry, + QgsFeature, + QgsWkbTypes, + QgsFeatureRequest, + QgsProcessing, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterBoolean, + QgsProcessingParameterFeatureSink, + QgsProcessingException) +from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm + + +class MinimalEnclosingCircle(QgisAlgorithm): + + INPUT = 'INPUT' + BY_FEATURE = 'BY_FEATURE' + + OUTPUT = 'OUTPUT' + + def group(self): + return self.tr('Vector general') + + def __init__(self): + super().__init__() + + def initAlgorithm(self, config=None): + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), [QgsProcessing.TypeVectorAnyGeometry])) + self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE, + self.tr('Calculate bounds for each feature separately'), defaultValue=True)) + + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Enclosing circles'), QgsProcessing.TypeVectorPolygon)) + + def name(self): + return 'minimalenclosingcircle' + + def displayName(self): + return self.tr('Minimal enclosing circle') + + def processAlgorithm(self, parameters, context, feedback): + source = self.parameterAsSource(parameters, self.INPUT, context) + by_feature = self.parameterAsBool(parameters, self.BY_FEATURE, context) + + if not by_feature and QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PointGeometry and source.featureCount() <= 2: + raise QgsProcessingException(self.tr("Can't calculate a minimal enclosing circle for each point, it's a point. The number of points must be greater than 2")) + + if by_feature: + fields = source.fields() + else: + fields = QgsFields() + fields.append(QgsField('center_x', QVariant.Double)) + fields.append(QgsField('center_y', QVariant.Double)) + fields.append(QgsField('radius', QVariant.Double)) + + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, QgsWkbTypes.Polygon, source.sourceCrs()) + + if by_feature: + self.featureMec(source, context, sink, feedback) + else: + self.layerMec(source, context, sink, feedback) + + return {self.OUTPUT: dest_id} + + def layerMec(self, source, context, sink, feedback): + req = QgsFeatureRequest().setSubsetOfAttributes([]) + features = source.getFeatures(req) + total = 100.0 / source.featureCount() if source.featureCount() else 0 + newgeometry = QgsGeometry() + first = True + geometries = [] + for current, inFeat in enumerate(features): + if feedback.isCanceled(): + break + + if inFeat.hasGeometry(): + geometries.append(inFeat.geometry()) + feedback.setProgress(int(current * total)) + + newgeometry = QgsGeometry.unaryUnion(geometries) + geometry, center, radius = newgeometry.minimalEnclosingCircle() + + if geometry: + outFeat = QgsFeature() + + outFeat.setGeometry(geometry) + outFeat.setAttributes([center.x(), + center.y(), + radius]) + sink.addFeature(outFeat, QgsFeatureSink.FastInsert) + + def featureMec(self, source, context, sink, feedback): + features = source.getFeatures() + total = 100.0 / source.featureCount() if source.featureCount() else 0 + outFeat = QgsFeature() + for current, inFeat in enumerate(features): + if feedback.isCanceled(): + break + + geometry, center, radius = inFeat.geometry().minimalEnclosingCircle() + if geometry: + outFeat.setGeometry(geometry) + attrs = inFeat.attributes() + attrs.extend([center.x(), + center.y(), + radius]) + outFeat.setAttributes(attrs) + sink.addFeature(outFeat, QgsFeatureSink.FastInsert) + else: + feedback.pushInfo(self.tr("Can't calculate a minimal enclosing circle for feature {0}.").format(inFeat.id())) + feedback.setProgress(int(current * total)) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 9a8539cd641..1a532f004dc 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -100,6 +100,7 @@ from .LinesToPolygons import LinesToPolygons from .MeanCoords import MeanCoords from .Merge import Merge from .MergeLines import MergeLines +from .MinimalEnclosingCircle import MinimalEnclosingCircle from .NearestNeighbourAnalysis import NearestNeighbourAnalysis from .OffsetLine import OffsetLine from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox @@ -253,6 +254,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): MeanCoords(), Merge(), MergeLines(), + MinimalEnclosingCircle(), NearestNeighbourAnalysis(), OffsetLine(), OrientedMinimumBoundingBox(), diff --git a/python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gfs b/python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gfs new file mode 100644 index 00000000000..eaa4f1732da --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gfs @@ -0,0 +1,31 @@ + + + enclosing_circles_all + enclosing_circles_all + + 3 + EPSG:4326 + + 1 + -1.50891 + 10.30638 + -5.30638 + 6.50891 + + + center_x + center_x + Real + + + center_y + center_y + Real + + + radius + radius + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gml b/python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gml new file mode 100644 index 00000000000..fb9e4694a96 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gml @@ -0,0 +1,22 @@ + + + + + -1.508909716010908-5.306378070441288 + 10.306378070441296.508909716010908 + + + + + + 4.39873417721519,6.50890971601091 5.42458577357907,6.4191593308691 6.41926738829339,6.15263519548031 7.35255612382824,5.71743551083061 8.19609447422128,5.12678359911643 8.92425195354681,4.39862611979089 9.51490386526099,3.55508776939786 9.95010354991069,2.62179903386301 10.2166276852995,1.62711741914869 10.3063780704413,0.601265822784808 10.2166276852995,-0.424585773579072 9.95010354991069,-1.41926738829339 9.51490386526099,-2.35255612382824 8.92425195354681,-3.19609447422128 8.19609447422127,-3.92425195354681 7.35255612382824,-4.514903865261 6.41926738829339,-4.95010354991069 5.42458577357907,-5.21662768529948 4.39873417721519,-5.30637807044129 3.37288258085131,-5.21662768529948 2.37820096613699,-4.95010354991069 1.44491223060214,-4.51490386526099 0.601373880209104,-3.92425195354681 -0.126783599116428,-3.19609447422127 -0.717435510830614,-2.35255612382824 -1.15263519548031,-1.41926738829339 -1.4191593308691,-0.42458577357907 -1.50890971601091,0.60126582278481 -1.4191593308691,1.62711741914869 -1.15263519548031,2.62179903386301 -0.717435510830614,3.55508776939786 -0.126783599116427,4.3986261197909 0.601373880209104,5.12678359911643 1.44491223060214,5.71743551083061 2.37820096613699,6.15263519548031 3.37288258085131,6.4191593308691 4.39873417721519,6.50890971601091 + 4.39873417721519 + 0.60126582278481 + 5.9076438932261 + + + diff --git a/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs new file mode 100644 index 00000000000..9d8dc0c421c --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs @@ -0,0 +1,47 @@ + + + enclosing_circles_each + enclosing_circles_each + + 3 + EPSG:4326 + + 6 + -1.81766 + 10.59982 + -3.43247 + 6.32190 + + + intval + intval + Integer + + + floatval + floatval + Real + + + center_x + center_x + Real + + + center_y + center_y + Real + + + radius + radius + Real + + + name + name + String + 5 + + + diff --git a/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml new file mode 100644 index 00000000000..0a23fc8539b --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml @@ -0,0 +1,77 @@ + + + + + -1.817664105611035-3.43247280352443 + 10.599819742299946.321903675995209 + + + + + + 3.8,2.2 4.26819673362059,2.35151315326536 4.75559048978224,2.4194229717016 5.24737205583712,2.40166604983954 5.72859889775413,2.29878192276454 6.18464918145415,2.11389667261588 6.60166604983954,1.85262794416288 6.96697865638513,1.52291425551124 7.26948716239812,1.13477379024745 7.5,0.7 7.65151315326536,0.23180326637941 7.7194229717016,-0.255590489782239 7.70166604983954,-0.747372055837116 7.59878192276454,-1.22859889775413 7.41389667261588,-1.68464918145415 7.15262794416288,-2.10166604983954 6.82291425551124,-2.46697865638513 6.43477379024745,-2.76948716239812 6.0,-3.0 5.53180326637941,-3.15151315326536 5.04440951021776,-3.2194229717016 4.55262794416288,-3.20166604983954 4.07140110224587,-3.09878192276454 3.61535081854585,-2.91389667261588 3.19833395016046,-2.65262794416288 2.83302134361487,-2.32291425551124 2.53051283760188,-1.93477379024745 2.3,-1.5 2.14848684673464,-1.03180326637941 2.0805770282984,-0.544409510217762 2.09833395016046,-0.052627944162882 2.20121807723546,0.428598897754127 2.38610332738412,0.884649181454149 2.64737205583712,1.30166604983954 2.97708574448876,1.66697865638513 3.36522620975255,1.96948716239812 3.8,2.2 + 120 + -100291.43213 + 4.9 + -0.4 + 2.82311884269862 + + + + + 5.0,5.08319489631876 5.17420296559052,5.06795411167699 5.34311286222252,5.02269484128082 5.50159744815938,4.94879226515894 5.64484124945447,4.8484918756903 5.7684918756903,4.72484124945447 5.86879226515894,4.58159744815938 5.94269484128082,4.42311286222252 5.98795411167699,4.25420296559052 6.00319489631876,4.08 5.98795411167699,3.90579703440948 5.94269484128082,3.73688713777748 5.86879226515894,3.57840255184062 5.7684918756903,3.43515875054553 5.64484124945447,3.3115081243097 5.50159744815938,3.21120773484106 5.34311286222252,3.13730515871918 5.17420296559052,3.09204588832301 5.0,3.07680510368124 4.82579703440948,3.09204588832301 4.65688713777748,3.13730515871918 4.49840255184062,3.21120773484106 4.35515875054553,3.3115081243097 4.2315081243097,3.43515875054553 4.13120773484106,3.57840255184062 4.05730515871918,3.73688713777748 4.01204588832301,3.90579703440948 3.99680510368124,4.08 4.01204588832301,4.25420296559052 4.05730515871918,4.42311286222252 4.13120773484106,4.58159744815938 4.2315081243097,4.72484124945447 4.35515875054553,4.8484918756903 4.49840255184062,4.94879226515894 4.65688713777748,5.02269484128082 4.82579703440948,5.06795411167699 5.0,5.08319489631876 + -33 + 0 + Aaaaa + 5 + 4.08 + 1.00319489631876 + + + + + 3.0,3.0 3.31691186135828,2.62231915069056 3.56342552822315,2.19534495492048 3.73205080756888,1.73205080756888 3.81766410561104,1.24651366686488 3.81766410561104,0.753486333135122 3.73205080756888,0.267949192431123 3.56342552822315,-0.195344954920481 3.31691186135828,-0.622319150690556 3.0,-1.0 2.62231915069056,-1.31691186135828 2.19534495492048,-1.56342552822315 1.73205080756888,-1.73205080756888 1.24651366686488,-1.81766410561103 0.753486333135123,-1.81766410561103 0.267949192431122,-1.73205080756888 -0.195344954920479,-1.56342552822315 -0.622319150690555,-1.31691186135828 -1.0,-1.0 -1.31691186135828,-0.622319150690556 -1.56342552822315,-0.195344954920479 -1.73205080756888,0.267949192431122 -1.81766410561103,0.753486333135123 -1.81766410561103,1.24651366686488 -1.73205080756888,1.73205080756888 -1.56342552822316,2.19534495492048 -1.31691186135828,2.62231915069056 -1.0,3.0 -0.622319150690556,3.31691186135828 -0.195344954920481,3.56342552822315 0.267949192431124,3.73205080756888 0.753486333135123,3.81766410561104 1.24651366686488,3.81766410561104 1.73205080756888,3.73205080756888 2.19534495492048,3.56342552822316 2.62231915069056,3.31691186135828 3.0,3.0 + 33 + 44.12346 + aaaaa + 1 + 1 + 2.82842712474619 + + + + + 7.8734693877551,2.02022790556525 8.3468951585034,1.97880851760375 8.80593612677252,1.85580886086324 9.23664456502752,1.65496621767295 9.62593361532103,1.38238309011494 9.96197492684963,1.04634177858634 10.2345580544076,0.657052728292829 10.4354006975979,0.226344290037822 10.5584003543384,-0.232696678231291 10.5998197422999,-0.706122448979591 10.5584003543384,-1.17954821972789 10.4354006975979,-1.638589187997 10.2345580544076,-2.06929762625201 9.96197492684963,-2.45858667654552 9.62593361532103,-2.79462798807412 9.23664456502752,-3.06721111563213 8.80593612677252,-3.26805375882242 8.3468951585034,-3.39105341556293 7.8734693877551,-3.43247280352443 7.4000436170068,-3.39105341556293 6.94100264873769,-3.26805375882242 6.51029421048268,-3.06721111563213 6.12100516018918,-2.79462798807412 5.78496384866057,-2.45858667654552 5.51238072110256,-2.06929762625201 5.31153807791227,-1.63858918799701 5.18853842117176,-1.17954821972789 5.14711903321026,-0.70612244897959 5.18853842117176,-0.23269667823129 5.31153807791227,0.226344290037823 5.51238072110256,0.65705272829283 5.78496384866057,1.04634177858634 6.12100516018918,1.38238309011494 6.51029421048268,1.65496621767295 6.94100264873769,1.85580886086324 7.4000436170068,1.97880851760375 7.8734693877551,2.02022790556525 + 0 + ASDF + 7.8734693877551 + -0.706122448979591 + 2.72635035454484 + + + + + 3,6 3.0935543337087,5.86933092744047 3.16299692054554,5.72440147214358 3.20621778264911,5.56961524227066 3.22190367599521,5.40967533909081 3.20957799265196,5.24944145562864 3.16961524227066,5.09378221735089 3.10322967279951,4.94742725144526 3.01243837617418,4.81482347949161 2.9,4.7 2.76933092744047,4.6064456662913 2.62440147214358,4.53700307945446 2.46961524227066,4.49378221735089 2.30967533909081,4.47809632400479 2.14944145562864,4.49042200734804 1.99378221735089,4.53038475772934 1.84742725144526,4.59677032720049 1.71482347949161,4.68756162382582 1.6,4.8 1.5064456662913,4.93066907255953 1.43700307945446,5.07559852785642 1.39378221735089,5.23038475772934 1.37809632400479,5.39032466090919 1.39042200734804,5.55055854437136 1.43038475772934,5.70621778264911 1.49677032720049,5.85257274855473 1.58756162382582,5.98517652050839 1.7,6.1 1.83066907255953,6.1935543337087 1.97559852785642,6.26299692054554 2.13038475772934,6.30621778264911 2.29032466090919,6.32190367599521 2.45055854437136,6.30957799265196 2.60621778264911,6.26961524227066 2.75257274855473,6.20322967279951 2.88517652050839,6.11243837617418 3,6 + 0.123 + bbaaa + 2.3 + 5.4 + 0.921954445729289 + + + + + 3.8,2.2 4.26819673362059,2.35151315326536 4.75559048978224,2.4194229717016 5.24737205583712,2.40166604983954 5.72859889775413,2.29878192276454 6.18464918145415,2.11389667261588 6.60166604983954,1.85262794416288 6.96697865638513,1.52291425551124 7.26948716239812,1.13477379024745 7.5,0.7 7.65151315326536,0.23180326637941 7.7194229717016,-0.255590489782239 7.70166604983954,-0.747372055837116 7.59878192276454,-1.22859889775413 7.41389667261588,-1.68464918145415 7.15262794416288,-2.10166604983954 6.82291425551124,-2.46697865638513 6.43477379024745,-2.76948716239812 6.0,-3.0 5.53180326637941,-3.15151315326536 5.04440951021776,-3.2194229717016 4.55262794416288,-3.20166604983954 4.07140110224587,-3.09878192276454 3.61535081854585,-2.91389667261588 3.19833395016046,-2.65262794416288 2.83302134361487,-2.32291425551124 2.53051283760188,-1.93477379024745 2.3,-1.5 2.14848684673464,-1.03180326637941 2.0805770282984,-0.544409510217762 2.09833395016046,-0.052627944162882 2.20121807723546,0.428598897754127 2.38610332738412,0.884649181454149 2.64737205583712,1.30166604983954 2.97708574448876,1.66697865638513 3.36522620975255,1.96948716239812 3.8,2.2 + 2 + 3.33 + elim + 4.9 + -0.4 + 2.82311884269862 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 570ba2d95a3..f4e544cafe8 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3239,3 +3239,33 @@ tests: OUTPUT: name: expected/raster_extent.gml type: vector + + - algorithm: qgis:minimalenclosingcircle + name: Minimal enclosing circle each features + params: + BY_FEATURE: true + INPUT: + name: custom/oriented_bbox.gml + type: vector + results: + OUTPUT: + name: expected/enclosing_circles_each.gml + type: vector + compare: + geometry: + precision: 7 + + - algorithm: qgis:minimalenclosingcircle + name: Minimal enclosing circle all features + params: + BY_FEATURE: false + INPUT: + name: custom/oriented_bbox.gml + type: vector + results: + OUTPUT: + name: expected/enclosing_circles_all.gml + type: vector + compare: + geometry: + precision: 7 diff --git a/src/core/geometry/qgscircle.cpp b/src/core/geometry/qgscircle.cpp index c65909d174d..2f3c3211236 100644 --- a/src/core/geometry/qgscircle.cpp +++ b/src/core/geometry/qgscircle.cpp @@ -38,7 +38,7 @@ QgsCircle QgsCircle::from2Points( const QgsPoint &pt1, const QgsPoint &pt2 ) { QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 ); double azimuth = QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() ) * 180.0 / M_PI; - double radius = pt1.distance( pt2 ); + double radius = pt1.distance( pt2 ) / 2.0; return QgsCircle( center, radius, azimuth ); } @@ -189,6 +189,22 @@ QgsCircle QgsCircle::from3Tangents( const QgsPoint &pt1_tg1, const QgsPoint &pt2 return QgsTriangle( p1, p2, p3 ).inscribedCircle(); } +QgsCircle QgsCircle::minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon ) +{ + double l1 = pt2.distance( pt3 ); + double l2 = pt3.distance( pt1 ); + double l3 = pt1.distance( pt2 ); + + if ( ( l1 * l1 ) - ( l2 * l2 + l3 * l3 ) >= epsilon ) + return QgsCircle().from2Points( pt2, pt3 ); + else if ( ( l2 * l2 ) - ( l1 * l1 + l3 * l3 ) >= epsilon ) + return QgsCircle().from2Points( pt3, pt1 ); + else if ( ( l3 * l3 ) - ( l1 * l1 + l2 * l2 ) >= epsilon ) + return QgsCircle().from2Points( pt1, pt2 ); + else + return QgsCircle().from3Points( pt1, pt2, pt3, epsilon ); +} + QgsCircle QgsCircle::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) { double delta_x = std::fabs( pt1.x() - pt2.x() ); @@ -245,6 +261,11 @@ QgsCircularString *QgsCircle::toCircularString( bool oriented ) const return circString.release(); } +bool QgsCircle::contains( const QgsPoint &point, double epsilon ) const +{ + return ( mCenter.distance( point ) <= mSemiMajorAxis + epsilon ); +} + QgsRectangle QgsCircle::boundingBox() const { return QgsRectangle( mCenter.x() - mSemiMajorAxis, mCenter.y() - mSemiMajorAxis, mCenter.x() + mSemiMajorAxis, mCenter.y() + mSemiMajorAxis ); diff --git a/src/core/geometry/qgscircle.h b/src/core/geometry/qgscircle.h index b15b9d4572c..27fd07f67eb 100644 --- a/src/core/geometry/qgscircle.h +++ b/src/core/geometry/qgscircle.h @@ -119,6 +119,18 @@ class CORE_EXPORT QgsCircle : public QgsEllipse */ static QgsCircle fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ); + /** + * Constructs the smallest circle from 3 points. + * Z and m values are dropped for the center point. + * The azimuth always takes the default value. + * If the points are colinear an empty circle is returned. + * \param pt1 First point. + * \param pt2 Second point. + * \param pt3 Third point. + * \param epsilon Value used to compare point. + */ + static QgsCircle minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon = 1E-8 ); + double area() const override; double perimeter() const override; @@ -168,6 +180,8 @@ class CORE_EXPORT QgsCircle : public QgsEllipse */ QgsCircularString *toCircularString( bool oriented = false ) const; + //! Returns true if the circle contains the \a point. + bool contains( const QgsPoint &point, double epsilon = 1E-8 ) const; QgsRectangle boundingBox() const override; diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index a4b48a2ce13..a96ee086d5d 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -44,6 +44,7 @@ email : morb at ozemail dot com dot au #include "qgspoint.h" #include "qgspolygon.h" #include "qgslinestring.h" +#include "qgscircle.h" struct QgsGeometryPrivate { @@ -1008,6 +1009,81 @@ QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle return minBounds; } +static QgsCircle __recMinimalEnclosingCircle( QgsMultiPoint points, QgsMultiPoint boundary ) +{ + auto l_boundary = boundary.length(); + QgsCircle circ_mec; + if ( ( points.length() == 0 ) || ( l_boundary == 3 ) ) + { + switch ( l_boundary ) + { + case 0: + circ_mec = QgsCircle(); + break; + case 1: + circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 ); + boundary.pop_back(); + break; + case 2: + { + QgsPointXY p1 = boundary.last(); + boundary.pop_back(); + QgsPointXY p2 = boundary.last(); + boundary.pop_back(); + circ_mec = QgsCircle().from2Points( QgsPoint( p1 ), QgsPoint( p2 ) ); + } + break; + default: + QgsPoint p1( boundary.at( 0 ) ); + QgsPoint p2( boundary.at( 1 ) ); + QgsPoint p3( boundary.at( 2 ) ); + circ_mec = QgsCircle().minimalCircleFrom3Points( p1, p2, p3 ); + break; + } + return circ_mec; + } + else + { + QgsPointXY pxy = points.last(); + points.pop_back(); + circ_mec = __recMinimalEnclosingCircle( points, boundary ); + QgsPoint p( pxy ); + if ( !circ_mec.contains( p ) ) + { + boundary.append( pxy ); + circ_mec = __recMinimalEnclosingCircle( points, boundary ); + } + } + return circ_mec; +} + +QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY ¢er, double &radius, unsigned int segments ) const +{ + center = QgsPointXY( ); + radius = 0; + + if ( !d->geometry ) + { + return QgsGeometry(); + } + + /* optimization */ + QgsGeometry hull = convexHull(); + if ( hull.isNull() ) + return QgsGeometry(); + + QgsMultiPoint P = hull.convertToPoint( true ).asMultiPoint(); + QgsMultiPoint R; + + QgsCircle circ = __recMinimalEnclosingCircle( P, R ); + center = QgsPointXY( circ.center() ); + radius = circ.radius(); + QgsGeometry geom; + geom.setGeometry( circ.toPolygon( segments ) ); + return geom; + +} + QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const { QgsInternalGeometryEngine engine( *this ); diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index 0103cbc14ca..6c6cabae11c 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -599,6 +599,15 @@ class CORE_EXPORT QgsGeometry */ QgsGeometry orientedMinimumBoundingBox( double &area SIP_OUT, double &angle SIP_OUT, double &width SIP_OUT, double &height SIP_OUT ) const; + /** + * Returns the minimal enclosing circle for the geometry. + * \param center Center of the minimal enclosing circle returneds + * \param radius Radius of the minimal enclosing circle returned + * \param segments Number of segments used to segment geometry. \see QgsEllipse::toPolygon() + * \since QGIS 3.0 + */ + QgsGeometry minimalEnclosingCircle( QgsPointXY ¢er SIP_OUT, double &radius SIP_OUT, unsigned int segments = 36 ) const; + /** * Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries * angles either right angles or flat lines. This is an iterative algorithm which will loop until diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 5a8ef17b3e8..dfbdcf91bef 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -128,6 +128,8 @@ class TestQgsGeometry : public QObject void reshapeGeometryLineMerge(); void createCollectionOfType(); + void minimalEnclosingCircle( ); + private: //! A helper method to do a render check to see if the geometry op is as expected bool renderCheck( const QString &testName, const QString &comment = QLatin1String( QLatin1String( "" ) ), int mismatchCount = 0 ); @@ -4161,8 +4163,8 @@ void TestQgsGeometry::circle() //test "alt" constructors // by2Points - QVERIFY( QgsCircle().from2Points( QgsPoint( -5, 0 ), QgsPoint( 5, 0 ) ) == QgsCircle( QgsPoint( 0, 0 ), 10, 90 ) ); - QVERIFY( QgsCircle().from2Points( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 10, 0 ) ); + QVERIFY( QgsCircle().from2Points( QgsPoint( -5, 0 ), QgsPoint( 5, 0 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 90 ) ); + QVERIFY( QgsCircle().from2Points( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) ); // byExtent QVERIFY( QgsCircle().fromExtent( QgsPoint( -5, -5 ), QgsPoint( 5, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) ); QVERIFY( QgsCircle().fromExtent( QgsPoint( -7.5, -2.5 ), QgsPoint( 2.5, 200.5 ) ) == QgsCircle() ); @@ -4182,6 +4184,15 @@ void TestQgsGeometry::circle() QgsCircle circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 2, 0 ), QgsPoint( 3, 0 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) ); QGSCOMPARENEARPOINT( circ_tgt.center(), QgsPoint( 1.4645, 1.4645 ), 0.0001 ); QGSCOMPARENEAR( circ_tgt.radius(), 1.4645, 0.0001 ); + // minimalCircleFrom3points + QgsCircle minCircle3Points = QgsCircle().minimalCircleFrom3Points( QgsPoint( 0, 5 ), QgsPoint( 0, -5 ), QgsPoint( 1, 2 ) ); + QGSCOMPARENEARPOINT( minCircle3Points.center(), QgsPoint( 0, 0 ), 0.0001 ); + QGSCOMPARENEAR( minCircle3Points.radius(), 5.0, 0.0001 ); + minCircle3Points = QgsCircle().minimalCircleFrom3Points( QgsPoint( 0, 5 ), QgsPoint( 5, 0 ), QgsPoint( -5, 0 ) ); + QGSCOMPARENEARPOINT( minCircle3Points.center(), QgsPoint( 0, 0 ), 0.0001 ); + QGSCOMPARENEAR( minCircle3Points.radius(), 5.0, 0.0001 ); + + // test quadrant QVector quad = QgsCircle( QgsPoint( 0, 0 ), 5 ).northQuadrant(); @@ -4286,6 +4297,15 @@ void TestQgsGeometry::circle() QGSCOMPARENEAR( 314.1593, QgsCircle( QgsPoint( 0, 0 ), 10 ).area(), 0.0001 ); // perimeter QGSCOMPARENEAR( 31.4159, QgsCircle( QgsPoint( 0, 0 ), 5 ).perimeter(), 0.0001 ); + + // contains + QgsPoint pc; + pc = QgsPoint( 1, 1 ); + QVERIFY( QgsCircle( QgsPoint( 0, 0 ), 5 ).contains( pc ) ); + pc = QgsPoint( 0, 5 ); + QVERIFY( QgsCircle( QgsPoint( 0, 0 ), 5 ).contains( pc ) ); + pc = QgsPoint( 6, 1 ); + QVERIFY( !QgsCircle( QgsPoint( 0, 0 ), 5 ).contains( pc ) ); } void TestQgsGeometry::regularPolygon() @@ -5577,5 +5597,73 @@ void TestQgsGeometry::createCollectionOfType() QVERIFY( dynamic_cast< QgsMultiSurface *>( collect.get() ) ); } +void TestQgsGeometry::minimalEnclosingCircle() +{ + QgsGeometry geomTest; + QgsGeometry result, resultTest; + QgsPointXY center; + double radius; + + // empty + result = geomTest.minimalEnclosingCircle( center, radius ); + QCOMPARE( center, QgsPointXY() ); + QCOMPARE( radius, 0.0 ); + QCOMPARE( result, QgsGeometry() ); + + // caase 1 + geomTest = QgsGeometry::fromPoint( QgsPointXY( 5, 5 ) ); + result = geomTest.minimalEnclosingCircle( center, radius ); + QCOMPARE( center, QgsPointXY( 5, 5 ) ); + QCOMPARE( radius, 0.0 ); + resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); + QCOMPARE( result, resultTest ); + + // case 2 + geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 3 8, 7 4 )" ) ); + result = geomTest.minimalEnclosingCircle( center, radius ); + QGSCOMPARENEARPOINT( center, QgsPointXY( 5, 6 ), 0.0001 ); + QGSCOMPARENEAR( radius, sqrt( 2 ) * 2, 0.0001 ); + resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); + QCOMPARE( result, resultTest ); + + geomTest = QgsGeometry::fromWkt( QString( "LINESTRING( 0 5, 2 2, 0 -5, -1 -1 )" ) ); + result = geomTest.minimalEnclosingCircle( center, radius ); + QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); + QGSCOMPARENEAR( radius, 5, 0.0001 ); + resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); + QCOMPARE( result, resultTest ); + + geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 0 5, 2 2, 0 -5, -1 -1 )" ) ); + result = geomTest.minimalEnclosingCircle( center, radius ); + QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); + QGSCOMPARENEAR( radius, 5, 0.0001 ); + resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); + QCOMPARE( result, resultTest ); + + geomTest = QgsGeometry::fromWkt( QString( "POLYGON(( 0 5, 2 2, 0 -5, -1 -1 ))" ) ); + result = geomTest.minimalEnclosingCircle( center, radius ); + QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); + QGSCOMPARENEAR( radius, 5, 0.0001 ); + resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); + QCOMPARE( result, resultTest ); + + geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 0 5, 0 -5, 0 0 )" ) ); + result = geomTest.minimalEnclosingCircle( center, radius ); + QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); + QGSCOMPARENEAR( radius, 5, 0.0001 ); + resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); + QCOMPARE( result, resultTest ); + + // case 3 + geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT((0 0), (5 5), (0 -5), (0 5), (-5 0))" ) ); + result = geomTest.minimalEnclosingCircle( center, radius ); + QGSCOMPARENEARPOINT( center, QgsPointXY( 0.8333, 0.8333 ), 0.0001 ); + QGSCOMPARENEAR( radius, 5.8926, 0.0001 ); + resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); + QCOMPARE( result, resultTest ); + +} + + QGSTEST_MAIN( TestQgsGeometry ) #include "testqgsgeometry.moc" From 7a2f148add1fbcb1ec0610e33aada94c60cd6413 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 3 Sep 2017 14:50:48 +1000 Subject: [PATCH 270/364] [ogr] Don't try to calculate CRS for geometryless layers Slight speedup when loading attribute table containing joins to a geometryless OGR table --- src/providers/ogr/qgsogrprovider.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index da4f637a89a..fc6febafd54 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -2913,10 +2913,8 @@ QGISEXTERN QList< QgsDataItemProvider * > *dataItemProviders() QgsCoordinateReferenceSystem QgsOgrProvider::crs() const { - QgsDebugMsg( "Entering." ); - QgsCoordinateReferenceSystem srs; - if ( !mValid ) + if ( !mValid || ( mOGRGeomType == wkbNone ) ) return srs; if ( ogrDriver ) @@ -2949,7 +2947,7 @@ QgsCoordinateReferenceSystem QgsOgrProvider::crs() const // get the proj4 text char *pszProj4 = nullptr; OSRExportToProj4( mySpatialRefSys, &pszProj4 ); - QgsDebugMsg( pszProj4 ); + QgsDebugMsgLevel( pszProj4, 4 ); CPLFree( pszProj4 ); char *pszWkt = nullptr; From 7cd7f1685fe35fc943926738269f7976d80834c2 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sun, 3 Sep 2017 21:30:45 +0200 Subject: [PATCH 271/364] Added last missing signal replaceVectorLayer This is only used by virtual layers, but it might be handy for others too and now I can start to remove all specialized source select add methods from the datas source manager dialog. This is only the first step: next is looping through the source select provider registry to get all needed GUI elements for the dialog. --- python/gui/qgsabstractdatasourcewidget.sip | 10 ++++++++++ src/gui/qgsabstractdatasourcewidget.h | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/python/gui/qgsabstractdatasourcewidget.sip b/python/gui/qgsabstractdatasourcewidget.sip index f650c78265f..8806f36bd4d 100644 --- a/python/gui/qgsabstractdatasourcewidget.sip +++ b/python/gui/qgsabstractdatasourcewidget.sip @@ -90,6 +90,16 @@ Emitted when a vector layer has been selected for addition \param dataSourceType string (can be "file" or "database") %End + void replaceVectorLayer( const QString &oldId, const QString &source, const QString &name, const QString &provider ); +%Docstring + Emitted when a layer needs to be replaced + \param oldId old layer ID + \param source URI of the layer + \params name of the layer + \params provider key +%End + + void progress( int, int ); %Docstring Emitted when a progress dialog is shown by the provider dialog diff --git a/src/gui/qgsabstractdatasourcewidget.h b/src/gui/qgsabstractdatasourcewidget.h index 0456bbccd6e..897a69f25a3 100644 --- a/src/gui/qgsabstractdatasourcewidget.h +++ b/src/gui/qgsabstractdatasourcewidget.h @@ -93,6 +93,15 @@ class GUI_EXPORT QgsAbstractDataSourceWidget : public QDialog */ void addVectorLayers( const QStringList &layerList, const QString &encoding, const QString &dataSourceType ); + /** Emitted when a layer needs to be replaced + * \param oldId old layer ID + * \param source URI of the layer + * \params name of the layer + * \params provider key + */ + void replaceVectorLayer( const QString &oldId, const QString &source, const QString &name, const QString &provider ); + + //! Emitted when a progress dialog is shown by the provider dialog void progress( int, int ); From fa71bc3ee50fd797653f8be9b359f714dec86522 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sun, 3 Sep 2017 21:34:42 +0200 Subject: [PATCH 272/364] Removed specialized signal that is now in the base class --- src/providers/virtual/qgsvirtuallayersourceselect.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/providers/virtual/qgsvirtuallayersourceselect.h b/src/providers/virtual/qgsvirtuallayersourceselect.h index 3de21bcd3d5..31ed616c38a 100644 --- a/src/providers/virtual/qgsvirtuallayersourceselect.h +++ b/src/providers/virtual/qgsvirtuallayersourceselect.h @@ -53,9 +53,6 @@ class QgsVirtualLayerSourceSelect : public QgsAbstractDataSourceWidget, private void onTableRowChanged( const QModelIndex ¤t, const QModelIndex &previous ); void updateLayersList(); - signals: - //! Old_id, source, name, provider - void replaceVectorLayer( QString, QString, QString, QString ); private: QgsVirtualLayerDefinition getVirtualLayerDef(); From e8685996c4fc6b0d4c66d21e2b8b910c2c677085 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Sun, 3 Sep 2017 21:38:55 +0200 Subject: [PATCH 273/364] Remove all specialized methods for adding the source selects We now have a unified interface for all dialogs. --- src/gui/qgsdatasourcemanagerdialog.cpp | 97 ++++++++++---------------- src/gui/qgsdatasourcemanagerdialog.h | 4 +- 2 files changed, 36 insertions(+), 65 deletions(-) diff --git a/src/gui/qgsdatasourcemanagerdialog.cpp b/src/gui/qgsdatasourcemanagerdialog.cpp index 197bd72e7d0..e0d7b227047 100644 --- a/src/gui/qgsdatasourcemanagerdialog.cpp +++ b/src/gui/qgsdatasourcemanagerdialog.cpp @@ -56,47 +56,38 @@ QgsDataSourceManagerDialog::QgsDataSourceManagerDialog( QWidget *parent, QgsMapC connect( this, &QgsDataSourceManagerDialog::updateProjectHome, mBrowserWidget, &QgsBrowserDockWidget::updateProjectHome ); // Add data provider dialogs - QWidget *dlg = nullptr; - addVectorProviderDialog( QStringLiteral( "ogr" ), tr( "Vector" ), QStringLiteral( "/mActionAddOgrLayer.svg" ) ); + providerDialog( QStringLiteral( "ogr" ), tr( "Vector" ), QStringLiteral( "/mActionAddOgrLayer.svg" ) ); - addRasterProviderDialog( QStringLiteral( "gdal" ), tr( "Raster" ), QStringLiteral( "/mActionAddRasterLayer.svg" ) ); + providerDialog( QStringLiteral( "gdal" ), tr( "Raster" ), QStringLiteral( "/mActionAddRasterLayer.svg" ) ); - addVectorProviderDialog( QStringLiteral( "delimitedtext" ), tr( "Delimited Text" ), QStringLiteral( "/mActionAddDelimitedTextLayer.svg" ) ); + providerDialog( QStringLiteral( "delimitedtext" ), tr( "Delimited Text" ), QStringLiteral( "/mActionAddDelimitedTextLayer.svg" ) ); #ifdef HAVE_POSTGRESQL - addDbProviderDialog( QStringLiteral( "postgres" ), tr( "PostgreSQL" ), QStringLiteral( "/mActionAddPostgisLayer.svg" ) ); + providerDialog( QStringLiteral( "postgres" ), tr( "PostgreSQL" ), QStringLiteral( "/mActionAddPostgisLayer.svg" ) ); #endif - addDbProviderDialog( QStringLiteral( "spatialite" ), tr( "SpatiaLite" ), QStringLiteral( "/mActionAddSpatiaLiteLayer.svg" ) ); + providerDialog( QStringLiteral( "spatialite" ), tr( "SpatiaLite" ), QStringLiteral( "/mActionAddSpatiaLiteLayer.svg" ) ); - addDbProviderDialog( QStringLiteral( "mssql" ), tr( "MSSQL" ), QStringLiteral( "/mActionAddMssqlLayer.svg" ) ); + providerDialog( QStringLiteral( "mssql" ), tr( "MSSQL" ), QStringLiteral( "/mActionAddMssqlLayer.svg" ) ); - addDbProviderDialog( QStringLiteral( "DB2" ), tr( "DB2" ), QStringLiteral( "/mActionAddDb2Layer.svg" ) ); + providerDialog( QStringLiteral( "DB2" ), tr( "DB2" ), QStringLiteral( "/mActionAddDb2Layer.svg" ) ); #ifdef HAVE_ORACLE - addDbProviderDialog( QStringLiteral( "oracle" ), tr( "Oracle" ), QStringLiteral( "/mActionAddOracleLayer.svg" ) ); + providerDialog( QStringLiteral( "oracle" ), tr( "Oracle" ), QStringLiteral( "/mActionAddOracleLayer.svg" ) ); #endif - dlg = addVectorProviderDialog( QStringLiteral( "virtual" ), tr( "Virtual Layer" ), QStringLiteral( "/mActionAddVirtualLayer.svg" ) ); + providerDialog( QStringLiteral( "virtual" ), tr( "Virtual Layer" ), QStringLiteral( "/mActionAddVirtualLayer.svg" ) ); - // Apparently this is the only provider using replaceVectorLayer, we should - // move this in to the base abstract class when it is used by at least one - // additional provider. - if ( dlg ) - { - connect( dlg, SIGNAL( replaceVectorLayer( QString, QString, QString, QString ) ), this, SIGNAL( replaceSelectedVectorLayer( QString, QString, QString, QString ) ) ); - } + providerDialog( QStringLiteral( "wms" ), tr( "WMS" ), QStringLiteral( "/mActionAddWmsLayer.svg" ) ); - addRasterProviderDialog( QStringLiteral( "wms" ), tr( "WMS" ), QStringLiteral( "/mActionAddWmsLayer.svg" ) ); + providerDialog( QStringLiteral( "wcs" ), tr( "WCS" ), QStringLiteral( "/mActionAddWcsLayer.svg" ) ); - addRasterProviderDialog( QStringLiteral( "wcs" ), tr( "WCS" ), QStringLiteral( "/mActionAddWcsLayer.svg" ) ); + providerDialog( QStringLiteral( "WFS" ), tr( "WFS" ), QStringLiteral( "/mActionAddWfsLayer.svg" ) ); - addVectorProviderDialog( QStringLiteral( "WFS" ), tr( "WFS" ), QStringLiteral( "/mActionAddWfsLayer.svg" ) ); + providerDialog( QStringLiteral( "arcgismapserver" ), tr( "ArcGIS Map Server" ), QStringLiteral( "/mActionAddAmsLayer.svg" ) ); - addRasterProviderDialog( QStringLiteral( "arcgismapserver" ), tr( "ArcGIS Map Server" ), QStringLiteral( "/mActionAddAmsLayer.svg" ) ); - - addVectorProviderDialog( QStringLiteral( "arcgisfeatureserver" ), tr( "ArcGIS Feature Server" ), QStringLiteral( "/mActionAddAfsLayer.svg" ) ); + providerDialog( QStringLiteral( "arcgisfeatureserver" ), tr( "ArcGIS Feature Server" ), QStringLiteral( "/mActionAddAfsLayer.svg" ) ); } @@ -171,51 +162,33 @@ QgsAbstractDataSourceWidget *QgsDataSourceManagerDialog::providerDialog( const Q } connect( dlg, &QgsAbstractDataSourceWidget::rejected, this, &QgsDataSourceManagerDialog::reject ); connect( dlg, &QgsAbstractDataSourceWidget::accepted, this, &QgsDataSourceManagerDialog::accept ); + makeConnections( dlg, providerKey ); return dlg; } } -QgsAbstractDataSourceWidget *QgsDataSourceManagerDialog::addDbProviderDialog( const QString providerKey, const QString providerName, const QString icon, QString title ) +void QgsDataSourceManagerDialog::makeConnections( QgsAbstractDataSourceWidget *dlg, const QString &providerKey ) { - QgsAbstractDataSourceWidget *dlg = providerDialog( providerKey, providerName, icon, title ); - if ( dlg ) - { - connect( dlg, SIGNAL( addDatabaseLayers( QStringList const &, QString const & ) ), - this, SIGNAL( addDatabaseLayers( QStringList const &, QString const & ) ) ); - connect( dlg, SIGNAL( progress( int, int ) ), - this, SIGNAL( showProgress( int, int ) ) ); - connect( dlg, SIGNAL( progressMessage( QString ) ), - this, SIGNAL( showStatusMessage( QString ) ) ); - connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); - connect( this, SIGNAL( providerDialogsRefreshRequested() ), dlg, SLOT( refresh() ) ); - } - return dlg; -} + // DB + connect( dlg, SIGNAL( addDatabaseLayers( QStringList const &, QString const & ) ), + this, SIGNAL( addDatabaseLayers( QStringList const &, QString const & ) ) ); + connect( dlg, SIGNAL( progress( int, int ) ), + this, SIGNAL( showProgress( int, int ) ) ); + connect( dlg, SIGNAL( progressMessage( QString ) ), + this, SIGNAL( showStatusMessage( QString ) ) ); + // Vector + connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayer, this, [ = ]( const QString & vectorLayerPath, const QString & baseName ) + { this->vectorLayerAdded( vectorLayerPath, baseName, providerKey ); } ); + connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayers, this, &QgsDataSourceManagerDialog::vectorLayersAdded ); connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); + // Raster + connect( dlg, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ), + this, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ) ); -QgsAbstractDataSourceWidget *QgsDataSourceManagerDialog::addRasterProviderDialog( const QString providerKey, const QString providerName, const QString icon, QString title ) -{ - QgsAbstractDataSourceWidget *dlg = providerDialog( providerKey, providerName, icon, title ); - if ( dlg ) - { - connect( dlg, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ), - this, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ) ); - connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); - connect( this, SIGNAL( providerDialogsRefreshRequested() ), dlg, SLOT( refresh() ) ); - } - return dlg; -} - -QgsAbstractDataSourceWidget *QgsDataSourceManagerDialog::addVectorProviderDialog( const QString providerKey, const QString providerName, const QString icon, QString title ) -{ - QgsAbstractDataSourceWidget *dlg = providerDialog( providerKey, providerName, icon, title ); - if ( dlg ) - { - connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayer, this, [ = ]( const QString & vectorLayerPath, const QString & baseName ) - { this->vectorLayerAdded( vectorLayerPath, baseName, providerKey ); } ); - connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayers, this, &QgsDataSourceManagerDialog::vectorLayersAdded ); - connect( this, SIGNAL( providerDialogsRefreshRequested() ), dlg, SLOT( refresh() ) ); - } - return dlg; + // Virtual + connect( dlg, SIGNAL( replaceVectorLayer( QString, QString, QString, QString ) ), this, SIGNAL( replaceSelectedVectorLayer( QString, QString, QString, QString ) ) ); + // Common + connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); + connect( this, SIGNAL( providerDialogsRefreshRequested() ), dlg, SLOT( refresh() ) ); } void QgsDataSourceManagerDialog::showEvent( QShowEvent *e ) diff --git a/src/gui/qgsdatasourcemanagerdialog.h b/src/gui/qgsdatasourcemanagerdialog.h index 0deb9cf9e95..5feaf3058b6 100644 --- a/src/gui/qgsdatasourcemanagerdialog.h +++ b/src/gui/qgsdatasourcemanagerdialog.h @@ -116,9 +116,7 @@ class GUI_EXPORT QgsDataSourceManagerDialog : public QgsOptionsDialogBase, priva private: // Return the dialog from the provider QgsAbstractDataSourceWidget *providerDialog( const QString providerKey, const QString providerName, const QString icon, QString title = QString() ); - QgsAbstractDataSourceWidget *addDbProviderDialog( QString const providerKey, QString const providerName, QString const icon, QString title = QString() ); - QgsAbstractDataSourceWidget *addRasterProviderDialog( QString const providerKey, QString const providerName, QString const icon, QString title = QString() ); - QgsAbstractDataSourceWidget *addVectorProviderDialog( QString const providerKey, QString const providerName, QString const icon, QString title = QString() ); + void makeConnections( QgsAbstractDataSourceWidget *dlg, const QString &providerKey ); Ui::QgsDataSourceManagerDialog *ui; QgsBrowserDockWidget *mBrowserWidget = nullptr; int mPreviousRow; From 1c3e358151b48d81791a77a168e1e84c96b011a5 Mon Sep 17 00:00:00 2001 From: Robert Szczepanek Date: Sun, 3 Sep 2017 23:29:23 +0200 Subject: [PATCH 274/364] New polish translators of QGIS desktop --- scripts/tsstat.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tsstat.pl b/scripts/tsstat.pl index a0889c0303f..1560b6909b8 100755 --- a/scripts/tsstat.pl +++ b/scripts/tsstat.pl @@ -70,7 +70,7 @@ my $translators= { 'mr' => '', 'nb' => 'James Stott, Maléne Peterson', 'nl' => 'Richard Duivenvoorde, Raymond Nijssen, Carlo van Rijswijk, Diethard Jansen, Willem Hoffmans, Dick Groskamp', - 'pl' => 'Robert Szczepanek, Milena Nowotarska, Borys Jurgiel, Mateusz Łoskot, Tomasz Paul, Andrzej Świąder, Radosław Pasiok, Michał Kułach, Ewelina Krawczak, Michał Smoczyk, Jakub Bobrowski', + 'pl' => 'Robert Szczepanek, Milena Nowotarska, Borys Jurgiel, Mateusz Łoskot, Tomasz Paul, Andrzej Świąder, Radosław Pasiok, Michał Kułach, Ewelina Krawczak, Michał Smoczyk, Jakub Bobrowski, Kuba Kiszkurno, Beata Baziak, Bartosz Mazurkiewcz', 'pt_BR' => 'Sidney Schaberle Goveia, Arthur Nanni, Marcelo Soares Souza, Narcélio de Sá Pereira Filho, Leônidas Descovi Filho, Felipe Sodré Barros ', 'pt_PT' => 'Giovanni Manghi, Joana Simões, Duarte Carreira, Alexandre Neto, Pedro Pereira, Pedro Palheiro, Nelson Silva, Ricardo Sena, Leandro Infantini, João Gaspar', 'ro' => 'Sorin Călinică, Tudor Bărăscu, Georgiana Ioanovici, Alex Bădescu, Lonut Losifescu-Enescu, Bogdan Pacurar', From 85e6a633288cffff09830c6dfb5bb432f19158e4 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 08:25:29 +1000 Subject: [PATCH 275/364] Daily Q_FOREACH -> for conversion --- src/core/composer/qgsatlascomposition.cpp | 2 +- .../qgscomposerattributetablemodelv2.cpp | 3 +- src/core/diagram/qgshistogramdiagram.cpp | 4 +-- src/core/dxf/qgsdxfexport.cpp | 36 ++++++++++++------- src/core/effects/qgseffectstack.cpp | 2 +- src/core/expression/qgsexpression.cpp | 14 ++++---- src/core/expression/qgsexpressionfunction.h | 2 +- 7 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/core/composer/qgsatlascomposition.cpp b/src/core/composer/qgsatlascomposition.cpp index eb07f6f6a6c..ad290154df5 100644 --- a/src/core/composer/qgsatlascomposition.cpp +++ b/src/core/composer/qgsatlascomposition.cpp @@ -69,7 +69,7 @@ void QgsAtlasComposition::removeLayers( const QStringList &layers ) return; } - Q_FOREACH ( const QString &layerId, layers ) + for ( const QString &layerId : layers ) { if ( layerId == mCoverageLayer.layerId ) { diff --git a/src/core/composer/qgscomposerattributetablemodelv2.cpp b/src/core/composer/qgscomposerattributetablemodelv2.cpp index a393f9683dc..cbb7d321cc3 100644 --- a/src/core/composer/qgscomposerattributetablemodelv2.cpp +++ b/src/core/composer/qgscomposerattributetablemodelv2.cpp @@ -399,7 +399,8 @@ bool QgsComposerAttributeTableColumnModelV2::moveColumnInSortRank( QgsComposerTa //find column before this one in sort order QList sortedColumns; - Q_FOREACH ( QgsComposerTableColumn *currentColumn, *mComposerTable->columns() ) + const QgsComposerTableColumns columns = *mComposerTable->columns(); + for ( QgsComposerTableColumn *currentColumn : columns ) { if ( currentColumn->sortByRank() > 0 ) { diff --git a/src/core/diagram/qgshistogramdiagram.cpp b/src/core/diagram/qgshistogramdiagram.cpp index df964e1d716..d039c713888 100644 --- a/src/core/diagram/qgshistogramdiagram.cpp +++ b/src/core/diagram/qgshistogramdiagram.cpp @@ -49,7 +49,7 @@ QSizeF QgsHistogramDiagram::diagramSize( const QgsFeature &feature, const QgsRen if ( !feature.fields().isEmpty() ) expressionContext.setFields( feature.fields() ); - Q_FOREACH ( const QString &cat, s.categoryAttributes ) + for ( const QString &cat : qgsAsConst( s.categoryAttributes ) ) { QgsExpression *expression = getExpression( cat, expressionContext ); maxValue = std::max( expression->evaluate( &expressionContext ).toDouble(), maxValue ); @@ -146,7 +146,7 @@ void QgsHistogramDiagram::renderDiagram( const QgsFeature &feature, QgsRenderCon if ( !feature.fields().isEmpty() ) expressionContext.setFields( feature.fields() ); - Q_FOREACH ( const QString &cat, s.categoryAttributes ) + for ( const QString &cat : qgsAsConst( s.categoryAttributes ) ) { QgsExpression *expression = getExpression( cat, expressionContext ); double currentVal = expression->evaluate( &expressionContext ).toDouble(); diff --git a/src/core/dxf/qgsdxfexport.cpp b/src/core/dxf/qgsdxfexport.cpp index e1eea467401..92ec5ccd1a8 100644 --- a/src/core/dxf/qgsdxfexport.cpp +++ b/src/core/dxf/qgsdxfexport.cpp @@ -516,7 +516,8 @@ int QgsDxfExport::writeToFile( QIODevice *d, const QString &encoding ) if ( mExtent.isEmpty() ) { - Q_FOREACH ( QgsMapLayer *ml, mMapSettings.layers() ) + const QList< QgsMapLayer * > layers = mMapSettings.layers(); + for ( QgsMapLayer *ml : layers ) { QgsVectorLayer *vl = qobject_cast( ml ); if ( !vl ) @@ -655,7 +656,8 @@ void QgsDxfExport::writeTables() writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) ); writeGroup( 70, 0 ); - Q_FOREACH ( const QString &block, QStringList() << "*Model_Space" << "*Paper_Space" << "*Paper_Space0" ) + const QStringList blockStrings = QStringList() << "*Model_Space" << "*Paper_Space" << "*Paper_Space0"; + for ( const QString &block : blockStrings ) { writeGroup( 0, QStringLiteral( "BLOCK_RECORD" ) ); mBlockHandles.insert( block, writeHandle() ); @@ -771,7 +773,8 @@ void QgsDxfExport::writeTables() writeGroup( 0, QStringLiteral( "ENDTAB" ) ); QSet layerNames; - Q_FOREACH ( QgsMapLayer *ml, mMapSettings.layers() ) + const QList< QgsMapLayer * > layers = mMapSettings.layers(); + for ( QgsMapLayer *ml : layers ) { if ( !layerIsScaleBasedVisible( ml ) ) continue; @@ -787,8 +790,8 @@ void QgsDxfExport::writeTables() } else { - QSet values = vl->uniqueValues( attrIdx ); - Q_FOREACH ( const QVariant &v, values ) + const QSet values = vl->uniqueValues( attrIdx ); + for ( const QVariant &v : values ) { layerNames << dxfLayerName( v.toString() ); } @@ -813,7 +816,7 @@ void QgsDxfExport::writeTables() writeGroup( 6, QStringLiteral( "CONTINUOUS" ) ); writeHandle( 390, DXF_HANDPLOTSTYLE ); - Q_FOREACH ( const QString &layerName, layerNames ) + for ( const QString &layerName : qgsAsConst( layerNames ) ) { writeGroup( 0, QStringLiteral( "LAYER" ) ); writeHandle(); @@ -859,7 +862,8 @@ void QgsDxfExport::writeBlocks() startSection(); writeGroup( 2, QStringLiteral( "BLOCKS" ) ); - Q_FOREACH ( const QString &block, QStringList() << "*Model_Space" << "*Paper_Space" << "*Paper_Space0" ) + const QStringList blockStrings = QStringList() << "*Model_Space" << "*Paper_Space" << "*Paper_Space0"; + for ( const QString &block : blockStrings ) { writeGroup( 0, QStringLiteral( "BLOCK" ) ); writeHandle(); @@ -968,7 +972,8 @@ void QgsDxfExport::writeEntities() engine.setMapSettings( mMapSettings ); // iterate through the maplayers - Q_FOREACH ( QgsMapLayer *ml, mMapSettings.layers() ) + const QList< QgsMapLayer *> layers = mMapSettings.layers(); + for ( QgsMapLayer *ml : layers ) { QgsVectorLayer *vl = qobject_cast( ml ); if ( !vl || !layerIsScaleBasedVisible( vl ) ) @@ -3978,7 +3983,8 @@ QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsR { QList< QPair< QgsSymbolLayer *, QgsSymbol * > > symbolLayers; - Q_FOREACH ( QgsMapLayer *ml, mMapSettings.layers() ) + const QList< QgsMapLayer * > layers = mMapSettings.layers(); + for ( QgsMapLayer *ml : layers ) { QgsVectorLayer *vl = qobject_cast( ml ); if ( !vl ) @@ -4016,7 +4022,8 @@ QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsR void QgsDxfExport::writeDefaultLinetypes() { // continuous (Qt solid line) - Q_FOREACH ( const QString <ype, QStringList() << "ByLayer" << "ByBlock" << "CONTINUOUS" ) + const QStringList blockStrings = QStringList() << "ByLayer" << "ByBlock" << "CONTINUOUS"; + for ( const QString <ype : blockStrings ) { writeGroup( 0, QStringLiteral( "LTYPE" ) ); writeHandle(); @@ -4232,7 +4239,8 @@ bool QgsDxfExport::layerIsScaleBasedVisible( const QgsMapLayer *layer ) const QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const { - Q_FOREACH ( QgsMapLayer *ml, mMapSettings.layers() ) + const QList< QgsMapLayer * > layers = mMapSettings.layers(); + for ( QgsMapLayer *ml : layers ) { QgsVectorLayer *vl = qobject_cast( ml ); if ( vl && vl->id() == id ) @@ -4247,7 +4255,8 @@ QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const QString QgsDxfExport::dxfEncoding( const QString &name ) { - Q_FOREACH ( const QByteArray &codec, QTextCodec::availableCodecs() ) + const QList< QByteArray > codecs = QTextCodec::availableCodecs(); + for ( const QByteArray &codec : codecs ) { if ( name != codec ) continue; @@ -4268,7 +4277,8 @@ QString QgsDxfExport::dxfEncoding( const QString &name ) QStringList QgsDxfExport::encodings() { QStringList encodings; - Q_FOREACH ( QByteArray codec, QTextCodec::availableCodecs() ) + const QList< QByteArray > codecs = QTextCodec::availableCodecs(); + for ( const QByteArray &codec : codecs ) { int i; for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i ) diff --git a/src/core/effects/qgseffectstack.cpp b/src/core/effects/qgseffectstack.cpp index 7a1be054984..330547e9b3e 100644 --- a/src/core/effects/qgseffectstack.cpp +++ b/src/core/effects/qgseffectstack.cpp @@ -157,7 +157,7 @@ bool QgsEffectStack::saveProperties( QDomDocument &doc, QDomElement &element ) c effectElement.setAttribute( QStringLiteral( "enabled" ), mEnabled ); bool ok = true; - Q_FOREACH ( QgsPaintEffect *effect, mEffectList ) + for ( QgsPaintEffect *effect : mEffectList ) { if ( effect ) ok = ok && effect->saveProperties( doc, effectElement ); diff --git a/src/core/expression/qgsexpression.cpp b/src/core/expression/qgsexpression.cpp index de17a92d229..8b749a98118 100644 --- a/src/core/expression/qgsexpression.cpp +++ b/src/core/expression/qgsexpression.cpp @@ -149,7 +149,8 @@ QString QgsExpression::quotedValue( const QVariant &value, QVariant::Type type ) case QVariant::List: { QStringList quotedValues; - Q_FOREACH ( const QVariant &v, value.toList() ) + const QVariantList values = value.toList(); + for ( const QVariant &v : values ) { quotedValues += quotedValue( v ); } @@ -175,7 +176,8 @@ int QgsExpression::functionIndex( const QString &name ) { if ( QString::compare( name, QgsExpression::Functions()[i]->name(), Qt::CaseInsensitive ) == 0 ) return i; - Q_FOREACH ( const QString &alias, QgsExpression::Functions()[i]->aliases() ) + const QStringList aliases = QgsExpression::Functions()[i]->aliases(); + for ( const QString &alias : aliases ) { if ( QString::compare( name, alias, Qt::CaseInsensitive ) == 0 ) return i; @@ -506,7 +508,7 @@ QString QgsExpression::helpText( QString name ) .arg( tr( "%1 %2" ).arg( f.mType, name ), f.mDescription ) ); - Q_FOREACH ( const HelpVariant &v, f.mVariants ) + for ( const HelpVariant &v : qgsAsConst( f.mVariants ) ) { if ( f.mVariants.size() > 1 ) { @@ -538,7 +540,7 @@ QString QgsExpression::helpText( QString name ) helpContents += '('; QString delim; - Q_FOREACH ( const HelpArg &a, v.mArguments ) + for ( const HelpArg &a : qgsAsConst( v.mArguments ) ) { helpContents += delim; delim = QStringLiteral( ", " ); @@ -564,7 +566,7 @@ QString QgsExpression::helpText( QString name ) { helpContents += QStringLiteral( "

      %1

      \n
      \n" ).arg( tr( "Arguments" ) ); - Q_FOREACH ( const HelpArg &a, v.mArguments ) + for ( const HelpArg &a : qgsAsConst( v.mArguments ) ) { if ( a.mSyntaxOnly ) continue; @@ -579,7 +581,7 @@ QString QgsExpression::helpText( QString name ) { helpContents += QStringLiteral( "

      %1

      \n
      \n
        \n" ).arg( tr( "Examples" ) ); - Q_FOREACH ( const HelpExample &e, v.mExamples ) + for ( const HelpExample &e : qgsAsConst( v.mExamples ) ) { helpContents += "
      • " + e.mExpression + "" + e.mReturns + ""; diff --git a/src/core/expression/qgsexpressionfunction.h b/src/core/expression/qgsexpressionfunction.h index 4734cad60de..2c7a085c592 100644 --- a/src/core/expression/qgsexpressionfunction.h +++ b/src/core/expression/qgsexpressionfunction.h @@ -179,7 +179,7 @@ class CORE_EXPORT QgsExpressionFunction return mParams; int min = 0; - Q_FOREACH ( const Parameter ¶m, mParameterList ) + for ( const Parameter ¶m : mParameterList ) { if ( !param.optional() ) min++; From 83affdc7f531a77cdb963fa8285fdc7af9015c76 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 3 Sep 2017 14:46:16 +1000 Subject: [PATCH 276/364] [FEATURE] New processing algorithm "minimum bounding geometry" This algorithm creates geometries which enclose the features from an input layer. Numerous enclosing geometry types are supported, including bounding boxes (envelopes), oriented rectangles, circles and convex hulls. Optionally, the features can be grouped by a field. If set, this causes the output layer to contain one feature per grouped value with a minimal geometry covering just the features with matching values. --- python/plugins/processing/algs/help/qgis.yaml | 7 + .../algs/qgis/MinimumBoundingGeometry.py | 237 ++++++++++++++++++ .../algs/qgis/QGISAlgorithmProvider.py | 2 + .../testdata/expected/mbg_circle_field.gfs | 37 +++ .../testdata/expected/mbg_circle_field.gml | 50 ++++ .../testdata/expected/mbg_circle_nofield.gfs | 31 +++ .../testdata/expected/mbg_circle_nofield.gml | 22 ++ .../tests/testdata/expected/mbg_env_field.gfs | 47 ++++ .../tests/testdata/expected/mbg_env_field.gml | 58 +++++ .../testdata/expected/mbg_env_nofield.dbf | Bin 0 -> 288 bytes .../testdata/expected/mbg_env_nofield.gfs | 41 +++ .../testdata/expected/mbg_env_nofield.gml | 24 ++ .../testdata/expected/mbg_env_nofield.prj | 1 + .../testdata/expected/mbg_env_nofield.qpj | 1 + .../testdata/expected/mbg_env_nofield.shp | Bin 0 -> 236 bytes .../testdata/expected/mbg_env_nofield.shx | Bin 0 -> 108 bytes .../testdata/expected/mbg_hull_field.gfs | 37 +++ .../testdata/expected/mbg_hull_field.gml | 50 ++++ .../testdata/expected/mbg_hull_nofield.gfs | 31 +++ .../testdata/expected/mbg_hull_nofield.gml | 22 ++ .../testdata/expected/mbg_rect_field.gfs | 52 ++++ .../testdata/expected/mbg_rect_field.gml | 62 +++++ .../testdata/expected/mbg_rect_nofield.gfs | 46 ++++ .../testdata/expected/mbg_rect_nofield.gml | 25 ++ .../tests/testdata/qgis_algorithm_tests.yaml | 152 +++++++++++ 25 files changed, 1035 insertions(+) create mode 100644 python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_circle_field.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_circle_field.gml create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gml create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_field.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_field.gml create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_nofield.dbf create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gml create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_nofield.prj create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_nofield.qpj create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_nofield.shp create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_env_nofield.shx create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_hull_field.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_hull_field.gml create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gml create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_rect_field.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_rect_field.gml create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gml diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index 9fa8531bd82..a488f121bd7 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -324,6 +324,13 @@ qgis:mergevectorlayers: > The layers will all be reprojected to match the coordinate reference system of the first input layer. +qgis:minimumboundinggeometry: > + This algorithm creates geometries which enclose the features from an input layer. + + Numerous enclosing geometry types are supported, including bounding boxes (envelopes), oriented rectangles, circles and convex hulls. + + Optionally, the features can be grouped by a field. If set, this causes the output layer to contain one feature per grouped value with a minimal geometry covering just the features with matching values. + qgis:multiparttosingleparts: > This algorithm takes a vector layer with multipart geometries and generates a new one in which all geometries contain a single part. Features with multipart geometries are divided in as many different features as parts the geometry contain, and the same attributes are used for each of them. diff --git a/python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py b/python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py new file mode 100644 index 00000000000..de4f9041b27 --- /dev/null +++ b/python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + MinimumBoundingGeometry.py + -------------------------- + Date : September 2017 + Copyright : (C) 2017 by Nyall Dawson + Email : nyall dot dawson at gmail dot com +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" +from builtins import str + +__author__ = 'Nyall Dawson' +__date__ = 'September 2017' +__copyright__ = '(C) 2017, Nyall Dawson' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +import os +import math + +from qgis.PyQt.QtGui import QIcon +from qgis.PyQt.QtCore import QVariant + +from qgis.core import (QgsField, + QgsFeatureSink, + QgsGeometry, + QgsWkbTypes, + QgsFeatureRequest, + QgsFields, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterField, + QgsProcessingParameterEnum, + QgsProcessingParameterFeatureSink, + QgsProcessing, + QgsFeature, + QgsVertexId, + QgsMultiPointV2) + +from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm + +pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] + + +class MinimumBoundingGeometry(QgisAlgorithm): + + INPUT = 'INPUT' + OUTPUT = 'OUTPUT' + TYPE = 'TYPE' + FIELD = 'FIELD' + + def icon(self): + return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png')) + + def group(self): + return self.tr('Vector geometry') + + def __init__(self): + super().__init__() + self.type_names = [self.tr('Envelope (Bounding Box)'), + self.tr('Minimum Oriented Rectangle'), + self.tr('Minimum Enclosing Circle'), + self.tr('Convex Hull')] + + def initAlgorithm(self, config=None): + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterField(self.FIELD, + self.tr('Field (optional, set if features should be grouped by class)'), + parentLayerParameterName=self.INPUT, optional=True)) + self.addParameter(QgsProcessingParameterEnum(self.TYPE, + self.tr('Geometry type'), options=self.type_names)) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding geometry'), QgsProcessing.TypeVectorPolygon)) + + def name(self): + return 'minimumboundinggeometry' + + def displayName(self): + return self.tr('Minimum bounding geometry') + + def tags(self): + return self.tr('bounding,box,bounds,envelope,minimum,oriented,rectangle,enclosing,circle,convex,hull,generalization').split(',') + + def processAlgorithm(self, parameters, context, feedback): + source = self.parameterAsSource(parameters, self.INPUT, context) + field_name = self.parameterAsString(parameters, self.FIELD, context) + type = self.parameterAsEnum(parameters, self.TYPE, context) + use_field = bool(field_name) + + field_index = -1 + + fields = QgsFields() + fields.append(QgsField('id', QVariant.Int, '', 20)) + + if use_field: + # keep original field type, name and parameters + field_index = source.fields().lookupField(field_name) + if field_index >= 0: + fields.append(source.fields()[field_index]) + if type == 0: + #envelope + fields.append(QgsField('width', QVariant.Double, '', 20, 6)) + fields.append(QgsField('height', QVariant.Double, '', 20, 6)) + fields.append(QgsField('area', QVariant.Double, '', 20, 6)) + fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) + elif type == 1: + #oriented rect + fields.append(QgsField('width', QVariant.Double, '', 20, 6)) + fields.append(QgsField('height', QVariant.Double, '', 20, 6)) + fields.append(QgsField('angle', QVariant.Double, '', 20, 6)) + fields.append(QgsField('area', QVariant.Double, '', 20, 6)) + fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) + elif type == 2: + # circle + fields.append(QgsField('radius', QVariant.Double, '', 20, 6)) + fields.append(QgsField('area', QVariant.Double, '', 20, 6)) + elif type == 3: + # convex hull + fields.append(QgsField('area', QVariant.Double, '', 20, 6)) + fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) + + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, QgsWkbTypes.Polygon, source.sourceCrs()) + + if field_index >= 0: + geometry_dict = {} + total = 50.0 / source.featureCount() if source.featureCount() else 1 + features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index])) + for current, f in enumerate(features): + if feedback.isCanceled(): + break + + if not f.hasGeometry(): + continue + + if not f.attributes()[field_index] in geometry_dict: + geometry_dict[f.attributes()[field_index]] = [f.geometry()] + else: + geometry_dict[f.attributes()[field_index]].append(f.geometry()) + + feedback.setProgress(int(current * total)) + + current = 0 + total = 50.0 / len(geometry_dict) if geometry_dict else 1 + for group, geometries in geometry_dict.items(): + if feedback.isCanceled(): + break + + feature = self.createFeature(feedback, current, type, geometries, group) + sink.addFeature(feature, QgsFeatureSink.FastInsert) + geometry_dict[group] = None + + feedback.setProgress(50 + int(current * total)) + current += 1 + else: + total = 80.0 / source.featureCount() if source.featureCount() else 1 + features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])) + geometry_queue = [] + for current, f in enumerate(features): + if feedback.isCanceled(): + break + + if not f.hasGeometry(): + continue + + geometry_queue.append(f.geometry()) + feedback.setProgress(int(current * total)) + + if not feedback.isCanceled(): + feature = self.createFeature(feedback, 0, type, geometry_queue) + sink.addFeature(feature, QgsFeatureSink.FastInsert) + + return {self.OUTPUT: dest_id} + + def createFeature(self, feedback, feature_id, type, geometries, class_field=None): + attrs = [feature_id] + if class_field is not None: + attrs.append(class_field) + + multi_point = QgsMultiPointV2() + + for g in geometries: + if feedback.isCanceled(): + break + + vid = QgsVertexId() + while True: + if feedback.isCanceled(): + break + found, point = g.geometry().nextVertex(vid) + if found: + multi_point.addGeometry(point) + else: + break + + geometry = QgsGeometry(multi_point) + output_geometry = None + if type == 0: + # envelope + rect = geometry.boundingBox() + output_geometry = QgsGeometry.fromRect(rect) + attrs.append(rect.width()) + attrs.append(rect.height()) + attrs.append(rect.area()) + attrs.append(rect.perimeter()) + elif type == 1: + # oriented rect + output_geometry, area, angle, width, height = geometry.orientedMinimumBoundingBox() + attrs.append(width) + attrs.append(height) + attrs.append(angle) + attrs.append(area) + attrs.append(2 * width + 2 * height) + elif type == 2: + # circle + output_geometry, center, radius = geometry.minimalEnclosingCircle(segments=72) + attrs.append(radius) + attrs.append(math.pi * radius * radius) + elif type == 3: + # convex hull + output_geometry = geometry.convexHull() + attrs.append(output_geometry.geometry().area()) + attrs.append(output_geometry.geometry().perimeter()) + f = QgsFeature() + f.setAttributes(attrs) + f.setGeometry(output_geometry) + return f diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 1a532f004dc..7299b188c5c 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -100,6 +100,7 @@ from .LinesToPolygons import LinesToPolygons from .MeanCoords import MeanCoords from .Merge import Merge from .MergeLines import MergeLines +from .MinimumBoundingGeometry import MinimumBoundingGeometry from .MinimalEnclosingCircle import MinimalEnclosingCircle from .NearestNeighbourAnalysis import NearestNeighbourAnalysis from .OffsetLine import OffsetLine @@ -254,6 +255,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): MeanCoords(), Merge(), MergeLines(), + MinimumBoundingGeometry(), MinimalEnclosingCircle(), NearestNeighbourAnalysis(), OffsetLine(), diff --git a/python/plugins/processing/tests/testdata/expected/mbg_circle_field.gfs b/python/plugins/processing/tests/testdata/expected/mbg_circle_field.gfs new file mode 100644 index 00000000000..6f4e6c288a7 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_circle_field.gfs @@ -0,0 +1,37 @@ + + + mbg_circle_field + mbg_circle_field + + 3 + EPSG:4326 + + 4 + -2.10952 + 9.37006 + -4.60952 + 7.10328 + + + id + id + Integer + + + name + name + String + 2 + + + radius + radius + Real + + + area + area + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_circle_field.gml b/python/plugins/processing/tests/testdata/expected/mbg_circle_field.gml new file mode 100644 index 00000000000..f941136cf1b --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_circle_field.gml @@ -0,0 +1,50 @@ + + + + + -2.109518379976041-4.609518379976041 + 9.3700626353132297.103275517095002 + + + + + + -1.0,3.0 -0.725214215078135,3.29362919389204 -0.425882602541937,3.56219188087088 -0.104283256704178,3.80364413672603 0.237136257226325,4.01614836399757 0.595777530593825,4.1980872772024 0.968911086754466,4.34807621135332 1.35369715404167,4.46497366009564 1.7472072781432,4.54788996325982 2.14644660940673,4.59619407771256 2.54837669545405,4.60951837997604 2.94993860563831,4.58776146406461 3.34807621135331,4.53108891324554 3.7397594450175,4.43993203985037 4.12200736071789,4.31498460272768 4.49191082100838,4.1571975273193 4.84665463720237,3.96777166854352 5.18353899465843,3.74814867156408 5.5,3.5 5.79362919389204,3.22521421507814 6.06219188087088,2.92588260254194 6.30364413672603,2.60428325670418 6.51614836399756,2.26286374277367 6.6980872772024,1.90422246940618 6.84807621135332,1.53108891324554 6.96497366009564,1.14630284595834 7.04788996325982,0.752792721856805 7.09619407771256,0.353553390593274 7.10951837997604,-0.048376695454046 7.08776146406461,-0.449938605638313 7.03108891324554,-0.848076211353317 6.93993203985037,-1.2397594450175 6.81498460272769,-1.62200736071788 6.6571975273193,-1.99191082100838 6.46777166854352,-2.34665463720237 6.24814867156408,-2.68353899465843 6.0,-3.0 5.72521421507814,-3.29362919389204 5.42588260254194,-3.56219188087088 5.10428325670418,-3.80364413672603 4.76286374277367,-4.01614836399757 4.40422246940618,-4.1980872772024 4.03108891324554,-4.34807621135332 3.64630284595833,-4.46497366009564 3.2527927218568,-4.54788996325982 2.85355339059327,-4.59619407771256 2.45162330454595,-4.60951837997604 2.05006139436169,-4.58776146406461 1.65192378864669,-4.53108891324554 1.2602405549825,-4.43993203985037 0.877992639282118,-4.31498460272769 0.508089178991617,-4.1571975273193 0.153345362797632,-3.96777166854352 -0.183538994658433,-3.74814867156408 -0.5,-3.5 -0.793629193892039,-3.22521421507814 -1.06219188087088,-2.92588260254194 -1.30364413672603,-2.60428325670418 -1.51614836399756,-2.26286374277368 -1.6980872772024,-1.90422246940618 -1.84807621135332,-1.53108891324553 -1.96497366009564,-1.14630284595833 -2.04788996325982,-0.752792721856805 -2.09619407771256,-0.353553390593274 -2.10951837997604,0.048376695454046 -2.08776146406461,0.449938605638312 -2.03108891324554,0.848076211353314 -1.93993203985037,1.2397594450175 -1.81498460272769,1.62200736071788 -1.6571975273193,1.99191082100838 -1.46777166854352,2.34665463720237 -1.24814867156408,2.68353899465843 -1.0,3.0 + 0 + aa + 4.609772 + 66.758844 + + + + + 7.24145873320538,-1.05451055662188 7.23765343129712,-1.14166629936954 7.22626648621758,-1.22815873428881 7.20738455949444,-1.3133296017244 7.18115135399128,-1.39653069994755 7.14776652024203,-1.47712881836258 7.10748413698981,-1.55451055662188 7.06061077749437,-1.62808699297293 7.00750317632435,-1.69729816630842 6.94856551439192,-1.76161733780843 6.88424634289191,-1.82055499974086 6.81503516955642,-1.87366260091087 6.74145873320538,-1.92053596040632 6.66407699494607,-1.96081834365853 6.58347887653104,-1.99420317740779 6.5002777783079,-2.02043638291095 6.41510691087231,-2.03931830963409 6.32861447595303,-2.05070525471363 6.24145873320538,-2.05451055662188 6.15430299045772,-2.05070525471363 6.06781055553845,-2.03931830963409 5.98263968810285,-2.02043638291095 5.89943858987971,-1.99420317740779 5.81884047146468,-1.96081834365853 5.74145873320538,-1.92053596040632 5.66788229685433,-1.87366260091087 5.59867112351884,-1.82055499974086 5.53435195201883,-1.76161733780843 5.4754142900864,-1.69729816630842 5.42230668891638,-1.62808699297293 5.37543332942094,-1.55451055662188 5.33515094616873,-1.47712881836258 5.30176611241947,-1.39653069994755 5.27553290691631,-1.3133296017244 5.25665098019317,-1.22815873428881 5.24526403511363,-1.14166629936954 5.24145873320538,-1.05451055662188 5.24526403511363,-0.967354813874224 5.25665098019317,-0.880862378954952 5.27553290691631,-0.795691511519361 5.30176611241947,-0.712490413296213 5.33515094616873,-0.631892294881182 5.37543332942094,-0.554510556621882 5.42230668891638,-0.480934120270836 5.4754142900864,-0.411722946935343 5.53435195201883,-0.347403775435334 5.59867112351884,-0.288466113502904 5.66788229685433,-0.23535851233289 5.74145873320538,-0.188485152837443 5.81884047146468,-0.148202769585232 5.89943858987971,-0.114817935835974 5.98263968810285,-0.088584730332814 6.06781055553845,-0.069702803609674 6.15430299045772,-0.058315858530136 6.24145873320538,-0.054510556621882 6.32861447595303,-0.058315858530136 6.41510691087231,-0.069702803609674 6.5002777783079,-0.088584730332814 6.58347887653104,-0.114817935835973 6.66407699494607,-0.148202769585232 6.74145873320538,-0.188485152837443 6.81503516955642,-0.23535851233289 6.88424634289191,-0.288466113502904 6.94856551439192,-0.347403775435335 7.00750317632435,-0.411722946935343 7.06061077749437,-0.480934120270836 7.10748413698981,-0.554510556621882 7.14776652024203,-0.631892294881182 7.18115135399128,-0.712490413296213 7.20738455949444,-0.795691511519361 7.22626648621758,-0.880862378954952 7.23765343129712,-0.967354813874224 7.24145873320538,-1.05451055662188 + 1 + dd + 1.000000 + 3.141593 + + + + + 5.17255278310941,4.82264875239923 5.11521006129984,4.6866357437648 5.04623123106812,4.55613805320215 4.96614126296287,4.43214884693314 4.87554969000102,4.31561175768419 4.77514596875349,4.20741370307151 4.66569423216439,4.10837813562248 4.54802747403795,4.01925877580469 4.42304120945263,3.9407338757581 4.29168665935069,3.87340105738669 4.15496351117246,3.81777276409493 4.01391231063126,3.77427236078406 3.86960654253227,3.74323091178954 3.72314446090491,3.72488466128155 3.5756407306266,3.71937323530423 3.42821794414994,3.72673857913715 3.28199807789633,3.74692463806644 3.13809395333793,3.77977778399495 2.99760076775432,3.82504798464491 2.86158775911988,3.88239070645448 2.73109006855724,3.9513695366862 2.60710086228822,4.03145950479144 2.49056377303928,4.1220510777533 2.3823657184266,4.22245479900083 2.28333015097757,4.33190653558992 2.19421079115978,4.44957329371637 2.11568589111318,4.57455955830169 2.04835307274177,4.70591410840363 1.99272477945002,4.84263725658186 1.94922437613915,4.98368845712305 1.91818292714463,5.12799422522205 1.89983667663664,5.2744563068494 1.89432525065932,5.42196003712771 1.90169059449224,5.56938282360437 1.92187665342152,5.71560268985798 1.95472979935004,5.85950681441638 2,6 2.05734272180957,6.13601300863443 2.12632155204128,6.26651069919708 2.20641152014653,6.3904999054661 2.29700309310839,6.50703699471504 2.39740681435592,6.61523504932772 2.50685855094501,6.71427061677675 2.62452530907146,6.80338997659454 2.74951157365678,6.88191487664113 2.88086612375872,6.94924769501255 3.01758927193695,7.0048759883043 3.15864047247814,7.04837639161517 3.30294624057714,7.07941784060969 3.44940832220449,7.09776409111768 3.5969120524828,7.103275517095 3.74433483895946,7.09591017326208 3.89055470521307,7.0757241143328 4.03445882977147,7.04287096840428 4.17495201535509,6.99760076775432 4.31096502398952,6.94025804594475 4.44146271455217,6.87127921571304 4.56545192082118,6.79118924760779 4.68198901007013,6.70059767464593 4.79018706468281,6.6001939533984 4.88922263213184,6.49074221680931 4.97834199194963,6.37307545868286 5.05686689199622,6.24808919409754 5.12419971036763,6.1167346439956 5.17982800365939,5.98001149581737 5.22332840697026,5.83896029527618 5.25436985596478,5.69465452717718 5.27271610647277,5.54819244554983 5.27822753245009,5.40068871527152 5.27086218861717,5.25326592879486 5.25067612968788,5.10704606254125 5.21782298375937,4.96314193798285 5.17255278310941,4.82264875239923 + 2 + bb + 1.691985 + 8.993788 + + + + + 9.16295585412668,3.73877159309021 9.20463107454638,3.69329107076225 9.24218381946625,3.64435138076285 9.27532828982248,3.59232498368348 9.30381223618247,3.53760783182033 9.32741887851536,3.48061635573819 9.3459685560189,3.42178429498243 9.3593200944467,3.36155939705918 9.36737188052944,3.30040000980643 9.37006263531323,3.23877159309021 9.36737188052944,3.17714317637399 9.3593200944467,3.11598378912124 9.3459685560189,3.05575889119799 9.32741887851536,2.99692683044224 9.30381223618247,2.93993535436009 9.27532828982248,2.88521820249694 9.24218381946625,2.83319180541757 9.20463107454638,2.78425211541817 9.16295585412668,2.73877159309021 9.11747533179873,2.69709637267051 9.06853564179932,2.65954362775064 9.01650924471996,2.62639915739442 8.9617920928568,2.59791521103442 8.90480061677466,2.57430856870154 8.8459685560189,2.55575889119799 8.78574365809565,2.54240735277019 8.7245842708429,2.53435556668745 8.66295585412668,2.53166481190366 8.60132743741046,2.53435556668745 8.54016805015771,2.54240735277019 8.47994315223446,2.55575889119799 8.42111109147871,2.57430856870154 8.36411961539656,2.59791521103442 8.30940246353341,2.62639915739442 8.25737606645404,2.65954362775064 8.20843637645464,2.69709637267051 8.16295585412668,2.73877159309021 8.12128063370698,2.78425211541817 8.08372788878711,2.83319180541757 8.05058341843089,2.88521820249694 8.02209947207089,2.93993535436009 7.99849282973801,2.99692683044224 7.97994315223446,3.05575889119799 7.96659161380666,3.11598378912124 7.95853982772392,3.17714317637399 7.95584907294014,3.23877159309021 7.95853982772392,3.30040000980643 7.96659161380666,3.36155939705918 7.97994315223446,3.42178429498243 7.99849282973801,3.48061635573819 8.02209947207089,3.53760783182033 8.05058341843089,3.59232498368348 8.08372788878711,3.64435138076285 8.12128063370698,3.69329107076225 8.16295585412668,3.73877159309021 8.20843637645464,3.78044681350991 8.25737606645404,3.81799955842978 8.30940246353341,3.85114402878601 8.36411961539656,3.879627975146 8.42111109147871,3.90323461747889 8.47994315223446,3.92178429498243 8.54016805015771,3.93513583341023 8.60132743741046,3.94318761949297 8.66295585412668,3.94587837427676 8.7245842708429,3.94318761949297 8.78574365809565,3.93513583341023 8.8459685560189,3.92178429498243 8.90480061677466,3.90323461747889 8.9617920928568,3.879627975146 9.01650924471996,3.85114402878601 9.06853564179932,3.81799955842978 9.11747533179873,3.78044681350991 9.16295585412668,3.73877159309021 + 3 + cc + 0.707107 + 1.570796 + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gfs b/python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gfs new file mode 100644 index 00000000000..9480f0eb975 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gfs @@ -0,0 +1,31 @@ + + + mbg_circle_nofield + mbg_circle_nofield + + 3 + EPSG:4326 + + 1 + -1.52525 + 9.68821 + -4.23734 + 6.97611 + + + id + id + Integer + + + radius + radius + Real + + + area + area + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gml b/python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gml new file mode 100644 index 00000000000..861bd973050 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_circle_nofield.gml @@ -0,0 +1,22 @@ + + + + + -1.525250794618014-4.237342925136248 + 9.6882066487446966.976114518226461 + + + + + + 9.16295585412668,3.73877159309021 9.35012487542772,3.28687538180819 9.49719631215569,2.82038591697045 9.60305086187294,2.34285346507845 9.6668829075394,1.85791233643306 9.6882066487447,1.36925322583872 9.66685979894264,0.880595124187521 9.60300482054979,0.395657014692835 9.49712768850842,-0.081870430818441 9.35003419172372,-0.548352940147011 9.16284380052371,-1.00024029972705 8.93698114681406,-1.43409337389509 8.6741651817691,-1.84661027878908 8.37639609357545,-2.23465151167826 8.04594008479214,-2.59526384447463 7.68531212518082,-2.92570279958194 7.29725681126725,-3.22345353702731 6.8847274783045,-3.48624999391199 6.45086372360845,-3.71209213051824 5.99896751232643,-3.89926115181927 5.53247804748869,-4.04633258854724 5.05494559559668,-4.15218713826449 4.57000446695129,-4.21601918393095 4.08134535635697,-4.23734292513625 3.59268725470576,-4.21599607533419 3.10774914521108,-4.15214109694135 2.6302216996998,-4.04626396489997 2.16373919037122,-3.89917046811528 1.71185183079119,-3.71198007691526 1.27799875662314,-3.48611742320561 0.865481851729151,-3.22330145816065 0.477440618839976,-2.925532369967 0.116828286043605,-2.5950763611837 -0.213610669063699,-2.23444840157238 -0.511361406509074,-1.8463930876588 -0.774157863393753,-1.43386375469605 -1.0,-1.0 -1.18716902130103,-0.54810378871798 -1.33424045802901,-0.081614323880241 -1.44009500774626,0.395918128011764 -1.50392705341272,0.880859256657155 -1.52525079461801,1.36951836725148 -1.50390394481595,1.85817646890269 -1.44004896642311,2.34311457839737 -1.33417183438174,2.82064202390865 -1.18707833759704,3.28712453323722 -0.999887946397026,3.73901189281726 -0.774025292687375,4.1728649669853 -0.511209327642419,4.58538187187929 -0.213440239448766,4.97342310476847 0.117015769334537,5.33403543756484 0.477643728945859,5.66447439267215 0.865699042859437,5.96222513011752 1.27822837582218,6.2250215870022 1.71209213051824,6.45086372360845 2.16398834180026,6.63803274490948 2.63047780663799,6.78510418163745 3.10801025853,6.8909587313547 3.59295138717539,6.95479077702116 4.08161049776972,6.97611451822646 4.57026859942092,6.9547676684244 5.05520670891561,6.89091269003156 5.53273415442689,6.78503555799019 5.99921666375546,6.63794206120549 6.45110402333549,6.45075167000547 6.88495709750354,6.22488901629582 7.29747400239753,5.96207305125087 7.6855152352867,5.66430396305721 8.04612756808307,5.33384795427391 8.37656652319038,4.97321999466259 8.67431726063576,4.58516468074901 8.93711371752043,4.17263534778626 9.16295585412668,3.73877159309021 + 0 + 5.606729 + 98.757244 + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_field.gfs b/python/plugins/processing/tests/testdata/expected/mbg_env_field.gfs new file mode 100644 index 00000000000..958c6c83e5a --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_env_field.gfs @@ -0,0 +1,47 @@ + + + mbg_env_field + mbg_env_field + + 3 + EPSG:4326 + + 4 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + id + id + Integer + + + name + name + String + 2 + + + width + width + Real + + + height + height + Real + + + area + area + Real + + + perimeter + perimeter + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_field.gml b/python/plugins/processing/tests/testdata/expected/mbg_env_field.gml new file mode 100644 index 00000000000..1d7be01a727 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_env_field.gml @@ -0,0 +1,58 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + + -1,-3 6,-3 6,3 -1,3 -1,-3 + 0 + aa + 7.000000 + 6.000000 + 42.000000 + 26.000000 + + + + + 5.24145873320538,-1.05451055662188 7.24145873320538,-1.05451055662188 7.24145873320538,-0.054510556621882 5.24145873320538,-0.054510556621882 5.24145873320538,-1.05451055662188 + 1 + dd + 2.000000 + 1.000000 + 2.000000 + 6.000000 + + + + + 2.0,4.42360844529751 5.17255278310941,4.42360844529751 5.17255278310941,6.08867562380038 2.0,6.08867562380038 2.0,4.42360844529751 + 2 + bb + 3.172553 + 1.665067 + 5.282514 + 9.675240 + + + + + 8.16295585412668,2.73877159309021 9.16295585412668,2.73877159309021 9.16295585412668,3.73877159309021 8.16295585412668,3.73877159309021 8.16295585412668,2.73877159309021 + 3 + cc + 1.000000 + 1.000000 + 1.000000 + 4.000000 + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.dbf b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.dbf new file mode 100644 index 0000000000000000000000000000000000000000..415dc9dfd79b1e7546910e34707a1788d8f58257 GIT binary patch literal 288 zcmZQB5FL150F;#uj>}2F6Ba#sDR|9VY+) literal 0 HcmV?d00001 diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gfs b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gfs new file mode 100644 index 00000000000..96a4d54c272 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gfs @@ -0,0 +1,41 @@ + + + mbg_env_nofield + mbg_env_nofield + + 3 + EPSG:4326 + + 1 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + id + id + Integer + + + width + width + Real + + + height + height + Real + + + area + area + Real + + + perimeter + perimeter + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gml b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gml new file mode 100644 index 00000000000..dd0621e7d38 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.gml @@ -0,0 +1,24 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + + -1,-3 9.16295585412668,-3.0 9.16295585412668,6.08867562380038 -1.0,6.08867562380038 -1,-3 + 0 + 10.162956 + 9.088676 + 92.367809 + 38.503263 + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.prj b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.prj new file mode 100644 index 00000000000..a30c00a55de --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.qpj b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.qpj new file mode 100644 index 00000000000..5fbc831e743 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.shp b/python/plugins/processing/tests/testdata/expected/mbg_env_nofield.shp new file mode 100644 index 0000000000000000000000000000000000000000..24bc5d2a014677e01df1b7ea5e375370e5a79eed GIT binary patch literal 236 zcmZQzQ0HR64$59IGcd5i + + mbg_hull_field + mbg_hull_field + + 3 + EPSG:4326 + + 4 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + id + id + Integer + + + name + name + String + 2 + + + area + area + Real + + + perimeter + perimeter + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_hull_field.gml b/python/plugins/processing/tests/testdata/expected/mbg_hull_field.gml new file mode 100644 index 00000000000..71ee4ad10e4 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_hull_field.gml @@ -0,0 +1,50 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + + 6,-3 -1,-1 -1,3 3,3 6,1 6,-3 + 0 + aa + 32.000000 + 22.885661 + + + + + 5.24145873320538,-1.05451055662188 6.24145873320538,-0.054510556621882 7.24145873320538,-1.05451055662188 5.24145873320538,-1.05451055662188 + 1 + dd + 1.000000 + 4.828427 + + + + + 2.44337811900192,4.42360844529751 2,5 2,6 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 5.17255278310941,5.82264875239923 5.17255278310941,4.82264875239923 3.44337811900192,4.42360844529751 2.44337811900192,4.42360844529751 + 2 + bb + 4.575793 + 8.703307 + + + + + 8.16295585412668,2.73877159309021 8.16295585412668,3.73877159309021 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 8.16295585412668,2.73877159309021 + 3 + cc + 1.000000 + 4.000000 + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gfs b/python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gfs new file mode 100644 index 00000000000..48dfb632105 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gfs @@ -0,0 +1,31 @@ + + + mbg_hull_nofield + mbg_hull_nofield + + 3 + EPSG:4326 + + 1 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + id + id + Integer + + + area + area + Real + + + perimeter + perimeter + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gml b/python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gml new file mode 100644 index 00000000000..4520296dbf7 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_hull_nofield.gml @@ -0,0 +1,22 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + + 6,-3 -1,-1 -1,3 2,6 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 5.17255278310941,5.82264875239923 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 7.24145873320538,-1.05451055662188 6,-3 + 0 + 66.558273 + 30.786042 + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_rect_field.gfs b/python/plugins/processing/tests/testdata/expected/mbg_rect_field.gfs new file mode 100644 index 00000000000..e9cc8449a23 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_rect_field.gfs @@ -0,0 +1,52 @@ + + + mbg_rect_field + mbg_rect_field + + 3 + EPSG:4326 + + 4 + -2.05660 + 9.16296 + -3.00000 + 6.08868 + + + id + id + Integer + + + name + name + String + 2 + + + width + width + Real + + + height + height + Real + + + angle + angle + Real + + + area + area + Real + + + perimeter + perimeter + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_rect_field.gml b/python/plugins/processing/tests/testdata/expected/mbg_rect_field.gml new file mode 100644 index 00000000000..0f39371ea18 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_rect_field.gml @@ -0,0 +1,62 @@ + + + + + -2.056603773584904-3 + 9.1629558541266816.088675623800386 + + + + + + 6,-3 7.35849056603774,1.75471698113208 -0.698113207547169,4.05660377358491 -2.0566037735849,-0.698113207547172 6,-3 + 0 + aa + 4.944980 + 8.378994 + 105.945396 + 41.433962 + 26.647949 + + + + + 5.24145873320538,-0.054510556621882 5.24145873320538,-1.05451055662188 7.24145873320538,-1.05451055662188 7.24145873320538,-0.054510556621882 5.24145873320538,-0.054510556621882 + 1 + dd + 1.000000 + 2.000000 + 90.000000 + 2.000000 + 6.000000 + + + + + 2.0,6.08867562380039 2.0,4.4236084452975 5.1725527831094,4.4236084452975 5.1725527831094,6.08867562380039 2.0,6.08867562380039 + 2 + bb + 1.665067 + 3.172553 + 90.000000 + 5.282514 + 9.675240 + + + + + 8.16295585412668,2.73877159309021 9.16295585412668,2.73877159309021 9.16295585412668,3.73877159309021 8.16295585412668,3.73877159309021 8.16295585412668,2.73877159309021 + 3 + cc + 1.000000 + 1.000000 + 0.000000 + 1.000000 + 4.000000 + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gfs b/python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gfs new file mode 100644 index 00000000000..a193607f964 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gfs @@ -0,0 +1,46 @@ + + + mbg_rect_nofield + mbg_rect_nofield + + 3 + EPSG:4326 + + 1 + -2.05660 + 9.67640 + -3.40238 + 7.24010 + + + id + id + Integer + + + width + width + Real + + + height + height + Real + + + angle + angle + Real + + + area + area + Real + + + perimeter + perimeter + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gml b/python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gml new file mode 100644 index 00000000000..393e28a1665 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/mbg_rect_nofield.gml @@ -0,0 +1,25 @@ + + + + + -2.056603773584904-3.402382935573825 + 9.6764024191504037.240104298699887 + + + + + + 7.40834027450839,-3.40238293557382 9.6764024191504,4.53583457067323 0.211458371057112,7.24010429869989 -2.0566037735849,-0.698113207547172 7.40834027450839,-3.40238293557382 + 0 + 8.255871 + 9.843690 + 105.945396 + 81.268236 + 36.199122 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index f4e544cafe8..4ae0e6f4dbb 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3269,3 +3269,155 @@ tests: compare: geometry: precision: 7 + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (hull, no field) + params: + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 3 + results: + OUTPUT: + name: expected/mbg_hull_nofield.gml + type: vector + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (hull, field) + params: + FIELD: name + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 3 + results: + OUTPUT: + name: expected/mbg_hull_field.gml + type: vector + pk: name + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (circle, field) + params: + FIELD: name + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 2 + results: + OUTPUT: + name: expected/mbg_circle_field.gml + type: vector + pk: name + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (circle, no field) + params: + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 2 + results: + OUTPUT: + name: expected/mbg_circle_nofield.gml + type: vector + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (oriented rect, no field) + params: + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 1 + results: + OUTPUT: + name: expected/mbg_rect_nofield.gml + type: vector + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (oriented rect, field) + params: + FIELD: name + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 1 + results: + OUTPUT: + name: expected/mbg_rect_field.gml + type: vector + pk: name + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (envelope, field) + params: + FIELD: name + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 0 + results: + OUTPUT: + name: expected/mbg_env_field.gml + type: vector + pk: name + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip + + - algorithm: qgis:minimumboundinggeometry + name: Minimum enclosing geom (envelope, no field) + params: + INPUT: + name: dissolve_polys.gml + type: vector + TYPE: 0 + results: + OUTPUT: + name: expected/mbg_env_nofield.gml + type: vector + compare: + geometry: + precision: 7 + fields: + fid: skip + id: skip From b6e35428e216f25eaf24d12b0fa29a8454a2ce3f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 3 Sep 2017 18:31:31 +1000 Subject: [PATCH 277/364] Optimise calculation of envelopes for MinimumBoundingGeometry alg It's more efficient to calculate these on the fly, rather then collecting all geometry points and then calculating. --- .../algs/qgis/MinimumBoundingGeometry.py | 83 ++++++++++++++----- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py b/python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py index de4f9041b27..a759a503ff8 100644 --- a/python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py +++ b/python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py @@ -38,6 +38,7 @@ from qgis.core import (QgsField, QgsWkbTypes, QgsFeatureRequest, QgsFields, + QgsRectangle, QgsProcessingParameterFeatureSource, QgsProcessingParameterField, QgsProcessingParameterEnum, @@ -53,7 +54,6 @@ pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] class MinimumBoundingGeometry(QgisAlgorithm): - INPUT = 'INPUT' OUTPUT = 'OUTPUT' TYPE = 'TYPE' @@ -76,11 +76,13 @@ class MinimumBoundingGeometry(QgisAlgorithm): self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterField(self.FIELD, - self.tr('Field (optional, set if features should be grouped by class)'), + self.tr( + 'Field (optional, set if features should be grouped by class)'), parentLayerParameterName=self.INPUT, optional=True)) self.addParameter(QgsProcessingParameterEnum(self.TYPE, self.tr('Geometry type'), options=self.type_names)) - self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding geometry'), QgsProcessing.TypeVectorPolygon)) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding geometry'), + QgsProcessing.TypeVectorPolygon)) def name(self): return 'minimumboundinggeometry' @@ -89,7 +91,9 @@ class MinimumBoundingGeometry(QgisAlgorithm): return self.tr('Minimum bounding geometry') def tags(self): - return self.tr('bounding,box,bounds,envelope,minimum,oriented,rectangle,enclosing,circle,convex,hull,generalization').split(',') + return self.tr( + 'bounding,box,bounds,envelope,minimum,oriented,rectangle,enclosing,circle,convex,hull,generalization').split( + ',') def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) @@ -108,13 +112,13 @@ class MinimumBoundingGeometry(QgisAlgorithm): if field_index >= 0: fields.append(source.fields()[field_index]) if type == 0: - #envelope + # envelope fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 1: - #oriented rect + # oriented rect fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('angle', QVariant.Double, '', 20, 6)) @@ -134,6 +138,7 @@ class MinimumBoundingGeometry(QgisAlgorithm): if field_index >= 0: geometry_dict = {} + bounds_dict = {} total = 50.0 / source.featureCount() if source.featureCount() else 1 features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index])) for current, f in enumerate(features): @@ -143,29 +148,56 @@ class MinimumBoundingGeometry(QgisAlgorithm): if not f.hasGeometry(): continue - if not f.attributes()[field_index] in geometry_dict: - geometry_dict[f.attributes()[field_index]] = [f.geometry()] + if type == 0: + # bounding boxes - calculate on the fly for efficiency + if not f.attributes()[field_index] in bounds_dict: + bounds_dict[f.attributes()[field_index]] = f.geometry().boundingBox() + else: + bounds_dict[f.attributes()[field_index]].combineExtentWith(f.geometry().boundingBox()) else: - geometry_dict[f.attributes()[field_index]].append(f.geometry()) + if not f.attributes()[field_index] in geometry_dict: + geometry_dict[f.attributes()[field_index]] = [f.geometry()] + else: + geometry_dict[f.attributes()[field_index]].append(f.geometry()) feedback.setProgress(int(current * total)) - current = 0 - total = 50.0 / len(geometry_dict) if geometry_dict else 1 - for group, geometries in geometry_dict.items(): - if feedback.isCanceled(): - break + if type == 0: + # bounding boxes + current = 0 + total = 50.0 / len(bounds_dict) if bounds_dict else 1 + for group, rect in bounds_dict.items(): + if feedback.isCanceled(): + break - feature = self.createFeature(feedback, current, type, geometries, group) - sink.addFeature(feature, QgsFeatureSink.FastInsert) - geometry_dict[group] = None + # envelope + feature = QgsFeature() + feature.setGeometry(QgsGeometry.fromRect(rect)) + feature.setAttributes([current, group, rect.width(), rect.height(), rect.area(), rect.perimeter()]) + sink.addFeature(feature, QgsFeatureSink.FastInsert) + geometry_dict[group] = None - feedback.setProgress(50 + int(current * total)) - current += 1 + feedback.setProgress(50 + int(current * total)) + current += 1 + else: + current = 0 + total = 50.0 / len(geometry_dict) if geometry_dict else 1 + + for group, geometries in geometry_dict.items(): + if feedback.isCanceled(): + break + + feature = self.createFeature(feedback, current, type, geometries, group) + sink.addFeature(feature, QgsFeatureSink.FastInsert) + geometry_dict[group] = None + + feedback.setProgress(50 + int(current * total)) + current += 1 else: total = 80.0 / source.featureCount() if source.featureCount() else 1 features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])) geometry_queue = [] + bounds = QgsRectangle() for current, f in enumerate(features): if feedback.isCanceled(): break @@ -173,11 +205,20 @@ class MinimumBoundingGeometry(QgisAlgorithm): if not f.hasGeometry(): continue - geometry_queue.append(f.geometry()) + if type == 0: + # bounding boxes, calculate on the fly for efficiency + bounds.combineExtentWith(f.geometry().boundingBox()) + else: + geometry_queue.append(f.geometry()) feedback.setProgress(int(current * total)) if not feedback.isCanceled(): - feature = self.createFeature(feedback, 0, type, geometry_queue) + if type == 0: + feature = QgsFeature() + feature.setGeometry(QgsGeometry.fromRect(bounds)) + feature.setAttributes([0, bounds.width(), bounds.height(), bounds.area(), bounds.perimeter()]) + else: + feature = self.createFeature(feedback, 0, type, geometry_queue) sink.addFeature(feature, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id} From 85cd1c1673bfa150c8f9ef785dfeda2ad6d59c35 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 3 Sep 2017 19:26:04 +1000 Subject: [PATCH 278/364] [FEATURE] Split minimum enclosing geometry algs into separate feature based algorithms Instead of algorithms which handle both whole layers/groups of features/individual features, we leave the whole layer and group of features handling to the "Minimum bounding geometry" algorithm. The feature-by-feature algorithms are now native c++ algorithms. This affects: - bounding boxes - convex hulls - minimum enclosing circle - minimum oriented rectangles --- .../expected/convex_hull_by_feature.gfs | 42 +++++ .../expected/convex_hull_by_feature.gml | 71 +++++++ .../expected/enclosing_circles_each.gfs | 17 +- .../expected/enclosing_circles_each.gml | 46 ++--- .../testdata/expected/oriented_bounds.gfs | 30 +-- .../testdata/expected/oriented_bounds.gml | 60 +++--- .../tests/testdata/qgis_algorithm_tests.yaml | 28 ++- src/core/processing/qgsnativealgorithms.cpp | 175 ++++++++++++++++++ src/core/processing/qgsnativealgorithms.h | 101 ++++++++++ 9 files changed, 479 insertions(+), 91 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gml diff --git a/python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gfs b/python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gfs new file mode 100644 index 00000000000..4168642000e --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gfs @@ -0,0 +1,42 @@ + + + convex_hull_by_feature + convex_hull_by_feature + + 3 + EPSG:4326 + + 6 + -1.00000 + 10.00000 + -3.00000 + 6.00000 + + + intval + intval + Integer + + + floatval + floatval + Real + + + area + area + Real + + + perimeter + perimeter + Real + + + name + name + String + 5 + + + diff --git a/python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gml b/python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gml new file mode 100644 index 00000000000..3f176f9dcb5 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/convex_hull_by_feature.gml @@ -0,0 +1,71 @@ + + + + + -1-3 + 106 + + + + + + 6,-3 2.4,-1.0 3.8,2.2 6,1 6,-3 + 120 + -100291.43213 + 11.560000 + 14.117095 + + + + + 5.2,3.8 4,4 5.4,5.0 6,4 5.2,3.8 + -33 + 0 + Aaaaa + 1.200000 + 4.927829 + + + + + -1,-1 -1,3 3,3 3,2 2,-1 -1,-1 + 33 + 44.12346 + aaaaa + 14.500000 + 15.162278 + + + + + 6.4,-3.0 6.8,1.8 10,1 9.6,-2.2 6.4,-3.0 + 0 + ASDF + 12.800000 + 14.638510 + + + + + 1.6,4.8 2,6 3,6 1.6,4.8 + 0.123 + bbaaa + 0.600000 + 4.108820 + + + + + 6,-3 2.4,-1.0 3.8,2.2 6,1 6,-3 + 2 + 3.33 + elim + 11.560000 + 14.117095 + + + diff --git a/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs index 9d8dc0c421c..8559400e5d0 100644 --- a/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs +++ b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs @@ -7,7 +7,7 @@ EPSG:4326 6 - -1.81766 + -1.82843 10.59982 -3.43247 6.32190 @@ -22,21 +22,16 @@ floatval Real - - center_x - center_x - Real - - - center_y - center_y - Real - radius radius Real + + area + area + Real + name name diff --git a/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml index 0a23fc8539b..2df57ab1c09 100644 --- a/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml +++ b/python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml @@ -6,72 +6,66 @@ xmlns:gml="http://www.opengis.net/gml"> - -1.817664105611035-3.43247280352443 + -1.82842712474619-3.43247280352443 10.599819742299946.321903675995209 - + - 3.8,2.2 4.26819673362059,2.35151315326536 4.75559048978224,2.4194229717016 5.24737205583712,2.40166604983954 5.72859889775413,2.29878192276454 6.18464918145415,2.11389667261588 6.60166604983954,1.85262794416288 6.96697865638513,1.52291425551124 7.26948716239812,1.13477379024745 7.5,0.7 7.65151315326536,0.23180326637941 7.7194229717016,-0.255590489782239 7.70166604983954,-0.747372055837116 7.59878192276454,-1.22859889775413 7.41389667261588,-1.68464918145415 7.15262794416288,-2.10166604983954 6.82291425551124,-2.46697865638513 6.43477379024745,-2.76948716239812 6.0,-3.0 5.53180326637941,-3.15151315326536 5.04440951021776,-3.2194229717016 4.55262794416288,-3.20166604983954 4.07140110224587,-3.09878192276454 3.61535081854585,-2.91389667261588 3.19833395016046,-2.65262794416288 2.83302134361487,-2.32291425551124 2.53051283760188,-1.93477379024745 2.3,-1.5 2.14848684673464,-1.03180326637941 2.0805770282984,-0.544409510217762 2.09833395016046,-0.052627944162882 2.20121807723546,0.428598897754127 2.38610332738412,0.884649181454149 2.64737205583712,1.30166604983954 2.97708574448876,1.66697865638513 3.36522620975255,1.96948716239812 3.8,2.2 + 3.8,2.2 4.03079076324299,2.28597753206096 4.26819673362059,2.35151315326536 4.51041110834858,2.39610809796435 4.75559048978224,2.4194229717016 5.00186891478551,2.42128033421006 5.24737205583712,2.40166604983954 5.49023148579483,2.36072939513753 5.72859889775413,2.29878192276454 5.96066017177982,2.21629509039023 6.18464918145415,2.11389667261588 6.39886123516523,1.99236598323061 6.60166604983954,1.85262794416288 6.79152015838052,1.69574604626613 6.96697865638513,1.52291425551124 7.12670619873881,1.33544792618453 7.26948716239812,1.13477379024745 7.39423489801611,0.922419099044831 7.5,0.7 7.58597753206096,0.469209236757009 7.65151315326536,0.23180326637941 7.69610809796435,-0.010411108348578 7.7194229717016,-0.255590489782239 7.72128033421006,-0.501868914785503 7.70166604983954,-0.747372055837116 7.66072939513753,-0.990231485794827 7.59878192276454,-1.22859889775413 7.51629509039023,-1.46066017177982 7.41389667261588,-1.68464918145415 7.29236598323061,-1.89886123516523 7.15262794416288,-2.10166604983954 6.99574604626613,-2.29152015838052 6.82291425551124,-2.46697865638513 6.63544792618453,-2.6267061987388 6.43477379024745,-2.76948716239812 6.22241909904483,-2.89423489801611 6.0,-3.0 5.76920923675701,-3.08597753206096 5.53180326637941,-3.15151315326536 5.28958889165142,-3.19610809796435 5.04440951021776,-3.2194229717016 4.7981310852145,-3.22128033421006 4.55262794416288,-3.20166604983954 4.30976851420517,-3.16072939513753 4.07140110224587,-3.09878192276454 3.83933982822018,-3.01629509039023 3.61535081854585,-2.91389667261588 3.40113876483477,-2.79236598323061 3.19833395016046,-2.65262794416288 3.00847984161948,-2.49574604626613 2.83302134361487,-2.32291425551124 2.67329380126119,-2.13544792618453 2.53051283760188,-1.93477379024745 2.40576510198389,-1.72241909904483 2.3,-1.5 2.21402246793904,-1.26920923675701 2.14848684673464,-1.03180326637941 2.10389190203565,-0.789588891651422 2.0805770282984,-0.544409510217762 2.07871966578994,-0.298131085214498 2.09833395016046,-0.052627944162882 2.13927060486247,0.190231485794829 2.20121807723546,0.428598897754127 2.28370490960977,0.660660171779822 2.38610332738412,0.884649181454149 2.50763401676939,1.09886123516523 2.64737205583712,1.30166604983954 2.80425395373387,1.49152015838052 2.97708574448876,1.66697865638513 3.16455207381547,1.82670619873881 3.36522620975255,1.96948716239812 3.57758090095517,2.09423489801611 3.8,2.2 120 -100291.43213 - 4.9 - -0.4 - 2.82311884269862 + 2.823119 + 25.038493 - 5.0,5.08319489631876 5.17420296559052,5.06795411167699 5.34311286222252,5.02269484128082 5.50159744815938,4.94879226515894 5.64484124945447,4.8484918756903 5.7684918756903,4.72484124945447 5.86879226515894,4.58159744815938 5.94269484128082,4.42311286222252 5.98795411167699,4.25420296559052 6.00319489631876,4.08 5.98795411167699,3.90579703440948 5.94269484128082,3.73688713777748 5.86879226515894,3.57840255184062 5.7684918756903,3.43515875054553 5.64484124945447,3.3115081243097 5.50159744815938,3.21120773484106 5.34311286222252,3.13730515871918 5.17420296559052,3.09204588832301 5.0,3.07680510368124 4.82579703440948,3.09204588832301 4.65688713777748,3.13730515871918 4.49840255184062,3.21120773484106 4.35515875054553,3.3115081243097 4.2315081243097,3.43515875054553 4.13120773484106,3.57840255184062 4.05730515871918,3.73688713777748 4.01204588832301,3.90579703440948 3.99680510368124,4.08 4.01204588832301,4.25420296559052 4.05730515871918,4.42311286222252 4.13120773484106,4.58159744815938 4.2315081243097,4.72484124945447 4.35515875054553,4.8484918756903 4.49840255184062,4.94879226515894 4.65688713777748,5.02269484128082 4.82579703440948,5.06795411167699 5.0,5.08319489631876 + 5.0,5.08319489631876 5.08743419630932,5.07937743686544 5.17420296559052,5.06795411167699 5.25964594511694,5.04901185915567 5.34311286222252,5.02269484128082 5.42396848326937,4.98920334644911 5.50159744815938,4.94879226515894 5.57540895359607,4.90176915013979 5.64484124945447,4.8484918756903 5.70936591403873,4.78936591403873 5.7684918756903,4.72484124945447 5.82176915013979,4.65540895359607 5.86879226515894,4.58159744815938 5.90920334644911,4.50396848326937 5.94269484128082,4.42311286222252 5.96901185915567,4.33964594511694 5.98795411167699,4.25420296559052 5.99937743686544,4.16743419630932 6.00319489631876,4.08 5.99937743686544,3.99256580369068 5.98795411167699,3.90579703440948 5.96901185915567,3.82035405488306 5.94269484128082,3.73688713777748 5.90920334644911,3.65603151673063 5.86879226515894,3.57840255184062 5.82176915013979,3.50459104640393 5.7684918756903,3.43515875054553 5.70936591403873,3.37063408596127 5.64484124945447,3.3115081243097 5.57540895359607,3.25823084986021 5.50159744815938,3.21120773484106 5.42396848326937,3.17079665355089 5.34311286222252,3.13730515871918 5.25964594511694,3.11098814084433 5.17420296559052,3.09204588832301 5.08743419630932,3.08062256313456 5.0,3.07680510368124 4.91256580369068,3.08062256313456 4.82579703440948,3.09204588832301 4.74035405488306,3.11098814084433 4.65688713777748,3.13730515871918 4.57603151673063,3.17079665355089 4.49840255184062,3.21120773484106 4.42459104640393,3.25823084986021 4.35515875054553,3.3115081243097 4.29063408596127,3.37063408596127 4.2315081243097,3.43515875054553 4.17823084986021,3.50459104640393 4.13120773484106,3.57840255184062 4.09079665355089,3.65603151673063 4.05730515871918,3.73688713777748 4.03098814084433,3.82035405488306 4.01204588832301,3.90579703440948 4.00062256313456,3.99256580369068 3.99680510368124,4.08 4.00062256313456,4.16743419630932 4.01204588832301,4.25420296559052 4.03098814084433,4.33964594511694 4.05730515871918,4.42311286222252 4.09079665355089,4.50396848326937 4.13120773484106,4.58159744815938 4.17823084986021,4.65540895359607 4.2315081243097,4.72484124945447 4.29063408596127,4.78936591403873 4.35515875054553,4.8484918756903 4.42459104640393,4.90176915013979 4.49840255184062,4.94879226515894 4.57603151673063,4.98920334644911 4.65688713777748,5.02269484128082 4.74035405488306,5.04901185915567 4.82579703440948,5.06795411167699 4.91256580369068,5.07937743686544 5.0,5.08319489631876 -33 0 Aaaaa - 5 - 4.08 - 1.00319489631876 + 1.003195 + 3.161699 - 3.0,3.0 3.31691186135828,2.62231915069056 3.56342552822315,2.19534495492048 3.73205080756888,1.73205080756888 3.81766410561104,1.24651366686488 3.81766410561104,0.753486333135122 3.73205080756888,0.267949192431123 3.56342552822315,-0.195344954920481 3.31691186135828,-0.622319150690556 3.0,-1.0 2.62231915069056,-1.31691186135828 2.19534495492048,-1.56342552822315 1.73205080756888,-1.73205080756888 1.24651366686488,-1.81766410561103 0.753486333135123,-1.81766410561103 0.267949192431122,-1.73205080756888 -0.195344954920479,-1.56342552822315 -0.622319150690555,-1.31691186135828 -1.0,-1.0 -1.31691186135828,-0.622319150690556 -1.56342552822315,-0.195344954920479 -1.73205080756888,0.267949192431122 -1.81766410561103,0.753486333135123 -1.81766410561103,1.24651366686488 -1.73205080756888,1.73205080756888 -1.56342552822316,2.19534495492048 -1.31691186135828,2.62231915069056 -1.0,3.0 -0.622319150690556,3.31691186135828 -0.195344954920481,3.56342552822315 0.267949192431124,3.73205080756888 0.753486333135123,3.81766410561104 1.24651366686488,3.81766410561104 1.73205080756888,3.73205080756888 2.19534495492048,3.56342552822316 2.62231915069056,3.31691186135828 3.0,3.0 + 3.0,3.0 3.16670088167881,2.81807791068817 3.31691186135828,2.62231915069056 3.44948974278318,2.4142135623731 3.56342552822315,2.19534495492048 3.6578520975547,1.9673790505919 3.73205080756888,1.73205080756888 3.78545696128008,1.49115121587589 3.81766410561104,1.24651366686488 3.82842712474619,1.0 3.81766410561104,0.753486333135122 3.78545696128008,0.508848784124109 3.73205080756888,0.267949192431123 3.6578520975547,0.032620949408098 3.56342552822315,-0.195344954920481 3.44948974278318,-0.414213562373096 3.31691186135828,-0.622319150690556 3.16670088167881,-0.818077910688175 3.0,-1.0 2.81807791068817,-1.16670088167881 2.62231915069056,-1.31691186135828 2.4142135623731,-1.44948974278318 2.19534495492048,-1.56342552822315 1.9673790505919,-1.6578520975547 1.73205080756888,-1.73205080756888 1.49115121587589,-1.78545696128008 1.24651366686488,-1.81766410561103 1.0,-1.82842712474619 0.753486333135123,-1.81766410561103 0.508848784124109,-1.78545696128008 0.267949192431122,-1.73205080756888 0.032620949408099,-1.6578520975547 -0.195344954920479,-1.56342552822315 -0.414213562373095,-1.44948974278318 -0.622319150690555,-1.31691186135828 -0.818077910688175,-1.16670088167881 -1.0,-1.0 -1.16670088167881,-0.818077910688175 -1.31691186135828,-0.622319150690556 -1.44948974278318,-0.414213562373095 -1.56342552822315,-0.195344954920479 -1.6578520975547,0.032620949408099 -1.73205080756888,0.267949192431122 -1.78545696128008,0.508848784124108 -1.81766410561103,0.753486333135123 -1.82842712474619,1.0 -1.81766410561103,1.24651366686488 -1.78545696128008,1.49115121587589 -1.73205080756888,1.73205080756888 -1.6578520975547,1.9673790505919 -1.56342552822316,2.19534495492048 -1.44948974278318,2.4142135623731 -1.31691186135828,2.62231915069056 -1.16670088167881,2.81807791068817 -1.0,3.0 -0.818077910688175,3.16670088167881 -0.622319150690556,3.31691186135828 -0.414213562373096,3.44948974278318 -0.195344954920481,3.56342552822315 0.032620949408098,3.6578520975547 0.267949192431124,3.73205080756888 0.508848784124109,3.78545696128008 0.753486333135123,3.81766410561104 1.0,3.82842712474619 1.24651366686488,3.81766410561104 1.49115121587589,3.78545696128008 1.73205080756888,3.73205080756888 1.9673790505919,3.6578520975547 2.19534495492048,3.56342552822316 2.4142135623731,3.44948974278318 2.62231915069056,3.31691186135828 2.81807791068817,3.16670088167881 3.0,3.0 33 44.12346 aaaaa - 1 - 1 - 2.82842712474619 + 2.828427 + 25.132741 - 7.8734693877551,2.02022790556525 8.3468951585034,1.97880851760375 8.80593612677252,1.85580886086324 9.23664456502752,1.65496621767295 9.62593361532103,1.38238309011494 9.96197492684963,1.04634177858634 10.2345580544076,0.657052728292829 10.4354006975979,0.226344290037822 10.5584003543384,-0.232696678231291 10.5998197422999,-0.706122448979591 10.5584003543384,-1.17954821972789 10.4354006975979,-1.638589187997 10.2345580544076,-2.06929762625201 9.96197492684963,-2.45858667654552 9.62593361532103,-2.79462798807412 9.23664456502752,-3.06721111563213 8.80593612677252,-3.26805375882242 8.3468951585034,-3.39105341556293 7.8734693877551,-3.43247280352443 7.4000436170068,-3.39105341556293 6.94100264873769,-3.26805375882242 6.51029421048268,-3.06721111563213 6.12100516018918,-2.79462798807412 5.78496384866057,-2.45858667654552 5.51238072110256,-2.06929762625201 5.31153807791227,-1.63858918799701 5.18853842117176,-1.17954821972789 5.14711903321026,-0.70612244897959 5.18853842117176,-0.23269667823129 5.31153807791227,0.226344290037823 5.51238072110256,0.65705272829283 5.78496384866057,1.04634177858634 6.12100516018918,1.38238309011494 6.51029421048268,1.65496621767295 6.94100264873769,1.85580886086324 7.4000436170068,1.97880851760375 7.8734693877551,2.02022790556525 + 7.8734693877551,2.02022790556525 8.1110864778958,2.00985331935853 8.3468951585034,1.97880851760375 8.57910078313332,1.92732976998763 8.80593612677252,1.85580886086324 9.02567483548898,1.76479010753453 9.23664456502752,1.65496621767295 9.43723970835934,1.52717301739383 9.62593361532103,1.38238309011494 9.80129021134411,1.22169837460941 9.96197492684963,1.04634177858634 10.1067648541285,0.85764787162465 10.2345580544076,0.657052728292829 10.3443819442692,0.446082998754287 10.4354006975979,0.226344290037822 10.5069216067223,-0.000491053601378 10.5584003543384,-0.232696678231291 10.5894451560932,-0.468505358838895 10.5998197422999,-0.706122448979591 10.5894451560932,-0.943739539120288 10.5584003543384,-1.17954821972789 10.5069216067223,-1.4117538443578 10.4354006975979,-1.638589187997 10.3443819442692,-1.85832789671347 10.2345580544076,-2.06929762625201 10.1067648541285,-2.26989276958383 9.96197492684963,-2.45858667654552 9.80129021134411,-2.6339432725686 9.62593361532103,-2.79462798807412 9.43723970835934,-2.93941791535301 9.23664456502752,-3.06721111563213 9.02567483548898,-3.17703500549371 8.80593612677252,-3.26805375882242 8.57910078313332,-3.33957466794681 8.3468951585034,-3.39105341556293 8.1110864778958,-3.42209821731771 7.8734693877551,-3.43247280352443 7.63585229761441,-3.42209821731771 7.4000436170068,-3.39105341556293 7.16783799237689,-3.33957466794681 6.94100264873769,-3.26805375882242 6.72126394002122,-3.17703500549371 6.51029421048268,-3.06721111563213 6.30969906715086,-2.93941791535301 6.12100516018918,-2.79462798807412 5.9456485641661,-2.63394327256859 5.78496384866057,-2.45858667654552 5.64017392138168,-2.26989276958383 5.51238072110256,-2.06929762625201 5.40255683124098,-1.85832789671347 5.31153807791227,-1.63858918799701 5.24001716878788,-1.4117538443578 5.18853842117176,-1.17954821972789 5.15749361941698,-0.943739539120287 5.14711903321026,-0.70612244897959 5.15749361941698,-0.468505358838894 5.18853842117176,-0.23269667823129 5.24001716878788,-0.000491053601377 5.31153807791227,0.226344290037823 5.40255683124098,0.446082998754288 5.51238072110256,0.65705272829283 5.64017392138168,0.85764787162465 5.78496384866057,1.04634177858634 5.9456485641661,1.22169837460941 6.12100516018918,1.38238309011494 6.30969906715086,1.52717301739383 6.51029421048268,1.65496621767295 6.72126394002122,1.76479010753453 6.94100264873769,1.85580886086324 7.16783799237689,1.92732976998763 7.4000436170068,1.97880851760375 7.63585229761441,2.00985331935853 7.8734693877551,2.02022790556525 0 ASDF - 7.8734693877551 - -0.706122448979591 - 2.72635035454484 + 2.726350 + 23.351415 - 3,6 3.0935543337087,5.86933092744047 3.16299692054554,5.72440147214358 3.20621778264911,5.56961524227066 3.22190367599521,5.40967533909081 3.20957799265196,5.24944145562864 3.16961524227066,5.09378221735089 3.10322967279951,4.94742725144526 3.01243837617418,4.81482347949161 2.9,4.7 2.76933092744047,4.6064456662913 2.62440147214358,4.53700307945446 2.46961524227066,4.49378221735089 2.30967533909081,4.47809632400479 2.14944145562864,4.49042200734804 1.99378221735089,4.53038475772934 1.84742725144526,4.59677032720049 1.71482347949161,4.68756162382582 1.6,4.8 1.5064456662913,4.93066907255953 1.43700307945446,5.07559852785642 1.39378221735089,5.23038475772934 1.37809632400479,5.39032466090919 1.39042200734804,5.55055854437136 1.43038475772934,5.70621778264911 1.49677032720049,5.85257274855473 1.58756162382582,5.98517652050839 1.7,6.1 1.83066907255953,6.1935543337087 1.97559852785642,6.26299692054554 2.13038475772934,6.30621778264911 2.29032466090919,6.32190367599521 2.45055854437136,6.30957799265196 2.60621778264911,6.26961524227066 2.75257274855473,6.20322967279951 2.88517652050839,6.11243837617418 3,6 + 3,6 3.04962973431282,5.93670779893169 3.0935543337087,5.86933092744047 3.13143950546386,5.79838216420168 3.16299692054554,5.72440147214358 3.18798640797007,5.6479518890035 3.20621778264911,5.56961524227066 3.21755229281292,5.48998772112766 3.22190367599521,5.40967533909081 3.21923881554251,5.32928932188135 3.20957799265196,5.24944145562864 3.19299473201913,5.17073943080833 3.16961524227066,5.09378221735089 3.13961745544048,5.01915550611876 3.10322967279951,4.94742725144526 3.0607288273452,4.87914334865916 3.01243837617418,4.81482347949161 2.95872583877841,4.75495715698437 2.9,4.7 2.83670779893169,4.65037026568718 2.76933092744047,4.6064456662913 2.69838216420168,4.56856049453614 2.62440147214358,4.53700307945446 2.5479518890035,4.51201359202993 2.46961524227066,4.49378221735089 2.38998772112766,4.48244770718708 2.30967533909081,4.47809632400479 2.22928932188134,4.48076118445749 2.14944145562864,4.49042200734804 2.07073943080833,4.50700526798087 1.99378221735089,4.53038475772934 1.91915550611876,4.56038254455952 1.84742725144526,4.59677032720049 1.77914334865916,4.63927117265479 1.71482347949161,4.68756162382582 1.65495715698437,4.74127416122159 1.6,4.8 1.55037026568718,4.86329220106831 1.5064456662913,4.93066907255953 1.46856049453614,5.00161783579832 1.43700307945446,5.07559852785642 1.41201359202993,5.1520481109965 1.39378221735089,5.23038475772934 1.38244770718708,5.31001227887234 1.37809632400479,5.39032466090919 1.38076118445749,5.47071067811865 1.39042200734804,5.55055854437136 1.40700526798087,5.62926056919167 1.43038475772934,5.70621778264911 1.46038254455952,5.78084449388124 1.49677032720049,5.85257274855473 1.53927117265479,5.92085665134084 1.58756162382582,5.98517652050839 1.64127416122159,6.04504284301563 1.7,6.1 1.76329220106831,6.14962973431282 1.83066907255953,6.1935543337087 1.90161783579832,6.23143950546386 1.97559852785642,6.26299692054554 2.0520481109965,6.28798640797008 2.13038475772934,6.30621778264911 2.21001227887234,6.31755229281292 2.29032466090919,6.32190367599521 2.37071067811865,6.31923881554251 2.45055854437136,6.30957799265196 2.52926056919167,6.29299473201913 2.60621778264911,6.26961524227066 2.68084449388124,6.23961745544048 2.75257274855473,6.20322967279951 2.82085665134084,6.16072882734521 2.88517652050839,6.11243837617418 2.94504284301563,6.05872583877841 3,6 0.123 bbaaa - 2.3 - 5.4 - 0.921954445729289 + 0.921954 + 2.670354 - 3.8,2.2 4.26819673362059,2.35151315326536 4.75559048978224,2.4194229717016 5.24737205583712,2.40166604983954 5.72859889775413,2.29878192276454 6.18464918145415,2.11389667261588 6.60166604983954,1.85262794416288 6.96697865638513,1.52291425551124 7.26948716239812,1.13477379024745 7.5,0.7 7.65151315326536,0.23180326637941 7.7194229717016,-0.255590489782239 7.70166604983954,-0.747372055837116 7.59878192276454,-1.22859889775413 7.41389667261588,-1.68464918145415 7.15262794416288,-2.10166604983954 6.82291425551124,-2.46697865638513 6.43477379024745,-2.76948716239812 6.0,-3.0 5.53180326637941,-3.15151315326536 5.04440951021776,-3.2194229717016 4.55262794416288,-3.20166604983954 4.07140110224587,-3.09878192276454 3.61535081854585,-2.91389667261588 3.19833395016046,-2.65262794416288 2.83302134361487,-2.32291425551124 2.53051283760188,-1.93477379024745 2.3,-1.5 2.14848684673464,-1.03180326637941 2.0805770282984,-0.544409510217762 2.09833395016046,-0.052627944162882 2.20121807723546,0.428598897754127 2.38610332738412,0.884649181454149 2.64737205583712,1.30166604983954 2.97708574448876,1.66697865638513 3.36522620975255,1.96948716239812 3.8,2.2 + 3.8,2.2 4.03079076324299,2.28597753206096 4.26819673362059,2.35151315326536 4.51041110834858,2.39610809796435 4.75559048978224,2.4194229717016 5.00186891478551,2.42128033421006 5.24737205583712,2.40166604983954 5.49023148579483,2.36072939513753 5.72859889775413,2.29878192276454 5.96066017177982,2.21629509039023 6.18464918145415,2.11389667261588 6.39886123516523,1.99236598323061 6.60166604983954,1.85262794416288 6.79152015838052,1.69574604626613 6.96697865638513,1.52291425551124 7.12670619873881,1.33544792618453 7.26948716239812,1.13477379024745 7.39423489801611,0.922419099044831 7.5,0.7 7.58597753206096,0.469209236757009 7.65151315326536,0.23180326637941 7.69610809796435,-0.010411108348578 7.7194229717016,-0.255590489782239 7.72128033421006,-0.501868914785503 7.70166604983954,-0.747372055837116 7.66072939513753,-0.990231485794827 7.59878192276454,-1.22859889775413 7.51629509039023,-1.46066017177982 7.41389667261588,-1.68464918145415 7.29236598323061,-1.89886123516523 7.15262794416288,-2.10166604983954 6.99574604626613,-2.29152015838052 6.82291425551124,-2.46697865638513 6.63544792618453,-2.6267061987388 6.43477379024745,-2.76948716239812 6.22241909904483,-2.89423489801611 6.0,-3.0 5.76920923675701,-3.08597753206096 5.53180326637941,-3.15151315326536 5.28958889165142,-3.19610809796435 5.04440951021776,-3.2194229717016 4.7981310852145,-3.22128033421006 4.55262794416288,-3.20166604983954 4.30976851420517,-3.16072939513753 4.07140110224587,-3.09878192276454 3.83933982822018,-3.01629509039023 3.61535081854585,-2.91389667261588 3.40113876483477,-2.79236598323061 3.19833395016046,-2.65262794416288 3.00847984161948,-2.49574604626613 2.83302134361487,-2.32291425551124 2.67329380126119,-2.13544792618453 2.53051283760188,-1.93477379024745 2.40576510198389,-1.72241909904483 2.3,-1.5 2.21402246793904,-1.26920923675701 2.14848684673464,-1.03180326637941 2.10389190203565,-0.789588891651422 2.0805770282984,-0.544409510217762 2.07871966578994,-0.298131085214498 2.09833395016046,-0.052627944162882 2.13927060486247,0.190231485794829 2.20121807723546,0.428598897754127 2.28370490960977,0.660660171779822 2.38610332738412,0.884649181454149 2.50763401676939,1.09886123516523 2.64737205583712,1.30166604983954 2.80425395373387,1.49152015838052 2.97708574448876,1.66697865638513 3.16455207381547,1.82670619873881 3.36522620975255,1.96948716239812 3.57758090095517,2.09423489801611 3.8,2.2 2 3.33 elim - 4.9 - -0.4 - 2.82311884269862 + 2.823119 + 25.038493 diff --git a/python/plugins/processing/tests/testdata/expected/oriented_bounds.gfs b/python/plugins/processing/tests/testdata/expected/oriented_bounds.gfs index 2867f28fd42..4f4d91bbfc2 100644 --- a/python/plugins/processing/tests/testdata/expected/oriented_bounds.gfs +++ b/python/plugins/processing/tests/testdata/expected/oriented_bounds.gfs @@ -22,21 +22,6 @@ floatval Real - - area - area - Real - - - perimeter - perimeter - Real - - - angle - angle - Real - width width @@ -47,6 +32,21 @@ height Real + + angle + angle + Real + + + area + area + Real + + + perimeter + perimeter + Real + name name diff --git a/python/plugins/processing/tests/testdata/expected/oriented_bounds.gml b/python/plugins/processing/tests/testdata/expected/oriented_bounds.gml index 8f543a5b0ff..f9a6aea471d 100644 --- a/python/plugins/processing/tests/testdata/expected/oriented_bounds.gml +++ b/python/plugins/processing/tests/testdata/expected/oriented_bounds.gml @@ -16,11 +16,11 @@ 6,-3 7.69811320754717,0.056603773584906 3.80943396226415,2.21698113207547 2.11132075471698,-0.839622641509436 6,-3 120 -100291.43213 - 15.5547169811321 - 15.8902367081648 - 119.054604099077 - 3.49662910448615 - 4.44848924959627 + 3.496629 + 4.448489 + 119.054604 + 15.554717 + 15.890237 @@ -29,11 +29,11 @@ -33 0 Aaaaa - 2.16470588235294 - 6.11189775091559 - 165.963756532073 - 1.94028500029066 - 1.11566387516713 + 1.940285 + 1.115664 + 165.963757 + 2.164706 + 6.111898 @@ -42,11 +42,11 @@ 33 44.12346 aaaaa - 16 - 16 - 90 - 4 - 4 + 4.000000 + 4.000000 + 90.000000 + 16.000000 + 16.000000 @@ -54,11 +54,11 @@ 6.4,-3.0 9.64413793103449,-3.27034482758621 10.0441379310345,1.52965517241379 6.8,1.8 6.4,-3.0 0 ASDF - 15.68 - 16.1440412835671 - 4.76364169072617 - 3.25538281026661 - 4.81663783151692 + 3.255383 + 4.816638 + 4.763642 + 15.680000 + 16.144041 @@ -66,11 +66,11 @@ 1.36470588235294,4.94117647058824 2.1,4.5 3.0,6.0 2.26470588235294,6.44117647058824 1.36470588235294,4.94117647058824 0.123 bbaaa - 1.5 - 5.21355698833227 - 30.9637565320735 - 0.857492925712544 - 1.74928556845359 + 0.857493 + 1.749286 + 30.963757 + 1.500000 + 5.213557 @@ -79,11 +79,11 @@ 2 3.33 elim - 15.5547169811321 - 15.8902367081648 - 119.054604099077 - 3.49662910448615 - 4.44848924959627 + 3.496629 + 4.448489 + 119.054604 + 15.554717 + 15.890237 diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 4ae0e6f4dbb..db4f901cb4e 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -610,7 +610,7 @@ tests: name: expected/multi_to_single.gml type: vector - - algorithm: qgis:boundingboxes + - algorithm: native:boundingboxes name: Bounding boxes for lines params: INPUT: @@ -621,7 +621,7 @@ tests: name: expected/lines_bounds.gml type: vector - - algorithm: qgis:boundingboxes + - algorithm: native:boundingboxes name: Bounding boxes for multilines params: INPUT: @@ -632,7 +632,7 @@ tests: name: expected/multiline_bounds.gml type: vector - - algorithm: qgis:boundingboxes + - algorithm: native:boundingboxes name: Bounding boxes for multipolygons params: INPUT: @@ -643,7 +643,7 @@ tests: name: expected/multipoly_bounds.gml type: vector - - algorithm: qgis:boundingboxes + - algorithm: native:boundingboxes name: Bounding boxes for points params: INPUT: @@ -654,7 +654,7 @@ tests: name: expected/point_bounds.gml type: vector - - algorithm: qgis:boundingboxes + - algorithm: native:boundingboxes name: Bounding boxes for polygons params: INPUT: @@ -665,7 +665,7 @@ tests: name: expected/poly_bounds.gml type: vector - - algorithm: qgis:boundingboxes + - algorithm: native:boundingboxes name: Bounding boxes for multipoints params: INPUT: @@ -2241,10 +2241,9 @@ tests: hash: fe6e018be13c5a3c17f3f4d0f0dc7686c628cb440b74c4642aa0c939 type: rasterhash - - algorithm: qgis:orientedminimumboundingbox + - algorithm: native:orientedminimumboundingbox name: Oriented minimum bounding box polys params: - BY_FEATURE: true INPUT: name: custom/oriented_bbox.gml type: vector @@ -3240,7 +3239,7 @@ tests: name: expected/raster_extent.gml type: vector - - algorithm: qgis:minimalenclosingcircle + - algorithm: native:minimumenclosingcircle name: Minimal enclosing circle each features params: BY_FEATURE: true @@ -3421,3 +3420,14 @@ tests: fields: fid: skip id: skip + + - algorithm: native:convexhull + name: Convex hull by feature + params: + INPUT: + name: custom/oriented_bbox.gml + type: vector + results: + OUTPUT: + name: expected/convex_hull_by_feature.gml + type: vector diff --git a/src/core/processing/qgsnativealgorithms.cpp b/src/core/processing/qgsnativealgorithms.cpp index 1854fb6214a..8a2f17df3ae 100644 --- a/src/core/processing/qgsnativealgorithms.cpp +++ b/src/core/processing/qgsnativealgorithms.cpp @@ -68,6 +68,10 @@ void QgsNativeAlgorithms::loadAlgorithms() addAlgorithm( new QgsSubdivideAlgorithm() ); addAlgorithm( new QgsTransformAlgorithm() ); addAlgorithm( new QgsRemoveNullGeometryAlgorithm() ); + addAlgorithm( new QgsBoundingBoxAlgorithm() ); + addAlgorithm( new QgsOrientedMinimumBoundingBoxAlgorithm() ); + addAlgorithm( new QgsMinimumEnclosingCircleAlgorithm() ); + addAlgorithm( new QgsConvexHullAlgorithm() ); } void QgsCentroidAlgorithm::initAlgorithm( const QVariantMap & ) @@ -1087,4 +1091,175 @@ QVariantMap QgsRemoveNullGeometryAlgorithm::processAlgorithm( const QVariantMap } +QString QgsBoundingBoxAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm calculates the bounding box (envelope) of each feature in an input layer." ); +} + +QgsBoundingBoxAlgorithm *QgsBoundingBoxAlgorithm::createInstance() const +{ + return new QgsBoundingBoxAlgorithm(); +} + +QgsFields QgsBoundingBoxAlgorithm::outputFields( const QgsFields &inputFields ) const +{ + QgsFields fields = inputFields; + fields.append( QgsField( QStringLiteral( "width" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "height" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "area" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "perimeter" ), QVariant::Double, QString(), 20, 6 ) ); + return fields; +} + +QgsFeature QgsBoundingBoxAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingFeedback * ) +{ + QgsFeature f = feature; + if ( f.hasGeometry() ) + { + QgsRectangle bounds = f.geometry().boundingBox(); + QgsGeometry outputGeometry = QgsGeometry::fromRect( bounds ); + f.setGeometry( outputGeometry ); + QgsAttributes attrs = f.attributes(); + attrs << bounds.width() + << bounds.height() + << bounds.area() + << bounds.perimeter(); + f.setAttributes( attrs ); + } + return f; +} + +QString QgsOrientedMinimumBoundingBoxAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm calculates the minimum area rotated rectangle which covers each feature in an input layer." ); +} + +QgsOrientedMinimumBoundingBoxAlgorithm *QgsOrientedMinimumBoundingBoxAlgorithm::createInstance() const +{ + return new QgsOrientedMinimumBoundingBoxAlgorithm(); +} + +QgsFields QgsOrientedMinimumBoundingBoxAlgorithm::outputFields( const QgsFields &inputFields ) const +{ + QgsFields fields = inputFields; + fields.append( QgsField( QStringLiteral( "width" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "height" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "angle" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "area" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "perimeter" ), QVariant::Double, QString(), 20, 6 ) ); + return fields; +} + +QgsFeature QgsOrientedMinimumBoundingBoxAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingFeedback * ) +{ + QgsFeature f = feature; + if ( f.hasGeometry() ) + { + double area = 0; + double angle = 0; + double width = 0; + double height = 0; + QgsGeometry outputGeometry = f.geometry().orientedMinimumBoundingBox( area, angle, width, height ); + f.setGeometry( outputGeometry ); + QgsAttributes attrs = f.attributes(); + attrs << width + << height + << angle + << area + << 2 * width + 2 * height; + f.setAttributes( attrs ); + } + return f; +} + + +void QgsMinimumEnclosingCircleAlgorithm::initParameters( const QVariantMap & ) +{ + addParameter( new QgsProcessingParameterNumber( QStringLiteral( "SEGMENTS" ), QObject::tr( "Number of segments in circles" ), QgsProcessingParameterNumber::Integer, + 72, false, 8, 100000 ) ); +} + +QString QgsMinimumEnclosingCircleAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm calculates the minimum enclosing circle which covers each feature in an input layer." ); +} + +QgsMinimumEnclosingCircleAlgorithm *QgsMinimumEnclosingCircleAlgorithm::createInstance() const +{ + return new QgsMinimumEnclosingCircleAlgorithm(); +} + +QgsFields QgsMinimumEnclosingCircleAlgorithm::outputFields( const QgsFields &inputFields ) const +{ + QgsFields fields = inputFields; + fields.append( QgsField( QStringLiteral( "radius" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "area" ), QVariant::Double, QString(), 20, 6 ) ); + return fields; +} + +bool QgsMinimumEnclosingCircleAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * ) +{ + mSegments = parameterAsInt( parameters, QStringLiteral( "SEGMENTS" ), context ); + return true; +} + +QgsFeature QgsMinimumEnclosingCircleAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingFeedback * ) +{ + QgsFeature f = feature; + if ( f.hasGeometry() ) + { + double radius = 0; + QgsPointXY center; + QgsGeometry outputGeometry = f.geometry().minimalEnclosingCircle( center, radius, mSegments ); + f.setGeometry( outputGeometry ); + QgsAttributes attrs = f.attributes(); + attrs << radius + << M_PI *radius *radius; + f.setAttributes( attrs ); + } + return f; +} + +QString QgsConvexHullAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm calculates the convex hull for each feature in an input layer." ); +} + +QgsConvexHullAlgorithm *QgsConvexHullAlgorithm::createInstance() const +{ + return new QgsConvexHullAlgorithm(); +} + +QgsFields QgsConvexHullAlgorithm::outputFields( const QgsFields &inputFields ) const +{ + QgsFields fields = inputFields; + fields.append( QgsField( QStringLiteral( "area" ), QVariant::Double, QString(), 20, 6 ) ); + fields.append( QgsField( QStringLiteral( "perimeter" ), QVariant::Double, QString(), 20, 6 ) ); + return fields; +} + +QgsFeature QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) +{ + QgsFeature f = feature; + if ( f.hasGeometry() ) + { + QgsGeometry outputGeometry = f.geometry().convexHull(); + if ( !outputGeometry ) + feedback->reportError( outputGeometry.lastError() ); + f.setGeometry( outputGeometry ); + if ( outputGeometry ) + { + QgsAttributes attrs = f.attributes(); + attrs << outputGeometry.geometry()->area() + << outputGeometry.geometry()->perimeter(); + f.setAttributes( attrs ); + } + } + return f; +} + ///@endcond + + + + diff --git a/src/core/processing/qgsnativealgorithms.h b/src/core/processing/qgsnativealgorithms.h index c23fbeb5140..89fdaa43b9a 100644 --- a/src/core/processing/qgsnativealgorithms.h +++ b/src/core/processing/qgsnativealgorithms.h @@ -323,6 +323,107 @@ class QgsRemoveNullGeometryAlgorithm : public QgsProcessingAlgorithm }; +/** + * Native bounding boxes algorithm. + */ +class QgsBoundingBoxAlgorithm : public QgsProcessingFeatureBasedAlgorithm +{ + + public: + + QgsBoundingBoxAlgorithm() = default; + QString name() const override { return QStringLiteral( "boundingboxes" ); } + QString displayName() const override { return QObject::tr( "Bounding boxes" ); } + virtual QStringList tags() const override { return QObject::tr( "bounding,boxes,envelope,rectangle,extent" ).split( ',' ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } + QString shortHelpString() const override; + QgsBoundingBoxAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + QString outputName() const override { return QObject::tr( "Bounds" ); } + QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type ) const override { return QgsWkbTypes::Polygon; } + QgsFields outputFields( const QgsFields &inputFields ) const override; + QgsFeature processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) override; + +}; + +/** + * Native minimum oriented bounding box algorithm. + */ +class QgsOrientedMinimumBoundingBoxAlgorithm : public QgsProcessingFeatureBasedAlgorithm +{ + + public: + + QgsOrientedMinimumBoundingBoxAlgorithm() = default; + QString name() const override { return QStringLiteral( "orientedminimumboundingbox" ); } + QString displayName() const override { return QObject::tr( "Oriented minimum bounding box" ); } + virtual QStringList tags() const override { return QObject::tr( "bounding,boxes,envelope,rectangle,extent,oriented,angle" ).split( ',' ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } + QString shortHelpString() const override; + QgsOrientedMinimumBoundingBoxAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + QString outputName() const override { return QObject::tr( "Bounding boxes" ); } + QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type ) const override { return QgsWkbTypes::Polygon; } + QgsFields outputFields( const QgsFields &inputFields ) const override; + QgsFeature processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) override; + +}; + +/** + * Native minimum enclosing circle algorithm. + */ +class QgsMinimumEnclosingCircleAlgorithm : public QgsProcessingFeatureBasedAlgorithm +{ + + public: + + QgsMinimumEnclosingCircleAlgorithm() = default; + void initParameters( const QVariantMap &configuration = QVariantMap() ) override; + QString name() const override { return QStringLiteral( "minimumenclosingcircle" ); } + QString displayName() const override { return QObject::tr( "Minimum enclosing circles" ); } + virtual QStringList tags() const override { return QObject::tr( "minimum,circle,ellipse,extent,bounds,bounding" ).split( ',' ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } + QString shortHelpString() const override; + QgsMinimumEnclosingCircleAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + QString outputName() const override { return QObject::tr( "Minimum enclosing circles" ); } + QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type ) const override { return QgsWkbTypes::Polygon; } + QgsFields outputFields( const QgsFields &inputFields ) const override; + bool prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; + QgsFeature processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) override; + + private: + + int mSegments = 72; +}; + +/** + * Native convex hull algorithm. + */ +class QgsConvexHullAlgorithm : public QgsProcessingFeatureBasedAlgorithm +{ + + public: + + QgsConvexHullAlgorithm() = default; + QString name() const override { return QStringLiteral( "convexhull" ); } + QString displayName() const override { return QObject::tr( "Convex hull" ); } + virtual QStringList tags() const override { return QObject::tr( "convex,hull,bounds,bounding" ).split( ',' ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } + QString shortHelpString() const override; + QgsConvexHullAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + QString outputName() const override { return QObject::tr( "Convex hulls" ); } + QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type ) const override { return QgsWkbTypes::Polygon; } + QgsFields outputFields( const QgsFields &inputFields ) const override; + QgsFeature processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) override; + +}; + ///@endcond PRIVATE #endif // QGSNATIVEALGORITHMS_H From 95eab5127f0d1a79b7c1fc687364ec74fc12cc29 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 3 Sep 2017 19:32:33 +1000 Subject: [PATCH 279/364] Remove redundant algorithms --- python/plugins/processing/algs/help/qgis.yaml | 13 -- .../processing/algs/qgis/BoundingBox.py | 75 ------- .../processing/algs/qgis/ConvexHull.py | 191 ------------------ .../algs/qgis/MinimalEnclosingCircle.py | 142 ------------- .../algs/qgis/OrientedMinimumBoundingBox.py | 148 -------------- .../algs/qgis/QGISAlgorithmProvider.py | 8 - .../tests/testdata/qgis_algorithm_tests.yaml | 38 ---- 7 files changed, 615 deletions(-) delete mode 100644 python/plugins/processing/algs/qgis/BoundingBox.py delete mode 100644 python/plugins/processing/algs/qgis/ConvexHull.py delete mode 100644 python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py delete mode 100644 python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index a488f121bd7..e981811dfcb 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -43,9 +43,6 @@ qgis:basicstatisticsforfields: > qgis:boundary: > Returns the closure of the combinatorial boundary of the input geometries (ie the topological boundary of the geometry). For instance, a polygon geometry will have a boundary consisting of the linestrings for each ring in the polygon. Only valid for polygon or line layers. -qgis:boundingboxes: > - This algorithm calculates the bounding box (envelope) of each feature in an input layer. - qgis:buildvirtualvector: > This algorithm creates a virtual layer that contains a set of vector layer. @@ -76,11 +73,6 @@ qgis:convertgeometrytype: > See the "Polygonize" or "Lines to polygons" algorithm for alternative options. -qgis:convexhull: > - This algorithm computes the convex hull of features in a layer. - - If a field is specified, it will divide the features into classes based on that field, and compute a separate convex hull for the features in each class. - qgis:countpointsinpolygon: > This algorithm takes a points layer and a polygon layer and counts the number of points from the first one in each polygons of the second one. @@ -356,11 +348,6 @@ qgis:offsetline: > The miter limit parameter is only applicable for miter join styles, and controls the maximum distance from the offset curve to use when creating a mitered join. -qgis:orientedminimumboundingbox: > - This algorithm takes a vector layer and generate a new one with the minimum rectangle that covers all the input features. - - As an alternative, the output layer can contain not just a single rectangle, but one for each input feature, representing the minimum rectangle that covers each of them. - qgis:minimalenclosingcircle: > This algorithm takes a vector layer and generate a new one with the minimum enclosing circle that covers all the input features. diff --git a/python/plugins/processing/algs/qgis/BoundingBox.py b/python/plugins/processing/algs/qgis/BoundingBox.py deleted file mode 100644 index d00d7156b4e..00000000000 --- a/python/plugins/processing/algs/qgis/BoundingBox.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - BoundingBox.py - -------------- - Date : July 2016 - Copyright : (C) 2016 by Nyall Dawson - Email : nyall dot dawson at gmail dot com -*************************************************************************** -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -*************************************************************************** -""" - -__author__ = 'Nyall Dawson' -__date__ = 'July 2016' -__copyright__ = '(C) 2016, Nyall Dawson' - -# This will get replaced with a git SHA1 when you do a git archive323 - -__revision__ = '$Format:%H$' - -import os - -from qgis.core import (QgsGeometry, - QgsWkbTypes, - QgsProcessingException) - - -from qgis.PyQt.QtGui import QIcon - -from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm - -pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] - - -class BoundingBox(QgisFeatureBasedAlgorithm): - - def icon(self): - return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'matrix.png')) - - def group(self): - return self.tr('Vector geometry') - - def __init__(self): - super().__init__() - - def name(self): - return 'boundingboxes' - - def displayName(self): - return self.tr('Bounding boxes') - - def outputName(self): - return self.tr('Bounds') - - def outputWkbType(self, inputWkb): - return QgsWkbTypes.Polygon - - def processFeature(self, feature, feedback): - input_geometry = feature.geometry() - if input_geometry: - output_geometry = QgsGeometry.fromRect(input_geometry.boundingBox()) - if not output_geometry: - raise QgsProcessingException( - self.tr('Error calculating bounding box')) - - feature.setGeometry(output_geometry) - - return feature diff --git a/python/plugins/processing/algs/qgis/ConvexHull.py b/python/plugins/processing/algs/qgis/ConvexHull.py deleted file mode 100644 index 214140bab3a..00000000000 --- a/python/plugins/processing/algs/qgis/ConvexHull.py +++ /dev/null @@ -1,191 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - ConvexHull.py - --------------------- - Date : August 2012 - Copyright : (C) 2012 by Victor Olaya - Email : volayaf at gmail dot com -*************************************************************************** -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -*************************************************************************** -""" -from builtins import str - -__author__ = 'Victor Olaya' -__date__ = 'August 2012' -__copyright__ = '(C) 2012, Victor Olaya' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -import os - -from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtCore import QVariant - -from qgis.core import (QgsField, - QgsFeature, - QgsFeatureSink, - QgsGeometry, - QgsWkbTypes, - QgsFeatureRequest, - QgsFields, - NULL, - QgsProcessingParameterFeatureSource, - QgsProcessingParameterField, - QgsProcessingParameterFeatureSink, - QgsProcessing, - QgsProcessingException) - -from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.tools import dataobjects, vector - -pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] - - -class ConvexHull(QgisAlgorithm): - - INPUT = 'INPUT' - OUTPUT = 'OUTPUT' - FIELD = 'FIELD' - - def icon(self): - return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png')) - - def group(self): - return self.tr('Vector geometry') - - def __init__(self): - super().__init__() - - def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Input layer'))) - self.addParameter(QgsProcessingParameterField(self.FIELD, - self.tr('Field (optional, set if creating convex hulls by classes)'), - parentLayerParameterName=self.INPUT, optional=True)) - self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Convex hull'), QgsProcessing.TypeVectorPolygon)) - - def name(self): - return 'convexhull' - - def displayName(self): - return self.tr('Convex hull') - - def processAlgorithm(self, parameters, context, feedback): - source = self.parameterAsSource(parameters, self.INPUT, context) - fieldName = self.parameterAsString(parameters, self.FIELD, context) - useField = bool(fieldName) - - field_index = None - f = QgsField('value', QVariant.String, '', 255) - if useField: - field_index = source.fields().lookupField(fieldName) - fType = source.fields()[field_index].type() - if fType in [QVariant.Int, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong]: - f.setType(fType) - f.setLength(20) - elif fType == QVariant.Double: - f.setType(QVariant.Double) - f.setLength(20) - f.setPrecision(6) - else: - f.setType(QVariant.String) - f.setLength(255) - - fields = QgsFields() - fields.append(QgsField('id', QVariant.Int, '', 20)) - fields.append(f) - fields.append(QgsField('area', QVariant.Double, '', 20, 6)) - fields.append(QgsField('perim', QVariant.Double, '', 20, 6)) - - (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, - fields, QgsWkbTypes.Polygon, source.sourceCrs()) - - outFeat = QgsFeature() - outGeom = QgsGeometry() - - fid = 0 - val = None - if useField: - unique = source.uniqueValues(field_index) - current = 0 - total = 100.0 / (source.featureCount() * len(unique)) if source.featureCount() else 1 - for i in unique: - if feedback.isCanceled(): - break - - first = True - hull = [] - features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index])) - for f in features: - if feedback.isCanceled(): - break - - idVar = f.attributes()[field_index] - if str(idVar).strip() == str(i).strip(): - if first: - val = idVar - first = False - - inGeom = f.geometry() - points = vector.extractPoints(inGeom) - hull.extend(points) - current += 1 - feedback.setProgress(int(current * total)) - - if len(hull) >= 3: - tmpGeom = QgsGeometry(outGeom.fromMultiPoint(hull)) - try: - outGeom = tmpGeom.convexHull() - if outGeom: - area = outGeom.geometry().area() - perim = outGeom.geometry().perimeter() - else: - area = NULL - perim = NULL - outFeat.setGeometry(outGeom) - outFeat.setAttributes([fid, val, area, perim]) - sink.addFeature(outFeat, QgsFeatureSink.FastInsert) - except: - raise QgsProcessingException( - self.tr('Exception while computing convex hull')) - fid += 1 - else: - hull = [] - total = 100.0 / source.featureCount() if source.featureCount() else 1 - features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])) - for current, f in enumerate(features): - if feedback.isCanceled(): - break - - inGeom = f.geometry() - points = vector.extractPoints(inGeom) - hull.extend(points) - feedback.setProgress(int(current * total)) - - tmpGeom = QgsGeometry(outGeom.fromMultiPoint(hull)) - try: - outGeom = tmpGeom.convexHull() - if outGeom: - area = outGeom.geometry().area() - perim = outGeom.geometry().perimeter() - else: - area = NULL - perim = NULL - outFeat.setGeometry(outGeom) - outFeat.setAttributes([0, 'all', area, perim]) - sink.addFeature(outFeat, QgsFeatureSink.FastInsert) - except: - raise QgsProcessingException( - self.tr('Exception while computing convex hull')) - - return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py b/python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py deleted file mode 100644 index a31e8b48526..00000000000 --- a/python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py +++ /dev/null @@ -1,142 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - MinimalEnclosingCircle.py - --------------------- - Date : September 2017 - Copyright : (C) 2017, Loïc BARTOLETTI - Email : lbartoletti at tuxfamily dot org -*************************************************************************** -* * -* 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. * -* * -*************************************************************************** -""" - -__author__ = 'Loïc BARTOLETTI' -__date__ = 'September 2017' -__copyright__ = '(C) 2017, Loïc BARTOLETTI' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -from qgis.PyQt.QtCore import QVariant -from qgis.core import (QgsField, - QgsFields, - QgsFeatureSink, - QgsGeometry, - QgsFeature, - QgsWkbTypes, - QgsFeatureRequest, - QgsProcessing, - QgsProcessingParameterFeatureSource, - QgsProcessingParameterBoolean, - QgsProcessingParameterFeatureSink, - QgsProcessingException) -from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm - - -class MinimalEnclosingCircle(QgisAlgorithm): - - INPUT = 'INPUT' - BY_FEATURE = 'BY_FEATURE' - - OUTPUT = 'OUTPUT' - - def group(self): - return self.tr('Vector general') - - def __init__(self): - super().__init__() - - def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Input layer'), [QgsProcessing.TypeVectorAnyGeometry])) - self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE, - self.tr('Calculate bounds for each feature separately'), defaultValue=True)) - - self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Enclosing circles'), QgsProcessing.TypeVectorPolygon)) - - def name(self): - return 'minimalenclosingcircle' - - def displayName(self): - return self.tr('Minimal enclosing circle') - - def processAlgorithm(self, parameters, context, feedback): - source = self.parameterAsSource(parameters, self.INPUT, context) - by_feature = self.parameterAsBool(parameters, self.BY_FEATURE, context) - - if not by_feature and QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PointGeometry and source.featureCount() <= 2: - raise QgsProcessingException(self.tr("Can't calculate a minimal enclosing circle for each point, it's a point. The number of points must be greater than 2")) - - if by_feature: - fields = source.fields() - else: - fields = QgsFields() - fields.append(QgsField('center_x', QVariant.Double)) - fields.append(QgsField('center_y', QVariant.Double)) - fields.append(QgsField('radius', QVariant.Double)) - - (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, - fields, QgsWkbTypes.Polygon, source.sourceCrs()) - - if by_feature: - self.featureMec(source, context, sink, feedback) - else: - self.layerMec(source, context, sink, feedback) - - return {self.OUTPUT: dest_id} - - def layerMec(self, source, context, sink, feedback): - req = QgsFeatureRequest().setSubsetOfAttributes([]) - features = source.getFeatures(req) - total = 100.0 / source.featureCount() if source.featureCount() else 0 - newgeometry = QgsGeometry() - first = True - geometries = [] - for current, inFeat in enumerate(features): - if feedback.isCanceled(): - break - - if inFeat.hasGeometry(): - geometries.append(inFeat.geometry()) - feedback.setProgress(int(current * total)) - - newgeometry = QgsGeometry.unaryUnion(geometries) - geometry, center, radius = newgeometry.minimalEnclosingCircle() - - if geometry: - outFeat = QgsFeature() - - outFeat.setGeometry(geometry) - outFeat.setAttributes([center.x(), - center.y(), - radius]) - sink.addFeature(outFeat, QgsFeatureSink.FastInsert) - - def featureMec(self, source, context, sink, feedback): - features = source.getFeatures() - total = 100.0 / source.featureCount() if source.featureCount() else 0 - outFeat = QgsFeature() - for current, inFeat in enumerate(features): - if feedback.isCanceled(): - break - - geometry, center, radius = inFeat.geometry().minimalEnclosingCircle() - if geometry: - outFeat.setGeometry(geometry) - attrs = inFeat.attributes() - attrs.extend([center.x(), - center.y(), - radius]) - outFeat.setAttributes(attrs) - sink.addFeature(outFeat, QgsFeatureSink.FastInsert) - else: - feedback.pushInfo(self.tr("Can't calculate a minimal enclosing circle for feature {0}.").format(inFeat.id())) - feedback.setProgress(int(current * total)) diff --git a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py b/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py deleted file mode 100644 index 1d85ee9eb2e..00000000000 --- a/python/plugins/processing/algs/qgis/OrientedMinimumBoundingBox.py +++ /dev/null @@ -1,148 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - OrientedMinimumBoundingBox.py - --------------------- - Date : June 2015 - Copyright : (C) 2015, Loïc BARTOLETTI - Email : coder at tuxfamily dot org -*************************************************************************** -* * -* 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. * -* * -*************************************************************************** -""" - -__author__ = 'Loïc BARTOLETTI' -__date__ = 'June 2015' -__copyright__ = '(C) 2015, Loïc BARTOLETTI' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -from qgis.PyQt.QtCore import QVariant -from qgis.core import (QgsField, - QgsFields, - QgsFeatureSink, - QgsGeometry, - QgsFeature, - QgsWkbTypes, - QgsFeatureRequest, - QgsProcessing, - QgsProcessingParameterFeatureSource, - QgsProcessingParameterBoolean, - QgsProcessingParameterFeatureSink, - QgsProcessingException) -from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm - - -class OrientedMinimumBoundingBox(QgisAlgorithm): - - INPUT = 'INPUT' - BY_FEATURE = 'BY_FEATURE' - - OUTPUT = 'OUTPUT' - - def group(self): - return self.tr('Vector general') - - def __init__(self): - super().__init__() - - def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Input layer'), [QgsProcessing.TypeVectorAnyGeometry])) - self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE, - self.tr('Calculate bounds for each feature separately'), defaultValue=True)) - - self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding boxes'), QgsProcessing.TypeVectorPolygon)) - - def name(self): - return 'orientedminimumboundingbox' - - def displayName(self): - return self.tr('Oriented minimum bounding box') - - def processAlgorithm(self, parameters, context, feedback): - source = self.parameterAsSource(parameters, self.INPUT, context) - by_feature = self.parameterAsBool(parameters, self.BY_FEATURE, context) - - if not by_feature and QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PointGeometry and source.featureCount() <= 2: - raise QgsProcessingException(self.tr("Can't calculate an OMBB for each point, it's a point. The number of points must be greater than 2")) - - if by_feature: - fields = source.fields() - else: - fields = QgsFields() - fields.append(QgsField('area', QVariant.Double)) - fields.append(QgsField('perimeter', QVariant.Double)) - fields.append(QgsField('angle', QVariant.Double)) - fields.append(QgsField('width', QVariant.Double)) - fields.append(QgsField('height', QVariant.Double)) - - (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, - fields, QgsWkbTypes.Polygon, source.sourceCrs()) - - if by_feature: - self.featureOmbb(source, context, sink, feedback) - else: - self.layerOmmb(source, context, sink, feedback) - - return {self.OUTPUT: dest_id} - - def layerOmmb(self, source, context, sink, feedback): - req = QgsFeatureRequest().setSubsetOfAttributes([]) - features = source.getFeatures(req) - total = 100.0 / source.featureCount() if source.featureCount() else 0 - newgeometry = QgsGeometry() - first = True - geometries = [] - for current, inFeat in enumerate(features): - if feedback.isCanceled(): - break - - if inFeat.hasGeometry(): - geometries.append(inFeat.geometry()) - feedback.setProgress(int(current * total)) - - newgeometry = QgsGeometry.unaryUnion(geometries) - geometry, area, angle, width, height = newgeometry.orientedMinimumBoundingBox() - - if geometry: - outFeat = QgsFeature() - - outFeat.setGeometry(geometry) - outFeat.setAttributes([area, - width * 2 + height * 2, - angle, - width, - height]) - sink.addFeature(outFeat, QgsFeatureSink.FastInsert) - - def featureOmbb(self, source, context, sink, feedback): - features = source.getFeatures() - total = 100.0 / source.featureCount() if source.featureCount() else 0 - outFeat = QgsFeature() - for current, inFeat in enumerate(features): - if feedback.isCanceled(): - break - - geometry, area, angle, width, height = inFeat.geometry().orientedMinimumBoundingBox() - if geometry: - outFeat.setGeometry(geometry) - attrs = inFeat.attributes() - attrs.extend([area, - width * 2 + height * 2, - angle, - width, - height]) - outFeat.setAttributes(attrs) - sink.addFeature(outFeat, QgsFeatureSink.FastInsert) - else: - feedback.pushInfo(self.tr("Can't calculate an OMBB for feature {0}.").format(inFeat.id())) - feedback.setProgress(int(current * total)) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 7299b188c5c..6b858b696f7 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -46,10 +46,8 @@ from .Aspect import Aspect from .AutoincrementalField import AutoincrementalField from .BasicStatistics import BasicStatisticsForField from .Boundary import Boundary -from .BoundingBox import BoundingBox from .CheckValidity import CheckValidity from .ConcaveHull import ConcaveHull -from .ConvexHull import ConvexHull from .CreateAttributeIndex import CreateAttributeIndex from .CreateConstantRaster import CreateConstantRaster from .Datasources2Vrt import Datasources2Vrt @@ -101,10 +99,8 @@ from .MeanCoords import MeanCoords from .Merge import Merge from .MergeLines import MergeLines from .MinimumBoundingGeometry import MinimumBoundingGeometry -from .MinimalEnclosingCircle import MinimalEnclosingCircle from .NearestNeighbourAnalysis import NearestNeighbourAnalysis from .OffsetLine import OffsetLine -from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox from .Orthogonalize import Orthogonalize from .PointDistance import PointDistance from .PointOnSurface import PointOnSurface @@ -201,10 +197,8 @@ class QGISAlgorithmProvider(QgsProcessingProvider): AutoincrementalField(), BasicStatisticsForField(), Boundary(), - BoundingBox(), CheckValidity(), ConcaveHull(), - ConvexHull(), CreateAttributeIndex(), CreateConstantRaster(), Datasources2Vrt(), @@ -256,10 +250,8 @@ class QGISAlgorithmProvider(QgsProcessingProvider): Merge(), MergeLines(), MinimumBoundingGeometry(), - MinimalEnclosingCircle(), NearestNeighbourAnalysis(), OffsetLine(), - OrientedMinimumBoundingBox(), Orthogonalize(), PointDistance(), PointOnSurface(), diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index db4f901cb4e..2cb45535ff2 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2510,29 +2510,6 @@ tests: name: expected/join_attribute_table.gml type: vector - - algorithm: qgis:convexhull - name: Simple convex hull - params: - INPUT: - name: custom/points_hull.gml - type: vector - results: - OUTPUT: - name: expected/convex_hull.gml - type: vector - - - algorithm: qgis:convexhull - name: Convex hull based on field attribute - params: - FIELD: hull - INPUT: - name: custom/points_hull.gml - type: vector - results: - OUTPUT: - name: expected/convex_hull_fields.gml - type: vector - # # These tests dissabled because algs require access to iface which # # is not available in the test suite. # #- algorithm: qgis:shortestpathpointtopoint @@ -3254,21 +3231,6 @@ tests: geometry: precision: 7 - - algorithm: qgis:minimalenclosingcircle - name: Minimal enclosing circle all features - params: - BY_FEATURE: false - INPUT: - name: custom/oriented_bbox.gml - type: vector - results: - OUTPUT: - name: expected/enclosing_circles_all.gml - type: vector - compare: - geometry: - precision: 7 - - algorithm: qgis:minimumboundinggeometry name: Minimum enclosing geom (hull, no field) params: From 9eeecbed659f5416d12ee6f7243b0fa8cb3f1c48 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 3 Sep 2017 19:35:41 +1000 Subject: [PATCH 280/364] Add note to see 'minimum bounding geometry' alg to feature by feature alg help --- src/core/processing/qgsnativealgorithms.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/processing/qgsnativealgorithms.cpp b/src/core/processing/qgsnativealgorithms.cpp index 8a2f17df3ae..21ec2c908ba 100644 --- a/src/core/processing/qgsnativealgorithms.cpp +++ b/src/core/processing/qgsnativealgorithms.cpp @@ -1093,7 +1093,7 @@ QVariantMap QgsRemoveNullGeometryAlgorithm::processAlgorithm( const QVariantMap QString QgsBoundingBoxAlgorithm::shortHelpString() const { - return QObject::tr( "This algorithm calculates the bounding box (envelope) of each feature in an input layer." ); + return QObject::tr( "This algorithm calculates the bounding box (envelope) for each feature in an input layer.\n\nSee the 'Minimum bounding geometry' algorithm for a bounding box calculation which covers the whole layer or grouped subsets of features." ); } QgsBoundingBoxAlgorithm *QgsBoundingBoxAlgorithm::createInstance() const @@ -1131,7 +1131,7 @@ QgsFeature QgsBoundingBoxAlgorithm::processFeature( const QgsFeature &feature, Q QString QgsOrientedMinimumBoundingBoxAlgorithm::shortHelpString() const { - return QObject::tr( "This algorithm calculates the minimum area rotated rectangle which covers each feature in an input layer." ); + return QObject::tr( "This algorithm calculates the minimum area rotated rectangle which covers each feature in an input layer.\n\nSee the 'Minimum bounding geometry' algorithm for a oriented bounding box calculation which covers the whole layer or grouped subsets of features." ); } QgsOrientedMinimumBoundingBoxAlgorithm *QgsOrientedMinimumBoundingBoxAlgorithm::createInstance() const @@ -1181,7 +1181,7 @@ void QgsMinimumEnclosingCircleAlgorithm::initParameters( const QVariantMap & ) QString QgsMinimumEnclosingCircleAlgorithm::shortHelpString() const { - return QObject::tr( "This algorithm calculates the minimum enclosing circle which covers each feature in an input layer." ); + return QObject::tr( "This algorithm calculates the minimum enclosing circle which covers each feature in an input layer.\n\nSee the 'Minimum bounding geometry' algorithm for a minimal enclosing circle calculation which covers the whole layer or grouped subsets of features." ); } QgsMinimumEnclosingCircleAlgorithm *QgsMinimumEnclosingCircleAlgorithm::createInstance() const @@ -1222,7 +1222,7 @@ QgsFeature QgsMinimumEnclosingCircleAlgorithm::processFeature( const QgsFeature QString QgsConvexHullAlgorithm::shortHelpString() const { - return QObject::tr( "This algorithm calculates the convex hull for each feature in an input layer." ); + return QObject::tr( "This algorithm calculates the convex hull for each feature in an input layer.\n\nSee the 'Minimum bounding geometry' algorithm for a convex hull calculation which covers the whole layer or grouped subsets of features." ); } QgsConvexHullAlgorithm *QgsConvexHullAlgorithm::createInstance() const From 5d504dcafb5ff8e76e4ca8f7ea99f0a4fcb532ad Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 3 Sep 2017 19:42:49 +1000 Subject: [PATCH 281/364] Unify processing "polygon from layer extent" algorithms Now that the extra features of the "polygon from vector layer extent" algorithm are covered by the new "Minimum bounding geometry" algorithm, we can replace the previous two "polygon from vector extent" and "polygon from raster extent" algorithms by a single "polygon from layer extent" algorithm. --- python/plugins/processing/algs/help/qgis.yaml | 7 +- .../processing/algs/qgis/ExtentFromLayer.py | 73 ++-------- .../algs/qgis/ExtentFromRasterLayer.py | 127 ------------------ .../algs/qgis/QGISAlgorithmProvider.py | 2 - .../tests/testdata/qgis_algorithm_tests.yaml | 2 +- 5 files changed, 15 insertions(+), 196 deletions(-) delete mode 100755 python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index e981811dfcb..170ec485b03 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -402,12 +402,7 @@ qgis:polygoncentroids: > NOTE: This algorithm is deprecated and the generic "centroids" algorithm (which works for line and multi geometry layers) should be used instead. qgis:polygonfromlayerextent: > - This algorithm takes a vector layer and generates a new one with the minimum bounding box (rectangle with N-S orientation) that covers all the input features. - - As an alternative, the output layer can contain not just a single bounding box, but one for each input feature, representing the bounding box of each of them. - -qgis:polygonfromrasterextent: > - This algorithm takes a raster layer and generates a vector layer containing a feature with the minimum bounding box that covers the raster layer's extent. + This algorithm takes a map layer and generates a new vector layer with the minimum bounding box (rectangle with N-S orientation) that covers the input layer. qgis:polygonize: > This algorithm takes a lines layer and creates a polygon layer, with polygons generated from the lines in the input layer. diff --git a/python/plugins/processing/algs/qgis/ExtentFromLayer.py b/python/plugins/processing/algs/qgis/ExtentFromLayer.py index 61a21426d53..99aa9232a83 100644 --- a/python/plugins/processing/algs/qgis/ExtentFromLayer.py +++ b/python/plugins/processing/algs/qgis/ExtentFromLayer.py @@ -32,14 +32,12 @@ from qgis.PyQt.QtCore import QVariant from qgis.core import (QgsField, QgsFeatureSink, - QgsPointXY, QgsGeometry, QgsFeature, QgsWkbTypes, QgsProcessing, - QgsProcessingParameterFeatureSource, + QgsProcessingParameterMapLayer, QgsProcessingParameterFeatureSink, - QgsProcessingParameterBoolean, QgsFields) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm @@ -61,27 +59,23 @@ class ExtentFromLayer(QgisAlgorithm): return self.tr('extent,envelope,bounds,bounding,boundary,layer').split(',') def group(self): - return self.tr('Vector general') + return self.tr('Layer tools') def __init__(self): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) - self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE, - self.tr('Calculate extent for each feature separately'), False)) - + self.addParameter(QgsProcessingParameterMapLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent'), type=QgsProcessing.TypeVectorPolygon)) def name(self): return 'polygonfromlayerextent' def displayName(self): - return self.tr('Polygon from vector extent') + return self.tr('Polygon from layer extent') def processAlgorithm(self, parameters, context, feedback): - source = self.parameterAsSource(parameters, self.INPUT, context) - byFeature = self.parameterAsBool(parameters, self.BY_FEATURE, context) + layer = self.parameterAsLayer(parameters, self.INPUT, context) fields = QgsFields() fields.append(QgsField('MINX', QVariant.Double)) @@ -96,17 +90,15 @@ class ExtentFromLayer(QgisAlgorithm): fields.append(QgsField('WIDTH', QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, - fields, QgsWkbTypes.Polygon, source.sourceCrs()) + fields, QgsWkbTypes.Polygon, layer.crs()) - if byFeature: - self.featureExtent(source, context, sink, feedback) - else: - self.layerExtent(source, sink, feedback) + try: + # may not be possible + layer.updateExtents() + except: + pass - return {self.OUTPUT: dest_id} - - def layerExtent(self, source, sink, feedback): - rect = source.sourceExtent() + rect = layer.extent() geometry = QgsGeometry.fromRect(rect) minx = rect.xMinimum() miny = rect.yMinimum() @@ -136,43 +128,4 @@ class ExtentFromLayer(QgisAlgorithm): feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) - def featureExtent(self, source, context, sink, feedback): - features = source.getFeatures() - total = 100.0 / source.featureCount() if source.featureCount() else 0 - feat = QgsFeature() - for current, f in enumerate(features): - if feedback.isCanceled(): - break - - rect = f.geometry().boundingBox() - minx = rect.xMinimum() - miny = rect.yMinimum() - maxx = rect.xMaximum() - maxy = rect.yMaximum() - height = rect.height() - width = rect.width() - cntx = minx + width / 2.0 - cnty = miny + height / 2.0 - area = width * height - perim = 2 * width + 2 * height - rect = [QgsPointXY(minx, miny), QgsPointXY(minx, maxy), QgsPointXY(maxx, - maxy), QgsPointXY(maxx, miny), QgsPointXY(minx, miny)] - - geometry = QgsGeometry().fromPolygon([rect]) - feat.setGeometry(geometry) - attrs = [ - minx, - miny, - maxx, - maxy, - cntx, - cnty, - area, - perim, - height, - width, - ] - feat.setAttributes(attrs) - - sink.addFeature(feat, QgsFeatureSink.FastInsert) - feedback.setProgress(int(current * total)) + return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py b/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py deleted file mode 100755 index 63efd366aef..00000000000 --- a/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - ExtentFromRasterLayer.py - --------------------- - Date : August 2017 - Copyright : (C) 2017 by Nyall Dawson - Email : nyall dot dawson at gmail dot com -*************************************************************************** -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -*************************************************************************** -""" - -__author__ = 'Nyall Dawson' -__date__ = 'August 2017' -__copyright__ = '(C) 2017, Nyall Dawson' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -import os - -from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtCore import QVariant - -from qgis.core import (QgsField, - QgsFeatureSink, - QgsPointXY, - QgsGeometry, - QgsFeature, - QgsWkbTypes, - QgsProcessing, - QgsProcessingParameterRasterLayer, - QgsProcessingParameterFeatureSink, - QgsFields) - -from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm - -pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] - - -class ExtentFromRasterLayer(QgisAlgorithm): - - INPUT = 'INPUT' - OUTPUT = 'OUTPUT' - - def icon(self): - return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'layer_extent.png')) - - def tags(self): - return self.tr('extent,envelope,bounds,bounding,boundary,layer').split(',') - - def group(self): - return self.tr('Raster tools') - - def __init__(self): - super().__init__() - - def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) - self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent'), type=QgsProcessing.TypeVectorPolygon)) - - def name(self): - return 'polygonfromrasterextent' - - def displayName(self): - return self.tr('Polygon from raster extent') - - def processAlgorithm(self, parameters, context, feedback): - raster = self.parameterAsRasterLayer(parameters, self.INPUT, context) - - fields = QgsFields() - fields.append(QgsField('MINX', QVariant.Double)) - fields.append(QgsField('MINY', QVariant.Double)) - fields.append(QgsField('MAXX', QVariant.Double)) - fields.append(QgsField('MAXY', QVariant.Double)) - fields.append(QgsField('CNTX', QVariant.Double)) - fields.append(QgsField('CNTY', QVariant.Double)) - fields.append(QgsField('AREA', QVariant.Double)) - fields.append(QgsField('PERIM', QVariant.Double)) - fields.append(QgsField('HEIGHT', QVariant.Double)) - fields.append(QgsField('WIDTH', QVariant.Double)) - - (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, - fields, QgsWkbTypes.Polygon, raster.crs()) - - self.layerExtent(raster, sink, feedback) - - return {self.OUTPUT: dest_id} - - def layerExtent(self, raster, sink, feedback): - rect = raster.extent() - geometry = QgsGeometry.fromRect(rect) - minx = rect.xMinimum() - miny = rect.yMinimum() - maxx = rect.xMaximum() - maxy = rect.yMaximum() - height = rect.height() - width = rect.width() - cntx = minx + width / 2.0 - cnty = miny + height / 2.0 - area = width * height - perim = 2 * width + 2 * height - - feat = QgsFeature() - feat.setGeometry(geometry) - attrs = [ - minx, - miny, - maxx, - maxy, - cntx, - cnty, - area, - perim, - height, - width, - ] - feat.setAttributes(attrs) - sink.addFeature(feat, QgsFeatureSink.FastInsert) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 6b858b696f7..c96471346f2 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -68,7 +68,6 @@ from .Explode import Explode from .ExportGeometryInfo import ExportGeometryInfo from .ExtendLines import ExtendLines from .ExtentFromLayer import ExtentFromLayer -from .ExtentFromRasterLayer import ExtentFromRasterLayer from .ExtractNodes import ExtractNodes from .ExtractSpecificNodes import ExtractSpecificNodes from .FieldPyculator import FieldsPyculator @@ -219,7 +218,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ExportGeometryInfo(), ExtendLines(), ExtentFromLayer(), - ExtentFromRasterLayer(), ExtractNodes(), ExtractSpecificNodes(), FieldsCalculator(), diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 2cb45535ff2..fc0c5b4c551 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3205,7 +3205,7 @@ tests: name: expected/execute_sql.gml type: vector - - algorithm: qgis:polygonfromrasterextent + - algorithm: qgis:polygonfromlayerextent name: Polygon from raster extent params: INPUT: From f01ad63bbad41b0196323668e1f246ab9aca73e9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 08:45:07 +1000 Subject: [PATCH 282/364] Rename 'Polygon from layer extent' to 'Extract layer extent' It's slightly simpler to understand and better reflects what this alg does --- python/plugins/processing/algs/qgis/ExtentFromLayer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/algs/qgis/ExtentFromLayer.py b/python/plugins/processing/algs/qgis/ExtentFromLayer.py index 99aa9232a83..bf213fa586f 100644 --- a/python/plugins/processing/algs/qgis/ExtentFromLayer.py +++ b/python/plugins/processing/algs/qgis/ExtentFromLayer.py @@ -56,7 +56,7 @@ class ExtentFromLayer(QgisAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'layer_extent.png')) def tags(self): - return self.tr('extent,envelope,bounds,bounding,boundary,layer').split(',') + return self.tr('polygon,from,vector,raster,extent,envelope,bounds,bounding,boundary,layer').split(',') def group(self): return self.tr('Layer tools') @@ -72,7 +72,7 @@ class ExtentFromLayer(QgisAlgorithm): return 'polygonfromlayerextent' def displayName(self): - return self.tr('Polygon from layer extent') + return self.tr('Extract layer extent') def processAlgorithm(self, parameters, context, feedback): layer = self.parameterAsLayer(parameters, self.INPUT, context) From ea09aa92a314b4a7f2ddc599d16c99167f3087f9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 09:05:38 +1000 Subject: [PATCH 283/364] Remove QgsTransectSample Because: - it's unused in master, and is more code to maintain just for possible use by plugins - it's unmaintained, and has had no work done (beside compilation fixes) in the recent past - there's no unit tests or detailed documentation to show how the class should be used --- doc/api_break.dox | 7 +- python/analysis/analysis_auto.sip | 1 - python/analysis/vector/qgstransectsample.sip | 49 -- src/analysis/CMakeLists.txt | 2 - src/analysis/vector/qgstransectsample.cpp | 670 ------------------- src/analysis/vector/qgstransectsample.h | 106 --- 6 files changed, 1 insertion(+), 834 deletions(-) delete mode 100644 python/analysis/vector/qgstransectsample.sip delete mode 100644 src/analysis/vector/qgstransectsample.cpp delete mode 100644 src/analysis/vector/qgstransectsample.h diff --git a/doc/api_break.dox b/doc/api_break.dox index 1a21ffdc9d3..5506d46d5d7 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -311,6 +311,7 @@ should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinat - QgsSvgAnnotationItem. Use QgsSvgAnnotation instead. - QgsSymbologyV2Conversion was removed. Reading of renderers from pre-1.0 versions is not supported anymore. - QgsTextAnnotationItem. Use QgsTextAnnotation instead. +- QgsTransectSample. This class was unused and unmaintained. - TriangleInterpolator. - Triangulation. - TriDecorator. @@ -2352,12 +2353,6 @@ QgsTracer {#qgis_api_break_3_0_QgsTracer} - hasCrsTransformEnabled() and setCrsTransformEnabled() were removed. CRS transformation is now always enabled when required. -QgsTransectSample {#qgis_api_break_3_0_QgsTransectSample} ------------------ - -- createSample() now uses an optional QgsFeedback instead of QProgressDialog -for progress reports and cancelation. - QgsTreeWidgetItem {#qgis_api_break_3_0_QgsTreeWidgetItem} ----------------- diff --git a/python/analysis/analysis_auto.sip b/python/analysis/analysis_auto.sip index 929e30f7d51..ac48eef57df 100644 --- a/python/analysis/analysis_auto.sip +++ b/python/analysis/analysis_auto.sip @@ -13,7 +13,6 @@ %Include raster/qgsrastercalcnode.sip %Include raster/qgstotalcurvaturefilter.sip %Include vector/qgsgeometrysnapper.sip -%Include vector/qgstransectsample.sip %Include vector/qgszonalstatistics.sip %Include interpolation/qgsinterpolator.sip %Include interpolation/qgsgridfilewriter.sip diff --git a/python/analysis/vector/qgstransectsample.sip b/python/analysis/vector/qgstransectsample.sip deleted file mode 100644 index 28923f8f2f9..00000000000 --- a/python/analysis/vector/qgstransectsample.sip +++ /dev/null @@ -1,49 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgstransectsample.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - -class QgsTransectSample -{ -%Docstring - A class for the creation of transect sample lines based on a set of strata polygons and baselines* -%End - -%TypeHeaderCode -#include "qgstransectsample.h" -%End - public: - - enum DistanceUnits - { - Meters, - StrataUnits - }; - - QgsTransectSample( QgsVectorLayer *strataLayer, const QString &strataIdAttribute, const QString &minDistanceAttribute, const QString &nPointsAttribute, - DistanceUnits minDistUnits, QgsVectorLayer *baselineLayer, bool shareBaseline, - const QString &baselineStrataId, const QString &outputPointLayer, const QString &outputLineLayer, const QString &usedBaselineLayer, double minTransectLength = 0.0, - double baselineBufferDistance = -1.0, double baselineSimplificationTolerance = -1.0 ); - - int createSample( QgsFeedback *feedback = 0 ); -%Docstring - Creates the sample. - - The optional ``feedback`` argument can be used for progress reporting and cancelation support. - :rtype: int -%End - -}; - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/analysis/vector/qgstransectsample.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 9919e98e2e3..dc07abd2af3 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -36,7 +36,6 @@ SET(QGIS_ANALYSIS_SRCS raster/qgsrastermatrix.cpp vector/mersenne-twister.cpp vector/qgsgeometrysnapper.cpp - vector/qgstransectsample.cpp vector/qgszonalstatistics.cpp openstreetmap/qgsosmbase.cpp @@ -115,7 +114,6 @@ SET(QGIS_ANALYSIS_HDRS vector/mersenne-twister.h vector/qgsgeometrysnapper.h - vector/qgstransectsample.h vector/qgszonalstatistics.h interpolation/qgsinterpolator.h diff --git a/src/analysis/vector/qgstransectsample.cpp b/src/analysis/vector/qgstransectsample.cpp deleted file mode 100644 index 04549200c53..00000000000 --- a/src/analysis/vector/qgstransectsample.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/*************************************************************************** - qgstransectsample.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 "qgstransectsample.h" -#include "qgsdistancearea.h" -#include "qgsfeatureiterator.h" -#include "qgsgeometry.h" -#include "qgsspatialindex.h" -#include "qgsvectorfilewriter.h" -#include "qgsvectorlayer.h" -#include "qgsproject.h" -#include "qgsfeedback.h" - -#include -#ifndef _MSC_VER -#include -#endif -#include "mersenne-twister.h" -#include - -QgsTransectSample::QgsTransectSample( QgsVectorLayer *strataLayer, const QString &strataIdAttribute, const QString &minDistanceAttribute, const QString &nPointsAttribute, DistanceUnits minDistUnits, - QgsVectorLayer *baselineLayer, bool shareBaseline, const QString &baselineStrataId, const QString &outputPointLayer, - const QString &outputLineLayer, const QString &usedBaselineLayer, double minTransectLength, - double baselineBufferDistance, double baselineSimplificationTolerance ) - : mStrataLayer( strataLayer ) - , mStrataIdAttribute( strataIdAttribute ) - , mMinDistanceAttribute( minDistanceAttribute ) - , mNPointsAttribute( nPointsAttribute ) - , mBaselineLayer( baselineLayer ) - , mShareBaseline( shareBaseline ) - , mBaselineStrataId( baselineStrataId ) - , mOutputPointLayer( outputPointLayer ) - , mOutputLineLayer( outputLineLayer ) - , mUsedBaselineLayer( usedBaselineLayer ) - , mMinDistanceUnits( minDistUnits ) - , mMinTransectLength( minTransectLength ) - , mBaselineBufferDistance( baselineBufferDistance ) - , mBaselineSimplificationTolerance( baselineSimplificationTolerance ) -{ -} - -QgsTransectSample::QgsTransectSample() - : mStrataLayer( nullptr ) - , mBaselineLayer( nullptr ) - , mShareBaseline( false ) - , mMinDistanceUnits( Meters ) - , mMinTransectLength( 0.0 ) - , mBaselineBufferDistance( -1.0 ) - , mBaselineSimplificationTolerance( -1.0 ) -{ -} - -int QgsTransectSample::createSample( QgsFeedback *feedback ) -{ - if ( !mStrataLayer || !mStrataLayer->isValid() ) - { - return 1; - } - - if ( !mBaselineLayer || !mBaselineLayer->isValid() ) - { - return 2; - } - - //stratum id is not necessarily an integer - QVariant::Type stratumIdType = QVariant::Int; - if ( !mStrataIdAttribute.isEmpty() ) - { - stratumIdType = mStrataLayer->fields().field( mStrataIdAttribute ).type(); - } - - //create vector file writers for output - QgsFields outputPointFields; - outputPointFields.append( QgsField( QStringLiteral( "id" ), stratumIdType ) ); - outputPointFields.append( QgsField( QStringLiteral( "station_id" ), QVariant::Int ) ); - outputPointFields.append( QgsField( QStringLiteral( "stratum_id" ), stratumIdType ) ); - outputPointFields.append( QgsField( QStringLiteral( "station_code" ), QVariant::String ) ); - outputPointFields.append( QgsField( QStringLiteral( "start_lat" ), QVariant::Double ) ); - outputPointFields.append( QgsField( QStringLiteral( "start_long" ), QVariant::Double ) ); - - QgsVectorFileWriter outputPointWriter( mOutputPointLayer, QStringLiteral( "utf-8" ), outputPointFields, QgsWkbTypes::Point, - mStrataLayer->crs() ); - if ( outputPointWriter.hasError() != QgsVectorFileWriter::NoError ) - { - return 3; - } - - outputPointFields.append( QgsField( QStringLiteral( "bearing" ), QVariant::Double ) ); //add bearing attribute for lines - QgsVectorFileWriter outputLineWriter( mOutputLineLayer, QStringLiteral( "utf-8" ), outputPointFields, QgsWkbTypes::LineString, - mStrataLayer->crs() ); - if ( outputLineWriter.hasError() != QgsVectorFileWriter::NoError ) - { - return 4; - } - - QgsFields usedBaselineFields; - usedBaselineFields.append( QgsField( QStringLiteral( "stratum_id" ), stratumIdType ) ); - usedBaselineFields.append( QgsField( QStringLiteral( "ok" ), QVariant::String ) ); - QgsVectorFileWriter usedBaselineWriter( mUsedBaselineLayer, QStringLiteral( "utf-8" ), usedBaselineFields, QgsWkbTypes::LineString, - mStrataLayer->crs() ); - if ( usedBaselineWriter.hasError() != QgsVectorFileWriter::NoError ) - { - return 5; - } - - //debug: write clipped buffer bounds with stratum id to same directory as out_point - QFileInfo outputPointInfo( mOutputPointLayer ); - QString bufferClipLineOutput = outputPointInfo.absolutePath() + "/out_buffer_clip_line.shp"; - QgsFields bufferClipLineFields; - bufferClipLineFields.append( QgsField( QStringLiteral( "id" ), stratumIdType ) ); - QgsVectorFileWriter bufferClipLineWriter( bufferClipLineOutput, QStringLiteral( "utf-8" ), bufferClipLineFields, QgsWkbTypes::LineString, mStrataLayer->crs() ); - - //configure distanceArea depending on minDistance units and output CRS - QgsDistanceArea distanceArea; - distanceArea.setSourceCrs( mStrataLayer->crs() ); - if ( mMinDistanceUnits == Meters ) - { - distanceArea.setEllipsoid( QgsProject::instance()->ellipsoid() ); - } - else - { - distanceArea.setEllipsoid( GEO_NONE ); - } - - //possibility to transform output points to lat/long - QgsCoordinateTransform toLatLongTransform( mStrataLayer->crs(), QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) ); - - //init random number generator - mt_srand( QTime::currentTime().msec() ); - - QgsFeatureRequest fr; - fr.setSubsetOfAttributes( QStringList() << mStrataIdAttribute << mMinDistanceAttribute << mNPointsAttribute, mStrataLayer->fields() ); - QgsFeatureIterator strataIt = mStrataLayer->getFeatures( fr ); - - QgsFeature fet; - int nTotalTransects = 0; - int nFeatures = 0; - int totalFeatures = 0; - - if ( feedback ) - { - totalFeatures = mStrataLayer->featureCount(); - } - - while ( strataIt.nextFeature( fet ) ) - { - if ( feedback ) - { - feedback->setProgress( 100.0 * static_cast< double >( nFeatures ) / totalFeatures ); - } - if ( feedback && feedback->isCanceled() ) - { - break; - } - - if ( !fet.hasGeometry() ) - { - continue; - } - QgsGeometry strataGeom = fet.geometry(); - - //find baseline for strata - QVariant strataId = fet.attribute( mStrataIdAttribute ); - QgsGeometry baselineGeom = findBaselineGeometry( strataId.isValid() ? strataId : -1 ); - if ( baselineGeom.isNull() ) - { - continue; - } - - double minDistance = fet.attribute( mMinDistanceAttribute ).toDouble(); - double minDistanceLayerUnits = minDistance; - //if minDistance is in meters and the data in degrees, we need to apply a rough conversion for the buffer distance - double bufferDist = bufferDistance( minDistance ); - if ( mMinDistanceUnits == Meters && mStrataLayer->crs().mapUnits() == QgsUnitTypes::DistanceDegrees ) - { - minDistanceLayerUnits = minDistance / 111319.9; - } - - QgsGeometry clippedBaseline = strataGeom.intersection( baselineGeom ); - if ( !clippedBaseline || clippedBaseline.wkbType() == QgsWkbTypes::Unknown ) - { - continue; - } - QgsGeometry *bufferLineClipped = clipBufferLine( strataGeom, &clippedBaseline, bufferDist ); - if ( !bufferLineClipped ) - { - continue; - } - - //save clipped baseline to file - QgsFeature blFeature( usedBaselineFields ); - blFeature.setGeometry( clippedBaseline ); - blFeature.setAttribute( QStringLiteral( "stratum_id" ), strataId ); - blFeature.setAttribute( QStringLiteral( "ok" ), "f" ); - usedBaselineWriter.addFeature( blFeature ); - - //start loop to create random points along the baseline - int nTransects = fet.attribute( mNPointsAttribute ).toInt(); - int nCreatedTransects = 0; - int nIterations = 0; - int nMaxIterations = nTransects * 50; - - QgsSpatialIndex sIndex; //to check minimum distance - QMap< QgsFeatureId, QgsGeometry > lineFeatureMap; - - while ( nCreatedTransects < nTransects && nIterations < nMaxIterations ) - { - double randomPosition = ( ( double )mt_rand() / MD_RAND_MAX ) * clippedBaseline.length(); - QgsGeometry samplePoint = clippedBaseline.interpolate( randomPosition ); - ++nIterations; - if ( samplePoint.isNull() ) - { - continue; - } - QgsPointXY sampleQgsPointXY = samplePoint.asPoint(); - QgsPointXY latLongSamplePoint = toLatLongTransform.transform( sampleQgsPointXY ); - - QgsFeature samplePointFeature( outputPointFields ); - samplePointFeature.setGeometry( samplePoint ); - samplePointFeature.setAttribute( QStringLiteral( "id" ), nTotalTransects + 1 ); - samplePointFeature.setAttribute( QStringLiteral( "station_id" ), nCreatedTransects + 1 ); - samplePointFeature.setAttribute( QStringLiteral( "stratum_id" ), strataId ); - samplePointFeature.setAttribute( QStringLiteral( "station_code" ), strataId.toString() + '_' + QString::number( nCreatedTransects + 1 ) ); - samplePointFeature.setAttribute( QStringLiteral( "start_lat" ), latLongSamplePoint.y() ); - samplePointFeature.setAttribute( QStringLiteral( "start_long" ), latLongSamplePoint.x() ); - - //find closest point on clipped buffer line - QgsPointXY minDistPoint; - - int afterVertex; - if ( bufferLineClipped->closestSegmentWithContext( sampleQgsPointXY, minDistPoint, afterVertex ) < 0 ) - { - continue; - } - - //bearing between sample point and min dist point (transect direction) - double bearing = distanceArea.bearing( sampleQgsPointXY, minDistPoint ) / M_PI * 180.0; - - QgsPointXY ptFarAway( sampleQgsPointXY.x() + ( minDistPoint.x() - sampleQgsPointXY.x() ) * 1000000, - sampleQgsPointXY.y() + ( minDistPoint.y() - sampleQgsPointXY.y() ) * 1000000 ); - QgsPolyline lineFarAway; - lineFarAway << sampleQgsPointXY << ptFarAway; - QgsGeometry lineFarAwayGeom = QgsGeometry::fromPolyline( lineFarAway ); - QgsGeometry lineClipStratum = lineFarAwayGeom.intersection( strataGeom ); - if ( lineClipStratum.isNull() ) - { - continue; - } - - //cancel if distance between sample point and line is too large (line does not start at point) - if ( lineClipStratum.distance( samplePoint ) > 0.000001 ) - { - continue; - } - - //if lineClipStratum is a multiline, take the part line closest to sampleQgsPoint - if ( lineClipStratum.wkbType() == QgsWkbTypes::MultiLineString - || lineClipStratum.wkbType() == QgsWkbTypes::MultiLineString25D ) - { - QgsGeometry singleLine = closestMultilineElement( sampleQgsPointXY, lineClipStratum ); - if ( !singleLine.isNull() ) - { - lineClipStratum = singleLine; - } - } - - //cancel if length of lineClipStratum is too small - double transectLength = distanceArea.measureLength( lineClipStratum ); - if ( transectLength < mMinTransectLength ) - { - continue; - } - - //search closest existing profile. Cancel if dist < minDist - if ( otherTransectWithinDistance( lineClipStratum, minDistanceLayerUnits, minDistance, sIndex, lineFeatureMap, distanceArea ) ) - { - continue; - } - - QgsFeatureId fid( nCreatedTransects ); - QgsFeature sampleLineFeature( outputPointFields, fid ); - sampleLineFeature.setGeometry( lineClipStratum ); - sampleLineFeature.setAttribute( QStringLiteral( "id" ), nTotalTransects + 1 ); - sampleLineFeature.setAttribute( QStringLiteral( "station_id" ), nCreatedTransects + 1 ); - sampleLineFeature.setAttribute( QStringLiteral( "stratum_id" ), strataId ); - sampleLineFeature.setAttribute( QStringLiteral( "station_code" ), strataId.toString() + '_' + QString::number( nCreatedTransects + 1 ) ); - sampleLineFeature.setAttribute( QStringLiteral( "start_lat" ), latLongSamplePoint.y() ); - sampleLineFeature.setAttribute( QStringLiteral( "start_long" ), latLongSamplePoint.x() ); - sampleLineFeature.setAttribute( QStringLiteral( "bearing" ), bearing ); - outputLineWriter.addFeature( sampleLineFeature ); - - //add point to file writer here. - //It can only be written if the corresponding transect has been as well - outputPointWriter.addFeature( samplePointFeature ); - - sIndex.insertFeature( sampleLineFeature ); - lineFeatureMap.insert( fid, lineClipStratum ); - - ++nTotalTransects; - ++nCreatedTransects; - } - - QgsFeature bufferClipFeature; - bufferClipFeature.setGeometry( *bufferLineClipped ); - delete bufferLineClipped; - bufferClipFeature.setAttribute( QStringLiteral( "id" ), strataId ); - bufferClipLineWriter.addFeature( bufferClipFeature ); - //delete bufferLineClipped; - - ++nFeatures; - } - - if ( feedback ) - { - feedback->setProgress( 100.0 ); - } - - return 0; -} - -QgsGeometry QgsTransectSample::findBaselineGeometry( const QVariant &strataId ) -{ - if ( !mBaselineLayer ) - { - return QgsGeometry(); - } - - QgsFeatureIterator baseLineIt = mBaselineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QStringList( mBaselineStrataId ), mBaselineLayer->fields() ) ); - QgsFeature fet; - - while ( baseLineIt.nextFeature( fet ) ) //todo: cache this in case there are many baslines - { - if ( strataId == fet.attribute( mBaselineStrataId ) || mShareBaseline ) - { - return fet.geometry(); - } - } - return QgsGeometry(); -} - -bool QgsTransectSample::otherTransectWithinDistance( const QgsGeometry &geom, double minDistLayerUnit, double minDistance, QgsSpatialIndex &sIndex, - const QMap< QgsFeatureId, QgsGeometry > &lineFeatureMap, QgsDistanceArea &da ) -{ - if ( geom.isNull() ) - { - return false; - } - - QgsGeometry buffer = geom.buffer( minDistLayerUnit, 8 ); - if ( buffer.isNull() ) - { - return false; - } - QgsRectangle rect = buffer.boundingBox(); - QList lineIdList = sIndex.intersects( rect ); - - QList::const_iterator lineIdIt = lineIdList.constBegin(); - for ( ; lineIdIt != lineIdList.constEnd(); ++lineIdIt ) - { - const QMap< QgsFeatureId, QgsGeometry >::const_iterator idMapIt = lineFeatureMap.find( *lineIdIt ); - if ( idMapIt != lineFeatureMap.constEnd() ) - { - double dist = 0; - QgsPointXY pt1, pt2; - closestSegmentPoints( geom, idMapIt.value(), dist, pt1, pt2 ); - dist = da.measureLine( pt1, pt2 ); //convert degrees to meters if necessary - - if ( dist < minDistance ) - { - return true; - } - } - } - - return false; -} - -bool QgsTransectSample::closestSegmentPoints( const QgsGeometry &g1, const QgsGeometry &g2, double &dist, QgsPointXY &pt1, QgsPointXY &pt2 ) -{ - QgsWkbTypes::Type t1 = g1.wkbType(); - if ( t1 != QgsWkbTypes::LineString && t1 != QgsWkbTypes::LineString25D ) - { - return false; - } - - QgsWkbTypes::Type t2 = g2.wkbType(); - if ( t2 != QgsWkbTypes::LineString && t2 != QgsWkbTypes::LineString25D ) - { - return false; - } - - QgsPolyline pl1 = g1.asPolyline(); - QgsPolyline pl2 = g2.asPolyline(); - - if ( pl1.size() < 2 || pl2.size() < 2 ) - { - return false; - } - - QgsPointXY p11 = pl1.at( 0 ); - QgsPointXY p12 = pl1.at( 1 ); - QgsPointXY p21 = pl2.at( 0 ); - QgsPointXY p22 = pl2.at( 1 ); - - double p1x = p11.x(); - double p1y = p11.y(); - double v1x = p12.x() - p11.x(); - double v1y = p12.y() - p11.y(); - double p2x = p21.x(); - double p2y = p21.y(); - double v2x = p22.x() - p21.x(); - double v2y = p22.y() - p21.y(); - - double denominatorU = v2x * v1y - v2y * v1x; - double denominatorT = v1x * v2y - v1y * v2x; - - if ( qgsDoubleNear( denominatorU, 0 ) || qgsDoubleNear( denominatorT, 0 ) ) - { - //lines are parallel - //project all points on the other segment and take the one with the smallest distance - QgsPointXY minDistPoint1; - double d1 = p11.sqrDistToSegment( p21.x(), p21.y(), p22.x(), p22.y(), minDistPoint1 ); - QgsPointXY minDistPoint2; - double d2 = p12.sqrDistToSegment( p21.x(), p21.y(), p22.x(), p22.y(), minDistPoint2 ); - QgsPointXY minDistPoint3; - double d3 = p21.sqrDistToSegment( p11.x(), p11.y(), p12.x(), p12.y(), minDistPoint3 ); - QgsPointXY minDistPoint4; - double d4 = p22.sqrDistToSegment( p11.x(), p11.y(), p12.x(), p12.y(), minDistPoint4 ); - - if ( d1 <= d2 && d1 <= d3 && d1 <= d4 ) - { - dist = std::sqrt( d1 ); - pt1 = p11; - pt2 = minDistPoint1; - return true; - } - else if ( d2 <= d1 && d2 <= d3 && d2 <= d4 ) - { - dist = std::sqrt( d2 ); - pt1 = p12; - pt2 = minDistPoint2; - return true; - } - else if ( d3 <= d1 && d3 <= d2 && d3 <= d4 ) - { - dist = std::sqrt( d3 ); - pt1 = p21; - pt2 = minDistPoint3; - return true; - } - else - { - dist = std::sqrt( d4 ); - pt1 = p21; - pt2 = minDistPoint4; - return true; - } - } - - double u = ( p1x * v1y - p1y * v1x - p2x * v1y + p2y * v1x ) / denominatorU; - double t = ( p2x * v2y - p2y * v2x - p1x * v2y + p1y * v2x ) / denominatorT; - - if ( u >= 0 && u <= 1.0 && t >= 0 && t <= 1.0 ) - { - dist = 0; - pt1.setX( p2x + u * v2x ); - pt1.setY( p2y + u * v2y ); - pt2 = pt1; - dist = 0; - return true; - } - - if ( t > 1.0 ) - { - pt1.setX( p12.x() ); - pt1.setY( p12.y() ); - } - else if ( t < 0.0 ) - { - pt1.setX( p11.x() ); - pt1.setY( p11.y() ); - } - if ( u > 1.0 ) - { - pt2.setX( p22.x() ); - pt2.setY( p22.y() ); - } - if ( u < 0.0 ) - { - pt2.setX( p21.x() ); - pt2.setY( p21.y() ); - } - if ( t >= 0.0 && t <= 1.0 ) - { - //project pt2 onto g1 - pt2.sqrDistToSegment( p11.x(), p11.y(), p12.x(), p12.y(), pt1 ); - } - if ( u >= 0.0 && u <= 1.0 ) - { - //project pt1 onto g2 - pt1.sqrDistToSegment( p21.x(), p21.y(), p22.x(), p22.y(), pt2 ); - } - - dist = std::sqrt( pt1.sqrDist( pt2 ) ); - return true; -} - -QgsGeometry QgsTransectSample::closestMultilineElement( const QgsPointXY &pt, const QgsGeometry &multiLine ) -{ - if ( !multiLine || ( multiLine.wkbType() != QgsWkbTypes::MultiLineString - && multiLine.wkbType() != QgsWkbTypes::MultiLineString25D ) ) - { - return QgsGeometry(); - } - - double minDist = DBL_MAX; - double currentDist = 0; - QgsGeometry currentLine; - QgsGeometry closestLine; - QgsGeometry pointGeom = QgsGeometry::fromPoint( pt ); - - QgsMultiPolyline multiPolyline = multiLine.asMultiPolyline(); - QgsMultiPolyline::const_iterator it = multiPolyline.constBegin(); - for ( ; it != multiPolyline.constEnd(); ++it ) - { - currentLine = QgsGeometry::fromPolyline( *it ); - currentDist = pointGeom.distance( currentLine ); - if ( currentDist < minDist ) - { - minDist = currentDist; - closestLine = currentLine; - } - } - - return closestLine; -} - -QgsGeometry *QgsTransectSample::clipBufferLine( const QgsGeometry &stratumGeom, QgsGeometry *clippedBaseline, double tolerance ) -{ - if ( !stratumGeom || !clippedBaseline || clippedBaseline->wkbType() == QgsWkbTypes::Unknown ) - { - return nullptr; - } - - QgsGeometry usedBaseline = *clippedBaseline; - if ( mBaselineSimplificationTolerance >= 0 ) - { - //int verticesBefore = usedBaseline->asMultiPolyline().count(); - usedBaseline = clippedBaseline->simplify( mBaselineSimplificationTolerance ); - if ( usedBaseline.isNull() ) - { - return nullptr; - } - //int verticesAfter = usedBaseline->asMultiPolyline().count(); - - //debug: write to file - /*QgsVectorFileWriter debugWriter( "/tmp/debug.shp", "utf-8", QgsFields(), QgsWkbTypes::LineString, &( mStrataLayer->crs() ) ); - QgsFeature debugFeature; debugFeature.setGeometry( usedBaseline ); - debugWriter.addFeature( debugFeature );*/ - } - - double currentBufferDist = tolerance; - int maxLoops = 10; - - for ( int i = 0; i < maxLoops; ++i ) - { - //loop with tolerance: create buffer, convert buffer to line, clip line by stratum, test if result is (single) line - QgsGeometry clipBaselineBuffer = usedBaseline.buffer( currentBufferDist, 8 ); - if ( clipBaselineBuffer.isNull() ) - { - continue; - } - - //it is also possible that clipBaselineBuffer is a multipolygon - QgsGeometry bufferLine; //buffer line or multiline - QgsGeometry bufferLineClipped; - QgsMultiPolyline mpl; - if ( clipBaselineBuffer.isMultipart() ) - { - QgsMultiPolygon bufferMultiPolygon = clipBaselineBuffer.asMultiPolygon(); - if ( bufferMultiPolygon.size() < 1 ) - { - continue; - } - - for ( int j = 0; j < bufferMultiPolygon.size(); ++j ) - { - int size = bufferMultiPolygon.at( j ).size(); - for ( int k = 0; k < size; ++k ) - { - mpl.append( bufferMultiPolygon.at( j ).at( k ) ); - } - } - bufferLine = QgsGeometry::fromMultiPolyline( mpl ); - } - else - { - QgsPolygon bufferPolygon = clipBaselineBuffer.asPolygon(); - if ( bufferPolygon.size() < 1 ) - { - continue; - } - - int size = bufferPolygon.size(); - mpl.reserve( size ); - for ( int j = 0; j < size; ++j ) - { - mpl.append( bufferPolygon[j] ); - } - bufferLine = QgsGeometry::fromMultiPolyline( mpl ); - } - bufferLineClipped = bufferLine.intersection( stratumGeom ); - - if ( bufferLineClipped.isNull() && bufferLineClipped.type() == QgsWkbTypes::LineGeometry ) - { - //if stratumGeom is a multipolygon, bufferLineClipped must intersect each part - bool bufferLineClippedIntersectsStratum = true; - if ( stratumGeom.wkbType() == QgsWkbTypes::MultiPolygon || stratumGeom.wkbType() == QgsWkbTypes::MultiPolygon25D ) - { - QVector multiPoly = stratumGeom.asMultiPolygon(); - QVector::const_iterator multiIt = multiPoly.constBegin(); - for ( ; multiIt != multiPoly.constEnd(); ++multiIt ) - { - QgsGeometry poly = QgsGeometry::fromPolygon( *multiIt ); - if ( !poly.intersects( bufferLineClipped ) ) - { - bufferLineClippedIntersectsStratum = false; - break; - } - } - } - - if ( bufferLineClippedIntersectsStratum ) - { - return new QgsGeometry( bufferLineClipped ); - } - } - - currentBufferDist /= 2; - } - - return nullptr; //no solution found even with reduced tolerances -} - -double QgsTransectSample::bufferDistance( double minDistanceFromAttribute ) const -{ - double bufferDist = minDistanceFromAttribute; - if ( mBaselineBufferDistance >= 0 ) - { - bufferDist = mBaselineBufferDistance; - } - - if ( mMinDistanceUnits == Meters && mStrataLayer->crs().mapUnits() == QgsUnitTypes::DistanceDegrees ) - { - bufferDist /= 111319.9; - } - - return bufferDist; -} diff --git a/src/analysis/vector/qgstransectsample.h b/src/analysis/vector/qgstransectsample.h deleted file mode 100644 index 153895582ac..00000000000 --- a/src/analysis/vector/qgstransectsample.h +++ /dev/null @@ -1,106 +0,0 @@ -/*************************************************************************** - qgstransectsample.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 QGSTRANSECTSAMPLE_H -#define QGSTRANSECTSAMPLE_H - -#include "qgsfeature.h" -#include -#include -#include "qgis_analysis.h" - -class QgsDistanceArea; -class QgsGeometry; -class QgsSpatialIndex; -class QgsVectorLayer; -class QgsPointXY; -class QgsFeedback; - -/** \ingroup analysis - * A class for the creation of transect sample lines based on a set of strata polygons and baselines*/ -class ANALYSIS_EXPORT QgsTransectSample -{ - public: - - enum DistanceUnits - { - Meters, - StrataUnits //units are the same as stratum layer - }; - - QgsTransectSample( QgsVectorLayer *strataLayer, const QString &strataIdAttribute, const QString &minDistanceAttribute, const QString &nPointsAttribute, - DistanceUnits minDistUnits, QgsVectorLayer *baselineLayer, bool shareBaseline, - const QString &baselineStrataId, const QString &outputPointLayer, const QString &outputLineLayer, const QString &usedBaselineLayer, double minTransectLength = 0.0, - double baselineBufferDistance = -1.0, double baselineSimplificationTolerance = -1.0 ); - - /** - * Creates the sample. - * - * The optional \a feedback argument can be used for progress reporting and cancelation support. - */ - int createSample( QgsFeedback *feedback = nullptr ); - - private: - QgsTransectSample(); //default constructor forbidden - - QgsGeometry findBaselineGeometry( const QVariant &strataId ); - - //! Returns true if another transect is within the specified minimum distance - static bool otherTransectWithinDistance( const QgsGeometry &geom, double minDistLayerUnit, double minDistance, QgsSpatialIndex &sIndex, const QMap &lineFeatureMap, QgsDistanceArea &da ); - - QgsVectorLayer *mStrataLayer = nullptr; - QString mStrataIdAttribute; - QString mMinDistanceAttribute; - QString mNPointsAttribute; - - QgsVectorLayer *mBaselineLayer = nullptr; - bool mShareBaseline; - QString mBaselineStrataId; - - QString mOutputPointLayer; - QString mOutputLineLayer; - QString mUsedBaselineLayer; - - DistanceUnits mMinDistanceUnits; - - double mMinTransectLength; - - //! If value is negative, the buffer distance ist set to the same value as the minimum distance - double mBaselineBufferDistance; - //! If value is negative, no simplification is done to the baseline prior to create the buffer - double mBaselineSimplificationTolerance; - - /** Finds the closest points between two line segments - \param g1 first input geometry. Must be a linestring with two vertices - \param g2 second input geometry. Must be a linestring with two vertices - \param dist out: distance between the segments - \param pt1 out: closest point on first geometry - \param pt2 out: closest point on secont geometry - \returns true in case of success*/ - static bool closestSegmentPoints( const QgsGeometry &g1, const QgsGeometry &g2, double &dist, QgsPointXY &pt1, QgsPointXY &pt2 ); - //! Returns a copy of the multiline element closest to a point (caller takes ownership) - static QgsGeometry closestMultilineElement( const QgsPointXY &pt, const QgsGeometry &multiLine ); - - /** Returns clipped buffer line. Iteratively applies reduced tolerances if the result is not a single line - \param stratumGeom stratum polygon - \param clippedBaseline base line geometry clipped to the stratum - \param tolerance buffer distance (in layer units) - \returns clipped buffer line or 0 in case of error*/ - QgsGeometry *clipBufferLine( const QgsGeometry &stratumGeom, QgsGeometry *clippedBaseline, double tolerance ); - - //! Returns distance to buffer the baseline (takes care of units and buffer settings - double bufferDistance( double minDistanceFromAttribute ) const; -}; - -#endif // QGSTRANSECTSAMPLE_H From 2286710606e212dc60f70d63e234f7edb2165198 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 10:32:08 +1000 Subject: [PATCH 284/364] Skip some task manager tests on Travis only Instead of #ifdefing them out, only prevent them running on Travis so that they still run locally. Also skip another task manager test which randomly fails only on Travis --- src/test/qgstest.h | 13 +++++++++ tests/src/core/testqgstaskmanager.cpp | 42 ++++++++++++--------------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/test/qgstest.h b/src/test/qgstest.h index e7382496490..3e692b175e6 100644 --- a/src/test/qgstest.h +++ b/src/test/qgstest.h @@ -34,5 +34,18 @@ return QTest::qExec(&tc, argc, argv); \ } +/** + * QGIS unit test utilities. + * \since QGIS 3.0 + */ +namespace QgsTest +{ + + //! Returns true if test is running on Travis infrastructure + bool isTravis() + { + return qgetenv( "TRAVIS" ) == QStringLiteral( "true" ); + } +} #endif // QGSTEST_H diff --git a/tests/src/core/testqgstaskmanager.cpp b/tests/src/core/testqgstaskmanager.cpp index d914413c47e..dc9593515d3 100644 --- a/tests/src/core/testqgstaskmanager.cpp +++ b/tests/src/core/testqgstaskmanager.cpp @@ -22,12 +22,6 @@ #include #include "qgstest.h" -// some of these tests have intermittent failure on Travis, probably due -// to inconsistent availability to multiple threads on the platform. -// These tests are disabled unless WITH_FLAKY is 1. - -#define WITH_FLAKY 0 //TODO - disable only for Travis? - class TestTask : public QgsTask { Q_OBJECT @@ -200,15 +194,11 @@ class TestQgsTaskManager : public QObject void task(); void taskResult(); void taskFinished(); -#if WITH_FLAKY void subTask(); -#endif void addTask(); void taskTerminationBeforeDelete(); void taskId(); -#if WITH_FLAKY void waitForFinished(); -#endif void progressChanged(); void statusChanged(); void allTasksFinished(); @@ -442,9 +432,11 @@ void TestQgsTaskManager::taskFinished() QCOMPARE( *resultObtained, false ); } -#if WITH_FLAKY void TestQgsTaskManager::subTask() { + if ( QgsTest::isTravis() ) + QSKIP( "This test is disabled on Travis CI environment" ); + QgsTaskManager manager; // parent with one subtask @@ -489,14 +481,14 @@ void TestQgsTaskManager::subTask() // test progress calculation QSignalSpy spy( parent, &QgsTask::progressChanged ); parent->emitProgressChanged( 50 ); - QCOMPARE( std::round( parent->progress() ), 17 ); + QCOMPARE( std::round( parent->progress() ), 17.0 ); //QCOMPARE( spy.count(), 1 ); - QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 17 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 17.0 ); subTask->emitProgressChanged( 100 ); - QCOMPARE( std::round( parent->progress() ), 50 ); + QCOMPARE( std::round( parent->progress() ), 50.0 ); //QCOMPARE( spy.count(), 2 ); - QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 50 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 50.0 ); subTask2->finish(); while ( subTask2->status() != QgsTask::Complete ) @@ -504,14 +496,14 @@ void TestQgsTaskManager::subTask() QCoreApplication::processEvents(); } flushEvents(); - QCOMPARE( std::round( parent->progress() ), 83 ); + QCOMPARE( std::round( parent->progress() ), 83.0 ); //QCOMPARE( spy.count(), 3 ); - QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 83 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 83.0 ); parent->emitProgressChanged( 100 ); - QCOMPARE( std::round( parent->progress() ), 100 ); + QCOMPARE( std::round( parent->progress() ), 100.0 ); //QCOMPARE( spy.count(), 4 ); - QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 100 ); + QCOMPARE( std::round( spy.last().at( 0 ).toDouble() ), 100.0 ); parent->terminate(); subTask->terminate(); @@ -641,7 +633,6 @@ void TestQgsTaskManager::subTask() flushEvents(); QVERIFY( parentFinished2.count() > 0 ); } -#endif void TestQgsTaskManager::taskId() { @@ -665,9 +656,11 @@ void TestQgsTaskManager::taskId() delete task3; } -#if WITH_FLAKY void TestQgsTaskManager::waitForFinished() { + if ( QgsTest::isTravis() ) + QSKIP( "This test is disabled on Travis CI environment" ); + QgsTaskManager manager; QEventLoop loop; @@ -705,7 +698,6 @@ void TestQgsTaskManager::waitForFinished() QCOMPARE( timeoutTooShortTask->waitForFinished( 20 ), false ); QCOMPARE( timeoutTooShortTask->status(), QgsTask::Running ); } -#endif void TestQgsTaskManager::progressChanged() { @@ -1105,6 +1097,9 @@ void TestQgsTaskManager::layerDependencies() void TestQgsTaskManager::managerWithSubTasks() { + if ( QgsTest::isTravis() ) + QSKIP( "This test is disabled on Travis CI environment" ); + // parent with subtasks ProgressReportingTask *parent = new ProgressReportingTask( "parent" ); ProgressReportingTask *subTask = new ProgressReportingTask( "subtask" ); @@ -1125,7 +1120,7 @@ void TestQgsTaskManager::managerWithSubTasks() QCOMPARE( manager->activeTasks().count(), 1 ); QVERIFY( manager->activeTasks().contains( parent ) ); QCOMPARE( spy.count(), 1 ); -#if WITH_FLAKY + //manager should not directly listen to progress reports from subtasks //(only parent tasks, which themselves include their subtask progress) QCOMPARE( spyProgress.count(), 0 ); @@ -1146,7 +1141,6 @@ void TestQgsTaskManager::managerWithSubTasks() QCOMPARE( spyProgress.count(), 3 ); QCOMPARE( spyProgress.last().at( 0 ).toLongLong(), 0LL ); QCOMPARE( spyProgress.last().at( 1 ).toInt(), 63 ); -#endif //manager should not emit statusChanged signals for subtasks QSignalSpy statusSpy( manager, &QgsTaskManager::statusChanged ); From 700f9b2ef856418199a73b564ef617b916f5f22a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 10:38:18 +1000 Subject: [PATCH 285/364] Condense qgstestutils.h and qgstest.h into a single file --- src/core/CMakeLists.txt | 1 - src/core/qgstestutils.h | 63 ------------------- src/test/qgstest.h | 36 +++++++++++ tests/src/analysis/testqgsalignraster.cpp | 1 - .../src/analysis/testqgsrastercalculator.cpp | 1 - tests/src/app/testqgsattributetable.cpp | 1 - tests/src/app/testqgsfieldcalculator.cpp | 1 - .../src/app/testqgsmaptoolidentifyaction.cpp | 1 - tests/src/app/testqgsmeasuretool.cpp | 1 - tests/src/core/testqgscomposermap.cpp | 1 - tests/src/core/testqgscomposerutils.cpp | 1 - tests/src/core/testqgscomposition.cpp | 1 - tests/src/core/testqgscoordinatetransform.cpp | 1 - tests/src/core/testqgscurve.cpp | 1 - tests/src/core/testqgsdistancearea.cpp | 1 - tests/src/core/testqgsexpression.cpp | 1 - tests/src/core/testqgsgeometry.cpp | 1 - tests/src/core/testqgsgeometryimport.cpp | 1 - tests/src/core/testqgsgeometryutils.cpp | 1 - tests/src/core/testqgshistogram.cpp | 1 - tests/src/core/testqgslayout.cpp | 1 - tests/src/core/testqgslayoutitem.cpp | 1 - tests/src/core/testqgslayoutunits.cpp | 1 - tests/src/core/testqgslayoututils.cpp | 1 - tests/src/core/testqgsmaptopixel.cpp | 1 - tests/src/core/testqgspoint.cpp | 1 - tests/src/core/testqgsprocessing.cpp | 1 - tests/src/core/testqgsproperty.cpp | 1 - tests/src/core/testqgsrasterlayer.cpp | 1 - tests/src/core/testqgsrastersublayer.cpp | 1 - tests/src/core/testqgsstatisticalsummary.cpp | 1 - tests/src/core/testqgstracer.cpp | 1 - tests/src/gui/testprojectionissues.cpp | 1 - tests/src/gui/testqgslayoutview.cpp | 1 - tests/src/gui/testqgsmapcanvas.cpp | 1 - tests/src/providers/testqgsgdalprovider.cpp | 1 - 36 files changed, 36 insertions(+), 97 deletions(-) delete mode 100644 src/core/qgstestutils.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c1c3e45863d..8f424071d36 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -868,7 +868,6 @@ SET(QGIS_CORE_HDRS qgsstatisticalsummary.h qgsstringstatisticalsummary.h qgsstringutils.h - qgstestutils.h qgstextlabelfeature.h qgstextrenderer.h qgstextrenderer_p.h diff --git a/src/core/qgstestutils.h b/src/core/qgstestutils.h deleted file mode 100644 index c81ccd4d23f..00000000000 --- a/src/core/qgstestutils.h +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************** - qgstestutils.h - --------------------- - begin : June 2016 - copyright : (C) 2016 by Nyall Dawson - email : nyalld dot dawson at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -#ifndef QGSTESTUTILS_H -#define QGSTESTUTILS_H - -#define SIP_NO_FILE - -#include "qgis.h" -#include "QtTest/qtestcase.h" - -/** \ingroup core - * Assorted helper methods for unit testing. - * \since QGIS 2.16 - */ - -#define QGSCOMPARENEAR(value,expected,epsilon) { \ - bool _xxxresult = qgsDoubleNear( value, expected, epsilon ); \ - if ( !_xxxresult ) \ - { \ - qDebug( "Expecting %f got %f (diff %f > %f)", static_cast< double >( expected ), static_cast< double >( value ), std::fabs( static_cast< double >( expected ) - value ), static_cast< double >( epsilon ) ); \ - } \ - QVERIFY( qgsDoubleNear( value, expected, epsilon ) ); \ - } - -#define QGSCOMPARENOTNEAR(value,not_expected,epsilon) { \ - bool _xxxresult = qgsDoubleNear( value, not_expected, epsilon ); \ - if ( _xxxresult ) \ - { \ - qDebug( "Expecting %f to be differerent from %f (diff %f > %f)", static_cast< double >( value ), static_cast< double >( not_expected ), std::fabs( static_cast< double >( not_expected ) - value ), static_cast< double >( epsilon ) ); \ - } \ - QVERIFY( !qgsDoubleNear( value, not_expected, epsilon ) ); \ - } - -#define QGSCOMPARENEARPOINT(point1,point2,epsilon) { \ - QGSCOMPARENEAR( point1.x(), point2.x(), epsilon ); \ - QGSCOMPARENEAR( point1.y(), point2.y(), epsilon ); \ - } - -#define QGSCOMPARENEARRECTANGLE(rectangle1,rectangle2,epsilon) { \ - QGSCOMPARENEAR( rectangle1.xMinimum(), rectangle2.xMinimum(), epsilon ); \ - QGSCOMPARENEAR( rectangle1.xMaximum(), rectangle2.xMaximum(), epsilon ); \ - QGSCOMPARENEAR( rectangle1.yMinimum(), rectangle2.yMinimum(), epsilon ); \ - QGSCOMPARENEAR( rectangle1.yMaximum(), rectangle2.yMaximum(), epsilon ); \ - } - -//sometimes GML attributes are in a different order - but that's ok -#define QGSCOMPAREGML(result,expected) { \ - QCOMPARE( result.replace( QStringLiteral("ts=\" \" cs=\",\""), QStringLiteral("cs=\",\" ts=\" \"") ), expected ); \ - } - -#endif // QGSTESTUTILS_H diff --git a/src/test/qgstest.h b/src/test/qgstest.h index 3e692b175e6..db5b8dee9da 100644 --- a/src/test/qgstest.h +++ b/src/test/qgstest.h @@ -34,6 +34,42 @@ return QTest::qExec(&tc, argc, argv); \ } + +#define QGSCOMPARENEAR(value,expected,epsilon) { \ + bool _xxxresult = qgsDoubleNear( value, expected, epsilon ); \ + if ( !_xxxresult ) \ + { \ + qDebug( "Expecting %f got %f (diff %f > %f)", static_cast< double >( expected ), static_cast< double >( value ), std::fabs( static_cast< double >( expected ) - value ), static_cast< double >( epsilon ) ); \ + } \ + QVERIFY( qgsDoubleNear( value, expected, epsilon ) ); \ + } + +#define QGSCOMPARENOTNEAR(value,not_expected,epsilon) { \ + bool _xxxresult = qgsDoubleNear( value, not_expected, epsilon ); \ + if ( _xxxresult ) \ + { \ + qDebug( "Expecting %f to be differerent from %f (diff %f > %f)", static_cast< double >( value ), static_cast< double >( not_expected ), std::fabs( static_cast< double >( not_expected ) - value ), static_cast< double >( epsilon ) ); \ + } \ + QVERIFY( !qgsDoubleNear( value, not_expected, epsilon ) ); \ + } + +#define QGSCOMPARENEARPOINT(point1,point2,epsilon) { \ + QGSCOMPARENEAR( point1.x(), point2.x(), epsilon ); \ + QGSCOMPARENEAR( point1.y(), point2.y(), epsilon ); \ + } + +#define QGSCOMPARENEARRECTANGLE(rectangle1,rectangle2,epsilon) { \ + QGSCOMPARENEAR( rectangle1.xMinimum(), rectangle2.xMinimum(), epsilon ); \ + QGSCOMPARENEAR( rectangle1.xMaximum(), rectangle2.xMaximum(), epsilon ); \ + QGSCOMPARENEAR( rectangle1.yMinimum(), rectangle2.yMinimum(), epsilon ); \ + QGSCOMPARENEAR( rectangle1.yMaximum(), rectangle2.yMaximum(), epsilon ); \ + } + +//sometimes GML attributes are in a different order - but that's ok +#define QGSCOMPAREGML(result,expected) { \ + QCOMPARE( result.replace( QStringLiteral("ts=\" \" cs=\",\""), QStringLiteral("cs=\",\" ts=\" \"") ), expected ); \ + } + /** * QGIS unit test utilities. * \since QGIS 3.0 diff --git a/tests/src/analysis/testqgsalignraster.cpp b/tests/src/analysis/testqgsalignraster.cpp index a10b9cea88a..3aa0f46a4fd 100644 --- a/tests/src/analysis/testqgsalignraster.cpp +++ b/tests/src/analysis/testqgsalignraster.cpp @@ -19,7 +19,6 @@ #include "qgsapplication.h" #include "qgscoordinatereferencesystem.h" #include "qgsrectangle.h" -#include "qgstestutils.h" #include diff --git a/tests/src/analysis/testqgsrastercalculator.cpp b/tests/src/analysis/testqgsrastercalculator.cpp index 030775af9ee..8a02de038ef 100644 --- a/tests/src/analysis/testqgsrastercalculator.cpp +++ b/tests/src/analysis/testqgsrastercalculator.cpp @@ -21,7 +21,6 @@ Email : nyall dot dawson at gmail dot com #include "qgsrastermatrix.h" #include "qgsapplication.h" #include "qgsproject.h" -#include "qgstestutils.h" Q_DECLARE_METATYPE( QgsRasterCalcNode::Operator ) diff --git a/tests/src/app/testqgsattributetable.cpp b/tests/src/app/testqgsattributetable.cpp index fffe70c0b62..14214d63243 100644 --- a/tests/src/app/testqgsattributetable.cpp +++ b/tests/src/app/testqgsattributetable.cpp @@ -28,7 +28,6 @@ #include "qgsvectorfilewriter.h" #include "qgstest.h" -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the attribute table dialog diff --git a/tests/src/app/testqgsfieldcalculator.cpp b/tests/src/app/testqgsfieldcalculator.cpp index 697e34eb56c..0c48f82166b 100644 --- a/tests/src/app/testqgsfieldcalculator.cpp +++ b/tests/src/app/testqgsfieldcalculator.cpp @@ -24,7 +24,6 @@ #include "qgsproject.h" #include "qgsmapcanvas.h" #include "qgsunittypes.h" -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the field calculator diff --git a/tests/src/app/testqgsmaptoolidentifyaction.cpp b/tests/src/app/testqgsmaptoolidentifyaction.cpp index 77bd17a66b6..cf252f9570d 100644 --- a/tests/src/app/testqgsmaptoolidentifyaction.cpp +++ b/tests/src/app/testqgsmaptoolidentifyaction.cpp @@ -26,7 +26,6 @@ #include "qgsunittypes.h" #include "qgsmaptoolidentifyaction.h" #include "qgssettings.h" -#include "qgstestutils.h" #include "cpl_conv.h" diff --git a/tests/src/app/testqgsmeasuretool.cpp b/tests/src/app/testqgsmeasuretool.cpp index 076cda2f104..d00a72df733 100644 --- a/tests/src/app/testqgsmeasuretool.cpp +++ b/tests/src/app/testqgsmeasuretool.cpp @@ -24,7 +24,6 @@ #include "qgsproject.h" #include "qgsmapcanvas.h" #include "qgsunittypes.h" -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the measure tool diff --git a/tests/src/core/testqgscomposermap.cpp b/tests/src/core/testqgscomposermap.cpp index 9853e6dea95..87a8658f15f 100644 --- a/tests/src/core/testqgscomposermap.cpp +++ b/tests/src/core/testqgscomposermap.cpp @@ -29,7 +29,6 @@ #include "qgsproperty.h" #include #include "qgstest.h" -#include "qgstestutils.h" class TestQgsComposerMap : public QObject { diff --git a/tests/src/core/testqgscomposerutils.cpp b/tests/src/core/testqgscomposerutils.cpp index 36daa711cc4..ab11510e90f 100644 --- a/tests/src/core/testqgscomposerutils.cpp +++ b/tests/src/core/testqgscomposerutils.cpp @@ -22,7 +22,6 @@ #include "qgsmultirenderchecker.h" #include "qgsfontutils.h" #include "qgsproject.h" -#include "qgstestutils.h" #include "qgsproperty.h" #include #include "qgstest.h" diff --git a/tests/src/core/testqgscomposition.cpp b/tests/src/core/testqgscomposition.cpp index 380ec809940..4f8faf60064 100644 --- a/tests/src/core/testqgscomposition.cpp +++ b/tests/src/core/testqgscomposition.cpp @@ -37,7 +37,6 @@ #include #include "qgstest.h" -#include "qgstestutils.h" class TestQgsComposition : public QObject { diff --git a/tests/src/core/testqgscoordinatetransform.cpp b/tests/src/core/testqgscoordinatetransform.cpp index 8a71e3be93f..b0e28bcee4f 100644 --- a/tests/src/core/testqgscoordinatetransform.cpp +++ b/tests/src/core/testqgscoordinatetransform.cpp @@ -19,7 +19,6 @@ #include "qgsrectangle.h" #include #include "qgstest.h" -#include "qgstestutils.h" class TestQgsCoordinateTransform: public QObject { diff --git a/tests/src/core/testqgscurve.cpp b/tests/src/core/testqgscurve.cpp index 03741a2dcfd..337815c2c83 100644 --- a/tests/src/core/testqgscurve.cpp +++ b/tests/src/core/testqgscurve.cpp @@ -25,7 +25,6 @@ #include "qgslinestring.h" #include "qgspoint.h" #include "qgstest.h" -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the operations on curve geometries diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp index 661a8e1b753..55d2ff38147 100644 --- a/tests/src/core/testqgsdistancearea.cpp +++ b/tests/src/core/testqgsdistancearea.cpp @@ -26,7 +26,6 @@ #include "qgsgeometryfactory.h" #include "qgsgeometry.h" #include "qgis.h" -#include "qgstestutils.h" #include class TestQgsDistanceArea: public QObject diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 45936998e4a..b2d9db39654 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -33,7 +33,6 @@ #include "qgsrasterlayer.h" #include "qgsproject.h" #include "qgsexpressionnodeimpl.h" -#include "qgstestutils.h" static void _parseAndEvalExpr( int arg ) { diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index dfbdcf91bef..56424f04b3a 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -44,7 +44,6 @@ #include "qgscircularstring.h" #include "qgsgeometrycollection.h" #include "qgsgeometryfactory.h" -#include "qgstestutils.h" //qgs unit test utility class #include "qgsrenderchecker.h" diff --git a/tests/src/core/testqgsgeometryimport.cpp b/tests/src/core/testqgsgeometryimport.cpp index c48fd0e8fb2..2dc7bd03078 100644 --- a/tests/src/core/testqgsgeometryimport.cpp +++ b/tests/src/core/testqgsgeometryimport.cpp @@ -21,7 +21,6 @@ #include "qgstest.h" -#include "qgstestutils.h" #include diff --git a/tests/src/core/testqgsgeometryutils.cpp b/tests/src/core/testqgsgeometryutils.cpp index 1d34e9dc3a2..05a581a2ef6 100644 --- a/tests/src/core/testqgsgeometryutils.cpp +++ b/tests/src/core/testqgsgeometryutils.cpp @@ -19,7 +19,6 @@ #include "qgslinestring.h" #include "qgspolygon.h" #include "qgsmultipolygon.h" -#include "qgstestutils.h" class TestQgsGeometryUtils: public QObject { diff --git a/tests/src/core/testqgshistogram.cpp b/tests/src/core/testqgshistogram.cpp index 2a5fa25185b..be27f61f3e4 100644 --- a/tests/src/core/testqgshistogram.cpp +++ b/tests/src/core/testqgshistogram.cpp @@ -20,7 +20,6 @@ #include "qgsvectorlayer.h" #include "qgsvectordataprovider.h" #include "qgshistogram.h" -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for QgsHistogram diff --git a/tests/src/core/testqgslayout.cpp b/tests/src/core/testqgslayout.cpp index 6684d6fccc7..c4932b1cfc2 100644 --- a/tests/src/core/testqgslayout.cpp +++ b/tests/src/core/testqgslayout.cpp @@ -20,7 +20,6 @@ #include "qgsproject.h" #include "qgslayoutitemmap.h" #include "qgslayoutitemshape.h" -#include "qgstestutils.h" class TestQgsLayout: public QObject { diff --git a/tests/src/core/testqgslayoutitem.cpp b/tests/src/core/testqgslayoutitem.cpp index 8caa697ba91..273be006c77 100644 --- a/tests/src/core/testqgslayoutitem.cpp +++ b/tests/src/core/testqgslayoutitem.cpp @@ -22,7 +22,6 @@ #include "qgstest.h" #include "qgsproject.h" #include "qgsreadwritecontext.h" -#include "qgstestutils.h" #include #include #include diff --git a/tests/src/core/testqgslayoutunits.cpp b/tests/src/core/testqgslayoutunits.cpp index 1f4c2ae3efd..44333141047 100644 --- a/tests/src/core/testqgslayoutunits.cpp +++ b/tests/src/core/testqgslayoutunits.cpp @@ -23,7 +23,6 @@ #include "qgslayoutsize.h" #include "qgslayoutmeasurementconverter.h" #include "qgis.h" -#include "qgstestutils.h" class TestQgsLayoutUnits : public QObject { diff --git a/tests/src/core/testqgslayoututils.cpp b/tests/src/core/testqgslayoututils.cpp index da5a66dc93a..3b36307931c 100644 --- a/tests/src/core/testqgslayoututils.cpp +++ b/tests/src/core/testqgslayoututils.cpp @@ -18,7 +18,6 @@ #include "qgslayout.h" #include "qgstest.h" #include "qgslayoututils.h" -#include "qgstestutils.h" #include "qgsproject.h" #include "qgslayoutitemmap.h" diff --git a/tests/src/core/testqgsmaptopixel.cpp b/tests/src/core/testqgsmaptopixel.cpp index 7a02b1baa3c..c3e91bee0f9 100644 --- a/tests/src/core/testqgsmaptopixel.cpp +++ b/tests/src/core/testqgsmaptopixel.cpp @@ -20,7 +20,6 @@ #include #include #include "qgslogger.h" -#include "qgstestutils.h" class TestQgsMapToPixel: public QObject { diff --git a/tests/src/core/testqgspoint.cpp b/tests/src/core/testqgspoint.cpp index 651cdd20c95..d3561451af7 100644 --- a/tests/src/core/testqgspoint.cpp +++ b/tests/src/core/testqgspoint.cpp @@ -25,7 +25,6 @@ #include //header for class being tested #include -#include "qgstestutils.h" class TestQgsPointXY: public QObject { diff --git a/tests/src/core/testqgsprocessing.cpp b/tests/src/core/testqgsprocessing.cpp index afd02c8a449..de11fe2c9cc 100644 --- a/tests/src/core/testqgsprocessing.cpp +++ b/tests/src/core/testqgsprocessing.cpp @@ -25,7 +25,6 @@ #include #include "qgis.h" #include "qgstest.h" -#include "qgstestutils.h" #include "qgsrasterlayer.h" #include "qgsproject.h" #include "qgspoint.h" diff --git a/tests/src/core/testqgsproperty.cpp b/tests/src/core/testqgsproperty.cpp index f72aebcb376..c6e9fe8733a 100644 --- a/tests/src/core/testqgsproperty.cpp +++ b/tests/src/core/testqgsproperty.cpp @@ -16,7 +16,6 @@ ***************************************************************************/ #include "qgstest.h" -#include "qgstestutils.h" #include "qgsproperty.h" #include "qgspropertycollection.h" #include "qgsvectorlayer.h" diff --git a/tests/src/core/testqgsrasterlayer.cpp b/tests/src/core/testqgsrasterlayer.cpp index 208871193c7..26d00f77bf9 100644 --- a/tests/src/core/testqgsrasterlayer.cpp +++ b/tests/src/core/testqgsrasterlayer.cpp @@ -45,7 +45,6 @@ //qgis unit test includes #include -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the QgsRasterLayer class. diff --git a/tests/src/core/testqgsrastersublayer.cpp b/tests/src/core/testqgsrastersublayer.cpp index eeb85335d6c..7c1f3002b5f 100644 --- a/tests/src/core/testqgsrastersublayer.cpp +++ b/tests/src/core/testqgsrastersublayer.cpp @@ -39,7 +39,6 @@ //qgis unit test includes #include -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for raster sublayers diff --git a/tests/src/core/testqgsstatisticalsummary.cpp b/tests/src/core/testqgsstatisticalsummary.cpp index 2f262ac2455..04d73af4713 100644 --- a/tests/src/core/testqgsstatisticalsummary.cpp +++ b/tests/src/core/testqgsstatisticalsummary.cpp @@ -20,7 +20,6 @@ #include "qgsstatisticalsummary.h" #include "qgis.h" -#include "qgstestutils.h" class TestQgsStatisticSummary: public QObject { diff --git a/tests/src/core/testqgstracer.cpp b/tests/src/core/testqgstracer.cpp index 7642aa6ee68..247597291ec 100644 --- a/tests/src/core/testqgstracer.cpp +++ b/tests/src/core/testqgstracer.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include diff --git a/tests/src/gui/testprojectionissues.cpp b/tests/src/gui/testprojectionissues.cpp index 50d7a487df8..6d83840df46 100644 --- a/tests/src/gui/testprojectionissues.cpp +++ b/tests/src/gui/testprojectionissues.cpp @@ -23,7 +23,6 @@ #include "qgsrasterlayer.h" #include #include "qgstest.h" -#include "qgstestutils.h" class TestProjectionIssues : public QObject { diff --git a/tests/src/gui/testqgslayoutview.cpp b/tests/src/gui/testqgslayoutview.cpp index 7aa23f7b1ee..dbedb93e7ae 100644 --- a/tests/src/gui/testqgslayoutview.cpp +++ b/tests/src/gui/testqgslayoutview.cpp @@ -23,7 +23,6 @@ #include "qgslayoutitemregistry.h" #include "qgslayoutitemguiregistry.h" #include "qgslayoutitemwidget.h" -#include "qgstestutils.h" #include "qgsproject.h" #include "qgsgui.h" #include diff --git a/tests/src/gui/testqgsmapcanvas.cpp b/tests/src/gui/testqgsmapcanvas.cpp index b6c3fac9310..0e4d83528e8 100644 --- a/tests/src/gui/testqgsmapcanvas.cpp +++ b/tests/src/gui/testqgsmapcanvas.cpp @@ -22,7 +22,6 @@ #include #include #include -#include "qgstestutils.h" namespace QTest { diff --git a/tests/src/providers/testqgsgdalprovider.cpp b/tests/src/providers/testqgsgdalprovider.cpp index d26f00dc7a6..700768ab709 100644 --- a/tests/src/providers/testqgsgdalprovider.cpp +++ b/tests/src/providers/testqgsgdalprovider.cpp @@ -29,7 +29,6 @@ #include #include #include -#include "qgstestutils.h" /** \ingroup UnitTests * This is a unit test for the gdal provider From fadfb3562a9f50566644feeb22aaafe3f0f9d93b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 11:16:17 +1000 Subject: [PATCH 286/364] Fix error when adding disabled plugin based processing providers --- python/plugins/processing/gui/ProcessingToolbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/gui/ProcessingToolbox.py b/python/plugins/processing/gui/ProcessingToolbox.py index e2cc4309593..fb0ca4a69a0 100644 --- a/python/plugins/processing/gui/ProcessingToolbox.py +++ b/python/plugins/processing/gui/ProcessingToolbox.py @@ -430,7 +430,7 @@ class ProcessingToolbox(BASE, WIDGET): label = QLabel(text + "    Activate") label.setStyleSheet("QLabel {background-color: white; color: grey;}") label.linkActivated.connect(activateProvider) - self.algorithmTree.setItemWidget(item, 0, label) + self.algorithmTree.setItemWidget(parent, 0, label) else: text += QCoreApplication.translate("TreeProviderItem", " [{0} geoalgorithms]").format(count) From 0f407e1e5d924fede147e62f2c6db3112a7be31a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 12:23:25 +1000 Subject: [PATCH 287/364] Fix rotate label tool results in inverted rotation Fix #17068 --- src/app/qgsmaptoolrotatelabel.cpp | 27 +++++++++++++++------------ src/app/qgsmaptoolrotatelabel.h | 8 ++++---- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/app/qgsmaptoolrotatelabel.cpp b/src/app/qgsmaptoolrotatelabel.cpp index ff6a9ff15b7..2976ae00ce0 100644 --- a/src/app/qgsmaptoolrotatelabel.cpp +++ b/src/app/qgsmaptoolrotatelabel.cpp @@ -75,7 +75,7 @@ void QgsMapToolRotateLabel::canvasPressEvent( QgsMapMouseEvent *e ) if ( true ) { - mCurrentMouseAzimuth = azimuthToCCW( mRotationPoint.azimuth( toMapCoordinates( e->pos() ) ) ); + mCurrentMouseAzimuth = convertAzimuth( mRotationPoint.azimuth( toMapCoordinates( e->pos() ) ) ); bool hasRotationValue; int rotationCol; @@ -91,7 +91,7 @@ void QgsMapToolRotateLabel::canvasPressEvent( QgsMapMouseEvent *e ) mRotationPreviewBox = createRotationPreviewBox(); mRotationItem = new QgsPointRotationItem( mCanvas ); - mRotationItem->setOrientation( QgsPointRotationItem::Counterclockwise ); + mRotationItem->setOrientation( QgsPointRotationItem::Clockwise ); mRotationItem->setSymbol( QgsApplication::getThemePixmap( QStringLiteral( "mActionRotatePointSymbols.svg" ) ).toImage() ); mRotationItem->setPointLocation( mRotationPoint ); mRotationItem->setSymbolRotation( mCurrentRotation ); @@ -104,15 +104,17 @@ void QgsMapToolRotateLabel::canvasMoveEvent( QgsMapMouseEvent *e ) if ( mLabelRubberBand ) { QgsPointXY currentPoint = toMapCoordinates( e->pos() ); - double azimuth = azimuthToCCW( mRotationPoint.azimuth( currentPoint ) ); + double azimuth = convertAzimuth( mRotationPoint.azimuth( currentPoint ) ); double azimuthDiff = azimuth - mCurrentMouseAzimuth; azimuthDiff = azimuthDiff > 180 ? azimuthDiff - 360 : azimuthDiff; mCurrentRotation += azimuthDiff; - mCurrentRotation = mCurrentRotation - static_cast( static_cast( mCurrentRotation / 360 ) ) * 360; //mCurrentRotation % 360; - mCurrentRotation = mCurrentRotation < 0 ? 360 - mCurrentRotation : mCurrentRotation; + if ( mCurrentRotation >= 360 || mCurrentRotation <= -360 ) + mCurrentRotation = std::fmod( mCurrentRotation, 360.0 ); + if ( mCurrentRotation < 0 ) + mCurrentRotation += 360.0; - mCurrentMouseAzimuth = azimuth - static_cast( static_cast( azimuth / 360 ) ) * 360; + mCurrentMouseAzimuth = std::fmod( azimuth, 360.0 ); //if shift-modifier is pressed, round to 15 degrees int displayValue; @@ -181,9 +183,10 @@ int QgsMapToolRotateLabel::roundTo15Degrees( double n ) return ( m * 15 ); } -double QgsMapToolRotateLabel::azimuthToCCW( double a ) +double QgsMapToolRotateLabel::convertAzimuth( double a ) { - return ( a > 0 ? 360 - a : -a ); + a -= 90; // convert from 0 = north to 0 = east + return ( a <= -180.0 ? 360 + a : a ); } QgsRubberBand *QgsMapToolRotateLabel::createRotationPreviewBox() @@ -218,15 +221,15 @@ void QgsMapToolRotateLabel::setRotationPreviewBox( double rotation ) for ( int i = 0; i < boxPoints.size(); ++i ) { - mRotationPreviewBox->addPoint( rotatePointCounterClockwise( boxPoints.at( i ), mRotationPoint, rotation ) ); + mRotationPreviewBox->addPoint( rotatePointClockwise( boxPoints.at( i ), mRotationPoint, rotation ) ); } - mRotationPreviewBox->addPoint( rotatePointCounterClockwise( boxPoints.at( 0 ), mRotationPoint, rotation ) ); + mRotationPreviewBox->addPoint( rotatePointClockwise( boxPoints.at( 0 ), mRotationPoint, rotation ) ); mRotationPreviewBox->show(); } -QgsPointXY QgsMapToolRotateLabel::rotatePointCounterClockwise( const QgsPointXY &input, const QgsPointXY ¢erPoint, double degrees ) +QgsPointXY QgsMapToolRotateLabel::rotatePointClockwise( const QgsPointXY &input, const QgsPointXY ¢erPoint, double degrees ) const { - double rad = degrees / 180 * M_PI; + double rad = -degrees / 180 * M_PI; double v1x = input.x() - centerPoint.x(); double v1y = input.y() - centerPoint.y(); diff --git a/src/app/qgsmaptoolrotatelabel.h b/src/app/qgsmaptoolrotatelabel.h index 91b299a10c8..510ab5eb842 100644 --- a/src/app/qgsmaptoolrotatelabel.h +++ b/src/app/qgsmaptoolrotatelabel.h @@ -37,14 +37,14 @@ class APP_EXPORT QgsMapToolRotateLabel: public QgsMapToolLabel protected: static int roundTo15Degrees( double n ); - //! Converts azimuth value to counterclockwise 0 - 360 - static double azimuthToCCW( double a ); + //! Converts azimuth value so that 0 is corresponds to East + static double convertAzimuth( double a ); QgsRubberBand *createRotationPreviewBox(); void setRotationPreviewBox( double rotation ); - //! Rotates input point counterclockwise around centerPoint - QgsPointXY rotatePointCounterClockwise( const QgsPointXY &input, const QgsPointXY ¢erPoint, double degrees ); + //! Rotates input point clockwise around centerPoint + QgsPointXY rotatePointClockwise( const QgsPointXY &input, const QgsPointXY ¢erPoint, double degrees ) const; double mStartRotation; //rotation value prior to start rotating double mCurrentRotation; From f0e53db254f38071e87b993b580ea050b19f4b17 Mon Sep 17 00:00:00 2001 From: Nathan Woodrow Date: Mon, 4 Sep 2017 12:29:54 +1000 Subject: [PATCH 288/364] Ignore some python warnings for now shhhh Qt5 --- python/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/python/utils.py b/python/utils.py index 241af1ed48e..6b9f35b2688 100644 --- a/python/utils.py +++ b/python/utils.py @@ -59,7 +59,16 @@ warnings.simplefilter('default') warnings.filterwarnings("ignore", "the sets module is deprecated") +ignorelist = [ + "objcreator.py:152: DeprecationWarning: 'U' mode is deprecated" + "DeprecationWarning: the imp module is deprecated in favour of importlib;" +] + def showWarning(message, category, filename, lineno, file=None, line=None): + for ignore in ignorelist: + if ignore in message: + return + stk = "" for s in traceback.format_stack()[:-2]: if hasattr(s, 'decode'): From d1d5e6cf83d4b2e898bf7385181febfc9e7ec6d1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 12:34:47 +1000 Subject: [PATCH 289/364] Fix bad icon drawn on top of points when rotating labels --- src/app/qgsmaptoolrotatelabel.cpp | 1 - src/app/qgspointrotationitem.cpp | 10 +++------- src/app/qgspointrotationitem.h | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/app/qgsmaptoolrotatelabel.cpp b/src/app/qgsmaptoolrotatelabel.cpp index 2976ae00ce0..d7354f9e1f0 100644 --- a/src/app/qgsmaptoolrotatelabel.cpp +++ b/src/app/qgsmaptoolrotatelabel.cpp @@ -92,7 +92,6 @@ void QgsMapToolRotateLabel::canvasPressEvent( QgsMapMouseEvent *e ) mRotationItem = new QgsPointRotationItem( mCanvas ); mRotationItem->setOrientation( QgsPointRotationItem::Clockwise ); - mRotationItem->setSymbol( QgsApplication::getThemePixmap( QStringLiteral( "mActionRotatePointSymbols.svg" ) ).toImage() ); mRotationItem->setPointLocation( mRotationPoint ); mRotationItem->setSymbolRotation( mCurrentRotation ); } diff --git a/src/app/qgspointrotationitem.cpp b/src/app/qgspointrotationitem.cpp index cb7405d1110..5e7897a3c9d 100644 --- a/src/app/qgspointrotationitem.cpp +++ b/src/app/qgspointrotationitem.cpp @@ -25,14 +25,10 @@ QgsPointRotationItem::QgsPointRotationItem( QgsMapCanvas *canvas ) //setup font mFont.setPointSize( 12 ); mFont.setBold( true ); -} - -QgsPointRotationItem::QgsPointRotationItem() - : QgsMapCanvasItem( nullptr ) - , mOrientation( Clockwise ) - , mRotation( 0.0 ) -{ + QImage im( 24, 24, QImage::Format_ARGB32 ); + im.fill( Qt::transparent ); + setSymbol( im ); } QgsPointRotationItem::~QgsPointRotationItem() diff --git a/src/app/qgspointrotationitem.h b/src/app/qgspointrotationitem.h index 37120cfbcc8..148613e3af7 100644 --- a/src/app/qgspointrotationitem.h +++ b/src/app/qgspointrotationitem.h @@ -51,7 +51,7 @@ class APP_EXPORT QgsPointRotationItem: public QgsMapCanvasItem Orientation orientation() const { return mOrientation; } private: - QgsPointRotationItem(); + //! Converts rotation into QPainter rotation considering mOrientation int painterRotation( int rotation ) const; //! Clockwise (default) or counterclockwise From df7c3df473d013f9e7c510e985b3b1d53a6164c9 Mon Sep 17 00:00:00 2001 From: Nathan Woodrow Date: Mon, 4 Sep 2017 14:11:38 +1000 Subject: [PATCH 290/364] Revert "Ignore some python warnings for now" Doesn't work on all platforms This reverts commit f0e53db254f38071e87b993b580ea050b19f4b17. --- python/utils.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/python/utils.py b/python/utils.py index 6b9f35b2688..241af1ed48e 100644 --- a/python/utils.py +++ b/python/utils.py @@ -59,16 +59,7 @@ warnings.simplefilter('default') warnings.filterwarnings("ignore", "the sets module is deprecated") -ignorelist = [ - "objcreator.py:152: DeprecationWarning: 'U' mode is deprecated" - "DeprecationWarning: the imp module is deprecated in favour of importlib;" -] - def showWarning(message, category, filename, lineno, file=None, line=None): - for ignore in ignorelist: - if ignore in message: - return - stk = "" for s in traceback.format_stack()[:-2]: if hasattr(s, 'decode'): From 90f873086cc56cd2bd29c11360d1975f6e8f686a Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 08:00:14 +0200 Subject: [PATCH 291/364] Attach QgsSourceSelectProviderRegistry to QgsGui And make it a singleton --- python/gui/qgsgui.sip | 6 ++++++ python/gui/qgssourceselectproviderregistry.sip | 3 +++ src/gui/qgsgui.cpp | 8 ++++++++ src/gui/qgsgui.h | 7 +++++++ src/gui/qgssourceselectproviderregistry.h | 3 +++ .../src/python/test_qgssourceselectprovider.py | 18 +++++++++++++----- 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/python/gui/qgsgui.sip b/python/gui/qgsgui.sip index f8ba363606a..aa704881ee2 100644 --- a/python/gui/qgsgui.sip +++ b/python/gui/qgsgui.sip @@ -38,6 +38,12 @@ class QgsGui :rtype: QgsEditorWidgetRegistry %End + static QgsSourceSelectProviderRegistry *sourceSelectProviderRegistry(); +%Docstring + Returns the global source select provider registry, used for managing all known source select widget factories. + :rtype: QgsSourceSelectProviderRegistry +%End + static QgsShortcutsManager *shortcutsManager(); %Docstring Returns the global shortcuts manager, used for managing a QAction and QShortcut sequences. diff --git a/python/gui/qgssourceselectproviderregistry.sip b/python/gui/qgssourceselectproviderregistry.sip index 3f108447f18..57976704a36 100644 --- a/python/gui/qgssourceselectproviderregistry.sip +++ b/python/gui/qgssourceselectproviderregistry.sip @@ -14,6 +14,9 @@ class QgsSourceSelectProviderRegistry This class keeps a list of source select providers that may add items to the QgsDataSourceManagerDialog When created, it automatically adds providers from data provider plugins (e.g. PostGIS, WMS, ...) + QgsSourceSelectProviderRegistry is not usually directly created, but rather accessed through + QgsGui.sourceSelectProviderRegistry(). + .. versionadded:: 3.0 %End diff --git a/src/gui/qgsgui.cpp b/src/gui/qgsgui.cpp index 0227af59b69..0d10361e4e5 100644 --- a/src/gui/qgsgui.cpp +++ b/src/gui/qgsgui.cpp @@ -19,6 +19,7 @@ #include "qgseditorwidgetregistry.h" #include "qgslayertreeembeddedwidgetregistry.h" #include "qgsmaplayeractionregistry.h" +#include "qgssourceselectproviderregistry.h" #include "qgslayoutitemregistry.h" #include "qgslayoutitemguiregistry.h" #include "qgslayoutviewrubberband.h" @@ -45,6 +46,11 @@ QgsEditorWidgetRegistry *QgsGui::editorWidgetRegistry() return instance()->mEditorWidgetRegistry; } +QgsSourceSelectProviderRegistry *QgsGui::sourceSelectProviderRegistry() +{ + return instance()->mSourceSelectProviderRegistry; +} + QgsShortcutsManager *QgsGui::shortcutsManager() { return instance()->mShortcutsManager; @@ -71,6 +77,7 @@ QgsGui::~QgsGui() delete mLayerTreeEmbeddedWidgetRegistry; delete mEditorWidgetRegistry; delete mMapLayerActionRegistry; + delete mSourceSelectProviderRegistry; delete mShortcutsManager; delete mNative; } @@ -87,6 +94,7 @@ QgsGui::QgsGui() mShortcutsManager = new QgsShortcutsManager(); mLayerTreeEmbeddedWidgetRegistry = new QgsLayerTreeEmbeddedWidgetRegistry(); mMapLayerActionRegistry = new QgsMapLayerActionRegistry(); + mSourceSelectProviderRegistry = new QgsSourceSelectProviderRegistry(); mLayoutItemGuiRegistry = new QgsLayoutItemGuiRegistry(); mLayoutItemGuiRegistry->populate(); } diff --git a/src/gui/qgsgui.h b/src/gui/qgsgui.h index 794e8ef79c1..5b756de1a66 100644 --- a/src/gui/qgsgui.h +++ b/src/gui/qgsgui.h @@ -25,6 +25,7 @@ class QgsEditorWidgetRegistry; class QgsShortcutsManager; class QgsLayerTreeEmbeddedWidgetRegistry; class QgsMapLayerActionRegistry; +class QgsSourceSelectProviderRegistry; class QgsNative; class QgsLayoutItemGuiRegistry; @@ -61,6 +62,11 @@ class GUI_EXPORT QgsGui */ static QgsEditorWidgetRegistry *editorWidgetRegistry(); + /** + * Returns the global source select provider registry, used for managing all known source select widget factories. + */ + static QgsSourceSelectProviderRegistry *sourceSelectProviderRegistry(); + /** * Returns the global shortcuts manager, used for managing a QAction and QShortcut sequences. */ @@ -89,6 +95,7 @@ class GUI_EXPORT QgsGui QgsNative *mNative = nullptr; QgsEditorWidgetRegistry *mEditorWidgetRegistry = nullptr; + QgsSourceSelectProviderRegistry *mSourceSelectProviderRegistry = nullptr; QgsShortcutsManager *mShortcutsManager = nullptr; QgsLayerTreeEmbeddedWidgetRegistry *mLayerTreeEmbeddedWidgetRegistry = nullptr; QgsMapLayerActionRegistry *mMapLayerActionRegistry = nullptr; diff --git a/src/gui/qgssourceselectproviderregistry.h b/src/gui/qgssourceselectproviderregistry.h index b896b8a6cf4..dfa7d9b4575 100644 --- a/src/gui/qgssourceselectproviderregistry.h +++ b/src/gui/qgssourceselectproviderregistry.h @@ -25,6 +25,9 @@ class QgsSourceSelectProvider; * This class keeps a list of source select providers that may add items to the QgsDataSourceManagerDialog * When created, it automatically adds providers from data provider plugins (e.g. PostGIS, WMS, ...) * + * QgsSourceSelectProviderRegistry is not usually directly created, but rather accessed through + * QgsGui::sourceSelectProviderRegistry(). + * * \since QGIS 3.0 */ class GUI_EXPORT QgsSourceSelectProviderRegistry diff --git a/tests/src/python/test_qgssourceselectprovider.py b/tests/src/python/test_qgssourceselectprovider.py index 293c5a009ae..f73519d434c 100644 --- a/tests/src/python/test_qgssourceselectprovider.py +++ b/tests/src/python/test_qgssourceselectprovider.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """ -Test the QgsSourceSelectProvider class +Test the QgsSourceSelectProvider +and QgsSourceSelectProviderRegistry classes Run with: ctest -V -R PyQgsSourceSelectProvider @@ -12,7 +13,7 @@ the Free Software Foundation; either version 2 of the License, or import os import tempfile -from qgis.gui import (QgsSourceSelectProvider, QgsSourceSelectProviderRegistry, QgsAbstractDataSourceWidget) +from qgis.gui import (QgsGui, QgsSourceSelectProvider, QgsSourceSelectProviderRegistry, QgsAbstractDataSourceWidget) from qgis.testing import start_app, unittest from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtWidgets import QWidget @@ -90,9 +91,8 @@ class TestQgsSourceSelectProvider(unittest.TestCase): self.assertEqual(provider.ordering(), 1) self.assertTrue(isinstance(provider.icon(), QIcon)) - def testRegistry(self): + def _testRegistry(self, registry): - registry = QgsSourceSelectProviderRegistry() registry.addProvider(ConcreteSourceSelectProvider()) registry.addProvider(ConcreteSourceSelectProvider2()) @@ -113,13 +113,21 @@ class TestQgsSourceSelectProvider(unittest.TestCase): # Get not existent by name self.assertFalse(registry.providerByName('Oh This Is Missing!')) - # Get providers by provider key + # Get providers by data provider key self.assertGreater(len(registry.providersByKey('MyTestProviderKey')), 0) self.assertGreater(len(registry.providersByKey('MyTestProviderKey2')), 0) # Get not existent by key self.assertEqual(len(registry.providersByKey('Oh This Is Missing!')), 0) + def testRegistry(self): + registry = QgsSourceSelectProviderRegistry() + self._testRegistry(registry) + + def testRegistrySingleton(self): + registry = QgsGui.sourceSelectProviderRegistry() + self._testRegistry(registry) + if __name__ == '__main__': unittest.main() From 42bd913f57dd00548b1f092c7c2504d58e183601 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 08:01:59 +0200 Subject: [PATCH 292/364] Add sourceSelectProviders() to the ogr data provider This is the first data provider real implementation, all other data provider will follow. --- src/providers/ogr/qgsogrdataitems.h | 1 + src/providers/ogr/qgsogrprovider.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/providers/ogr/qgsogrdataitems.h b/src/providers/ogr/qgsogrdataitems.h index 5edf1bdb0ea..ebc948d632a 100644 --- a/src/providers/ogr/qgsogrdataitems.h +++ b/src/providers/ogr/qgsogrdataitems.h @@ -50,4 +50,5 @@ class QgsOgrDataCollectionItem : public QgsDataCollectionItem }; + #endif // QGSOGRDATAITEMS_H diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index da4f637a89a..701046fbd4a 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -35,6 +35,8 @@ email : sherman at mrcc.com #include "qgsogrdataitems.h" #include "qgsgeopackagedataitems.h" #include "qgswkbtypes.h" +#include "qgssourceselectprovider.h" +#include "qgsogrsourceselect.h" #include "qgis.h" @@ -4346,3 +4348,29 @@ QGISEXTERN bool deleteLayer( const QString &uri, QString &errCause ) errCause = QObject::tr( "Layer not found: %1" ).arg( uri ); return false; } + + +//! Provider for OGR vector source select +class QgsOgrVectorSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "ogr" ); } + virtual QString text() const override { return QObject::tr( "Vector" ); } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddOgrLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr ) const override + { + return new QgsOgrSourceSelect( parent ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsOgrVectorSourceSelectProvider; + + return providers; +} From b629fbca49bf91336fac6a8f7fa9806d9791cda1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 4 Sep 2017 16:55:35 +1000 Subject: [PATCH 293/364] Skip another test which is fragile on Travis --- tests/src/python/test_qgsactionmanager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/src/python/test_qgsactionmanager.py b/tests/src/python/test_qgsactionmanager.py index 16f5423b6c2..a365e3c3580 100644 --- a/tests/src/python/test_qgsactionmanager.py +++ b/tests/src/python/test_qgsactionmanager.py @@ -154,6 +154,8 @@ class TestQgsActionManager(unittest.TestCase): return output @unittest.expectedFailure(platform.system() != 'Linux') + @unittest.skipIf( + os.environ.get('TRAVIS', '') == 'true', 'Test is flaky on Travis environment') def testDoAction(self): """ test running action """ From c555079971249a2d424f73857bd23a79f75df05f Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Mon, 4 Sep 2017 09:00:25 +0200 Subject: [PATCH 294/364] Remove unreachable code from curveToLine Drops the unused support for including control points in output. See https://github.com/qgis/QGIS/pull/4746#issuecomment-326730297 --- src/core/geometry/qgsgeometryutils.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/core/geometry/qgsgeometryutils.cpp b/src/core/geometry/qgsgeometryutils.cpp index fafee9f9529..d3f01554abe 100644 --- a/src/core/geometry/qgsgeometryutils.cpp +++ b/src/core/geometry/qgsgeometryutils.cpp @@ -737,15 +737,6 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co . arg( increment ) ); - //make sure the curve point p2 is part of the segmentized vertices. But only if p1 != p3 - // TODO: make this a parameter - bool addP2 = true; - if ( qgsDoubleNear( circlePoint1.x(), circlePoint3.x() ) && qgsDoubleNear( circlePoint1.y(), circlePoint3.y() ) ) - { - addP2 = false; - } - addP2 = false; - // As we're adding the last point in any case, we'll avoid // including a point which is at less than 1% increment distance // from it (may happen to find them due to numbers approximation). @@ -757,16 +748,6 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co QgsDebugMsg( QString( "stopAngle: %1 (%2)" ) . arg( stopAngle ) .arg( stopAngle * 180 / M_PI ) ); for ( double angle = a1 + increment; angle < stopAngle; angle += increment ) { - if ( addP2 && angle > a2 ) - { - if ( stringPoints.empty() || stringPoints.back() != circlePoint2 ) - { - QgsDebugMsg( QString( "Adding control point, with angle %1 (%2)" ) . arg( a2 ) .arg( a2 * 180 / M_PI ) ); - stringPoints.insert( stringPoints.size(), circlePoint2 ); - } - addP2 = false; - } - QgsDebugMsg( QString( "SA - %1 (%2)" ) . arg( angle ) .arg( angle * 180 / M_PI ) ); x = centerX + radius * std::cos( angle ); From 1816087485ea105d5566e52c6d3b966e80984648 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 09:10:56 +0200 Subject: [PATCH 295/364] QgsSourceSelectProviderRegistry delayed initialization Due to QgsGui being initialized after data provider. --- .../gui/qgssourceselectproviderregistry.sip | 12 +++- src/gui/qgssourceselectproviderregistry.cpp | 72 ++++++++++++------- src/gui/qgssourceselectproviderregistry.h | 14 +++- 3 files changed, 65 insertions(+), 33 deletions(-) diff --git a/python/gui/qgssourceselectproviderregistry.sip b/python/gui/qgssourceselectproviderregistry.sip index 57976704a36..24ef8d8c206 100644 --- a/python/gui/qgssourceselectproviderregistry.sip +++ b/python/gui/qgssourceselectproviderregistry.sip @@ -17,6 +17,12 @@ class QgsSourceSelectProviderRegistry QgsSourceSelectProviderRegistry is not usually directly created, but rather accessed through QgsGui.sourceSelectProviderRegistry(). +.. note:: + + This class access to QgsProviderRegistry instance to initialize, but QgsProviderRegistry is + typically initialized after QgsGui is constructed, for this reason a delayed initialization has been + implemented in the class. + .. versionadded:: 3.0 %End @@ -29,7 +35,7 @@ class QgsSourceSelectProviderRegistry ~QgsSourceSelectProviderRegistry(); - QList< QgsSourceSelectProvider *> providers() const; + QList< QgsSourceSelectProvider *> providers(); %Docstring Get list of available providers :rtype: list of QgsSourceSelectProvider @@ -45,13 +51,13 @@ Add a provider implementation. Takes ownership of the object. Remove provider implementation from the list (provider object is deleted) %End - QgsSourceSelectProvider *providerByName( const QString &name ) const; + QgsSourceSelectProvider *providerByName( const QString &name ); %Docstring Return a provider by name or None if not found :rtype: QgsSourceSelectProvider %End - QList providersByKey( const QString &providerKey ) const; + QList providersByKey( const QString &providerKey ); %Docstring Return a (possibly empty) list of providers by data provider's key :rtype: list of QgsSourceSelectProvider diff --git a/src/gui/qgssourceselectproviderregistry.cpp b/src/gui/qgssourceselectproviderregistry.cpp index 76bc84a89d0..b57b9c22a59 100644 --- a/src/gui/qgssourceselectproviderregistry.cpp +++ b/src/gui/qgssourceselectproviderregistry.cpp @@ -24,26 +24,7 @@ typedef QList *sourceSelectProviders_t(); QgsSourceSelectProviderRegistry::QgsSourceSelectProviderRegistry() { - QStringList providersList = QgsProviderRegistry::instance()->providerList(); - - Q_FOREACH ( const QString &key, providersList ) - { - std::unique_ptr< QLibrary > library( QgsProviderRegistry::instance()->createProviderLibrary( key ) ); - if ( !library ) - continue; - - sourceSelectProviders_t *sourceSelectProvidersFn = reinterpret_cast< sourceSelectProviders_t * >( cast_to_fptr( library->resolve( "sourceSelectProviders" ) ) ); - if ( sourceSelectProvidersFn ) - { - QList *providerList = sourceSelectProvidersFn(); - // the function is a factory - we keep ownership of the returned providers - for ( auto provider : qgsAsConst( *providerList ) ) - { - addProvider( provider ); - } - delete providerList; - } - } + // Initialization is delayed } QgsSourceSelectProviderRegistry::~QgsSourceSelectProviderRegistry() @@ -51,6 +32,12 @@ QgsSourceSelectProviderRegistry::~QgsSourceSelectProviderRegistry() qDeleteAll( mProviders ); } +QList QgsSourceSelectProviderRegistry::providers() +{ + init(); + return mProviders; +} + void QgsSourceSelectProviderRegistry::addProvider( QgsSourceSelectProvider *provider ) { mProviders.append( provider ); @@ -67,9 +54,10 @@ void QgsSourceSelectProviderRegistry::removeProvider( QgsSourceSelectProvider *p delete mProviders.takeAt( index ); } -QgsSourceSelectProvider *QgsSourceSelectProviderRegistry::providerByName( const QString &name ) const +QgsSourceSelectProvider *QgsSourceSelectProviderRegistry::providerByName( const QString &name ) { - for ( const auto provider : qgsAsConst( mProviders ) ) + const QList providerList = providers(); + for ( const auto provider : providerList ) { if ( provider->name() == name ) { @@ -79,15 +67,45 @@ QgsSourceSelectProvider *QgsSourceSelectProviderRegistry::providerByName( const return nullptr; } -QList QgsSourceSelectProviderRegistry::providersByKey( const QString &providerKey ) const +QList QgsSourceSelectProviderRegistry::providersByKey( const QString &providerKey ) { - QList providerList; - for ( const auto provider : qgsAsConst( mProviders ) ) + QList result; + const QList providerList = providers(); + for ( const auto provider : providerList ) { if ( provider->providerKey() == providerKey ) { - providerList << provider; + result << provider; } } - return providerList; + return result; } + +void QgsSourceSelectProviderRegistry::init() +{ + if ( mInitialized ) + { + return; + } + QStringList providersList = QgsProviderRegistry::instance()->providerList(); + Q_FOREACH ( const QString &key, providersList ) + { + std::unique_ptr< QLibrary > library( QgsProviderRegistry::instance()->createProviderLibrary( key ) ); + if ( !library ) + continue; + + sourceSelectProviders_t *sourceSelectProvidersFn = reinterpret_cast< sourceSelectProviders_t * >( cast_to_fptr( library->resolve( "sourceSelectProviders" ) ) ); + if ( sourceSelectProvidersFn ) + { + QList *providerList = sourceSelectProvidersFn(); + // the function is a factory - we keep ownership of the returned providers + for ( auto provider : qgsAsConst( *providerList ) ) + { + addProvider( provider ); + } + delete providerList; + } + } + mInitialized = true; +} + diff --git a/src/gui/qgssourceselectproviderregistry.h b/src/gui/qgssourceselectproviderregistry.h index dfa7d9b4575..ea24fda8352 100644 --- a/src/gui/qgssourceselectproviderregistry.h +++ b/src/gui/qgssourceselectproviderregistry.h @@ -28,6 +28,10 @@ class QgsSourceSelectProvider; * QgsSourceSelectProviderRegistry is not usually directly created, but rather accessed through * QgsGui::sourceSelectProviderRegistry(). * + * \note This class access to QgsProviderRegistry instance to initialize, but QgsProviderRegistry is + * typically initialized after QgsGui is constructed, for this reason a delayed initialization has been + * implemented in the class. + * * \since QGIS 3.0 */ class GUI_EXPORT QgsSourceSelectProviderRegistry @@ -43,7 +47,7 @@ class GUI_EXPORT QgsSourceSelectProviderRegistry QgsSourceSelectProviderRegistry &operator=( const QgsSourceSelectProviderRegistry &rh ) = delete; //! Get list of available providers - QList< QgsSourceSelectProvider *> providers() const { return mProviders; } + QList< QgsSourceSelectProvider *> providers(); //! Add a provider implementation. Takes ownership of the object. void addProvider( QgsSourceSelectProvider *provider SIP_TRANSFER ); @@ -52,13 +56,17 @@ class GUI_EXPORT QgsSourceSelectProviderRegistry void removeProvider( QgsSourceSelectProvider *provider ); //! Return a provider by name or nullptr if not found - QgsSourceSelectProvider *providerByName( const QString &name ) const; + QgsSourceSelectProvider *providerByName( const QString &name ); //! Return a (possibly empty) list of providers by data provider's key - QList providersByKey( const QString &providerKey ) const; + QList providersByKey( const QString &providerKey ); private: + //! Populate the providers list, this needs to happen after the data provider + //! registry has been initialized. + void init(); + bool mInitialized = false; #ifdef SIP_RUN QgsSourceSelectProviderRegistry( const QgsSourceSelectProviderRegistry &rh ); #endif From 167df0127dff8d9be637fc86b1574dd65ebe0f86 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Sat, 19 Aug 2017 13:15:47 +0200 Subject: [PATCH 296/364] Set labeling widgets to horizontally expand will be consistent with diagram and symbology widgets (combobox, spinbox and textbox) behavior --- src/ui/qgstextformatwidgetbase.ui | 414 ++++++++++++------------------ 1 file changed, 168 insertions(+), 246 deletions(-) diff --git a/src/ui/qgstextformatwidgetbase.ui b/src/ui/qgstextformatwidgetbase.ui index c8b6eec5bd2..5bffe84e20f 100755 --- a/src/ui/qgstextformatwidgetbase.ui +++ b/src/ui/qgstextformatwidgetbase.ui @@ -172,7 +172,7 @@ 0 0 - 486 + 482 300 @@ -680,8 +680,8 @@ 0 0 - 452 - 470 + 448 + 444 @@ -1161,20 +1161,14 @@ - + 0 0 - - - 120 - 0 - - - 120 + 16777215 16777215 @@ -1466,8 +1460,8 @@ font-style: italic; 0 0 - 359 - 410 + 448 + 389 @@ -2101,8 +2095,8 @@ font-style: italic; 0 0 - 302 - 317 + 464 + 366 @@ -2223,20 +2217,14 @@ font-style: italic; - + 0 0 - - - 120 - 0 - - - 120 + 16777215 16777215 @@ -2453,8 +2441,8 @@ font-style: italic; 0 0 - 458 - 778 + 462 + 738 @@ -2523,6 +2511,66 @@ font-style: italic; 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + Radius X,Y + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + @@ -2595,73 +2643,21 @@ font-style: italic; - + - + - - - - - - - 200 - 0 - - - - - Rectangle - - - - - Square - - - - - Ellipse - - - - - Circle - - - - - SVG - - - - - - - - Qt::Horizontal - - - - 0 - 20 - - - - - - - + @@ -2681,7 +2677,7 @@ font-style: italic; - + @@ -2695,7 +2691,7 @@ font-style: italic; - + @@ -2721,7 +2717,7 @@ font-style: italic; - + @@ -2750,16 +2746,16 @@ font-style: italic; - + + + + - - - @@ -2774,7 +2770,7 @@ font-style: italic; - + @@ -2819,7 +2815,7 @@ font-style: italic; - + @@ -2833,7 +2829,7 @@ font-style: italic; - + @@ -2892,13 +2888,23 @@ font-style: italic; - + + + + + + 150 + 0 + + + + @@ -2921,16 +2927,6 @@ font-style: italic; - - - - - 150 - 0 - - - - @@ -2950,24 +2946,24 @@ font-style: italic; - + - - + + - + Rotation - - + + - Radius X,Y + @@ -3013,35 +3009,28 @@ font-style: italic; - - - - Rotation - - - - + - + - + - + @@ -3067,14 +3056,14 @@ font-style: italic; - + - + @@ -3087,98 +3076,21 @@ font-style: italic; - + 0 0 - - - 120 - 0 - - - - - 120 - 16777215 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - + 0 0 - - - 120 - 0 - - - - - 120 - 16777215 - - @@ -3209,6 +3121,41 @@ font-style: italic; + + + + + 200 + 0 + + + + + Rectangle + + + + + Square + + + + + Ellipse + + + + + Circle + + + + + SVG + + + + @@ -3267,8 +3214,8 @@ font-style: italic; 0 0 - 311 - 470 + 448 + 441 @@ -3337,23 +3284,11 @@ font-style: italic; - + 0 0 - - - 120 - 0 - - - - - 120 - 16777215 - - @@ -3740,8 +3675,8 @@ font-style: italic; 0 0 - 452 - 977 + 448 + 917 @@ -3793,7 +3728,7 @@ font-style: italic; QFrame::Sunken - 1 + 2 @@ -5374,8 +5309,8 @@ font-style: italic; 0 0 - 452 - 877 + 448 + 795 @@ -5685,6 +5620,12 @@ font-style: italic; + + + 0 + 0 + + Controls how labels are drawn on top of each other. Labels with a higher z-index are drawn above labels and diagrams with a lower z-index. @@ -5703,19 +5644,6 @@ font-style: italic; - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -6247,20 +6175,14 @@ font-style: italic; - - - - - - Qt::Horizontal + + + + 0 + 0 + - - - 40 - 20 - - - + From b3afe17629c073f38990298ac3cd569ee44a8b63 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Tue, 22 Aug 2017 00:51:46 +0200 Subject: [PATCH 297/364] Right align pixel size-based visibility DD button --- src/ui/qgstextformatwidgetbase.ui | 68 ++++++++++--------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/src/ui/qgstextformatwidgetbase.ui b/src/ui/qgstextformatwidgetbase.ui index 5bffe84e20f..ae1af6e92c4 100755 --- a/src/ui/qgstextformatwidgetbase.ui +++ b/src/ui/qgstextformatwidgetbase.ui @@ -651,7 +651,7 @@ - 0 + 6 @@ -5467,39 +5467,6 @@ font-style: italic; 6 - - - - - 0 - 0 - - - - Pixel size-based visibility (labels in map units) - - - - - - - - - - - - - - Qt::Horizontal - - - - 0 - 20 - - - - @@ -5600,6 +5567,26 @@ font-style: italic; + + + + + + + + + + + + 0 + 0 + + + + Pixel size-based visibility (labels in map units) + + + @@ -6067,19 +6054,6 @@ font-style: italic; - - - - Qt::Horizontal - - - - 40 - 20 - - - - From 2920c0128cb1f7a3f948a18fa609c754ede6ee9f Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Mon, 4 Sep 2017 11:43:32 +0200 Subject: [PATCH 298/364] translation string fix (cherry picked from commit 3943e7b9310714cbf2b2943669a8c8f56f3ed884) --- src/core/qgsvectorfilewriter.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index f5451903115..f20864c68ef 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -1781,8 +1781,10 @@ QMap QgsVectorFileWriter::initMetaData() layerOptions.insert( QStringLiteral( "OGR_XLSX_HEADERS" ), new SetOption( QObject::tr( "By default, the driver will read the first lines of each sheet to detect " - "if the first line might be the name of columns. If set to FORCE, the " - "driver will consider the first default" ), + "if the first line might be the name of columns. If set to FORCE, the driver " + "will consider the first line will be taken as the header line. If set to " + "DISABLE, it will be considered as the first feature. Otherwise " + "auto-detection will occur." ), QStringList() << QStringLiteral( "FORCE" ) << QStringLiteral( "DISABLE" ) @@ -1819,8 +1821,10 @@ QMap QgsVectorFileWriter::initMetaData() layerOptions.insert( QStringLiteral( "OGR_ODS_HEADERS" ), new SetOption( QObject::tr( "By default, the driver will read the first lines of each sheet to detect " - "if the first line might be the name of columns. If set to FORCE, the " - "driver will consider the first default" ), + "if the first line might be the name of columns. If set to FORCE, the driver " + "will consider the first line will be taken as the header line. If set to " + "DISABLE, it will be considered as the first feature. Otherwise " + "auto-detection will occur." ), QStringList() << QStringLiteral( "FORCE" ) << QStringLiteral( "DISABLE" ) From eb0d57072fd38228616502812d42f01400790823 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 13:12:02 +0200 Subject: [PATCH 299/364] Link gui --- src/providers/wms/CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/providers/wms/CMakeLists.txt b/src/providers/wms/CMakeLists.txt index 0a00cecd006..5001376d3db 100644 --- a/src/providers/wms/CMakeLists.txt +++ b/src/providers/wms/CMakeLists.txt @@ -64,17 +64,22 @@ TARGET_LINK_LIBRARIES(wmsprovider ${GDAL_LIBRARY} # for OGR_G_CreateGeometryFromJson() ) -IF (WITH_GUI) - TARGET_LINK_LIBRARIES (wmsprovider - qgis_gui - ) -ENDIF () TARGET_LINK_LIBRARIES(wmsprovider_a qgis_core ${QT_QTSCRIPT_LIBRARY} ) + +IF (WITH_GUI) + TARGET_LINK_LIBRARIES (wmsprovider + qgis_gui + ) + TARGET_LINK_LIBRARIES (wmsprovider_a + qgis_gui + ) +ENDIF () + INSTALL (TARGETS wmsprovider RUNTIME DESTINATION ${QGIS_PLUGIN_DIR} LIBRARY DESTINATION ${QGIS_PLUGIN_DIR}) From ea303771d1d8a16f02d1919267c47e8d6c486ed6 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Fri, 18 Aug 2017 23:27:47 +0200 Subject: [PATCH 300/364] Moving diagram placement options from combobox to radio buttons --- src/app/qgsdiagramproperties.cpp | 72 ++-- src/app/qgsdiagramproperties.h | 8 +- src/ui/qgsdiagrampropertiesbase.ui | 586 ++++++++++++++++++++--------- 3 files changed, 461 insertions(+), 205 deletions(-) diff --git a/src/app/qgsdiagramproperties.cpp b/src/app/qgsdiagramproperties.cpp index 425fd0ec5fe..5adeaf87aeb 100644 --- a/src/app/qgsdiagramproperties.cpp +++ b/src/app/qgsdiagramproperties.cpp @@ -59,6 +59,9 @@ QgsExpressionContext QgsDiagramProperties::createExpressionContext() const QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas ) : QWidget( parent ) + , mPlacePointBtnGrp( nullptr ) + , mPlaceLineBtnGrp( nullptr ) + , mPlacePolygonBtnGrp( nullptr ) , mMapCanvas( canvas ) { mLayer = layer; @@ -119,31 +122,52 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare mDiagramFrame->setEnabled( false ); } - //insert placement options - mPlacementComboBox->blockSignals( true ); - switch ( layerType ) + // set placement methods page based on geometry type + + switch ( mLayer->geometryType() ) { case QgsWkbTypes::PointGeometry: - mPlacementComboBox->addItem( tr( "Around Point" ), QgsDiagramLayerSettings::AroundPoint ); - mPlacementComboBox->addItem( tr( "Over Point" ), QgsDiagramLayerSettings::OverPoint ); + stackedPlacement->setCurrentWidget( pagePoint ); mLinePlacementFrame->setVisible( false ); break; case QgsWkbTypes::LineGeometry: - mPlacementComboBox->addItem( tr( "Around Line" ), QgsDiagramLayerSettings::Line ); - mPlacementComboBox->addItem( tr( "Over Line" ), QgsDiagramLayerSettings::Horizontal ); + stackedPlacement->setCurrentWidget( pageLine ); mLinePlacementFrame->setVisible( true ); break; case QgsWkbTypes::PolygonGeometry: - mPlacementComboBox->addItem( tr( "Around Centroid" ), QgsDiagramLayerSettings::AroundPoint ); - mPlacementComboBox->addItem( tr( "Over Centroid" ), QgsDiagramLayerSettings::OverPoint ); - mPlacementComboBox->addItem( tr( "Perimeter" ), QgsDiagramLayerSettings::Line ); - mPlacementComboBox->addItem( tr( "Inside Polygon" ), QgsDiagramLayerSettings::Horizontal ); + stackedPlacement->setCurrentWidget( pagePolygon ); mLinePlacementFrame->setVisible( false ); break; - default: + case QgsWkbTypes::NullGeometry: break; + case QgsWkbTypes::UnknownGeometry: + qFatal( "unknown geometry type unexpected" ); } - mPlacementComboBox->blockSignals( false ); + + //insert placement options + // setup point placement button group + mPlacePointBtnGrp = new QButtonGroup( this ); + mPlacePointBtnGrp->addButton( radAroundPoint, ( int )QgsDiagramLayerSettings::AroundPoint ); + mPlacePointBtnGrp->addButton( radOverPoint, ( int )QgsDiagramLayerSettings::OverPoint ); + mPlacePointBtnGrp->setExclusive( true ); + connect( mPlacePointBtnGrp, static_cast( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets ); + + // setup line placement button group (assigned enum id currently unused) + mPlaceLineBtnGrp = new QButtonGroup( this ); + mPlaceLineBtnGrp->addButton( radAroundLine, ( int )QgsDiagramLayerSettings::Line ); + mPlaceLineBtnGrp->addButton( radOverLine, ( int )QgsDiagramLayerSettings::Horizontal ); + mPlaceLineBtnGrp->setExclusive( true ); + connect( mPlaceLineBtnGrp, static_cast( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets ); + + // setup polygon placement button group (assigned enum id currently unused) + mPlacePolygonBtnGrp = new QButtonGroup( this ); + mPlacePolygonBtnGrp->addButton( radAroundCentroid, ( int )QgsDiagramLayerSettings::AroundPoint ); + mPlacePolygonBtnGrp->addButton( radOverCentroid, ( int )QgsDiagramLayerSettings::OverPoint ); + mPlacePolygonBtnGrp->addButton( radPolygonPerimeter, ( int )QgsDiagramLayerSettings::Line ); + mPlacePolygonBtnGrp->addButton( radInsidePolygon, ( int )QgsDiagramLayerSettings::Horizontal ); + mPlacePolygonBtnGrp->setExclusive( true ); + connect( mPlacePolygonBtnGrp, static_cast( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets ); + mLabelPlacementComboBox->addItem( tr( "Height" ), QgsDiagramSettings::Height ); mLabelPlacementComboBox->addItem( tr( "x-height" ), QgsDiagramSettings::XHeight ); @@ -216,17 +240,17 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare switch ( layerType ) { case QgsWkbTypes::PointGeometry: - mPlacementComboBox->setCurrentIndex( mPlacementComboBox->findData( QgsDiagramLayerSettings::AroundPoint ) ); + radAroundPoint->setChecked( true ); break; case QgsWkbTypes::LineGeometry: - mPlacementComboBox->setCurrentIndex( mPlacementComboBox->findData( QgsDiagramLayerSettings::Line ) ); + radAroundLine->setChecked( true ); chkLineAbove->setChecked( true ); chkLineBelow->setChecked( false ); chkLineOn->setChecked( false ); chkLineOrientationDependent->setChecked( false ); break; case QgsWkbTypes::PolygonGeometry: - mPlacementComboBox->setCurrentIndex( mPlacementComboBox->findData( QgsDiagramLayerSettings::AroundPoint ) ); + radAroundCentroid->setChecked( true ); break; case QgsWkbTypes::UnknownGeometry: case QgsWkbTypes::NullGeometry: @@ -821,7 +845,7 @@ void QgsDiagramProperties::apply() dls.setPriority( mPrioritySlider->value() ); dls.setZIndex( mZIndexSpinBox->value() ); dls.setShowAllDiagrams( mShowAllCheckBox->isChecked() ); - dls.setPlacement( ( QgsDiagramLayerSettings::Placement )mPlacementComboBox->currentData().toInt() ); + //dls.setPlacement( ( QgsDiagramLayerSettings::Placement )mPlacementComboBox->currentData().toInt() ); QgsDiagramLayerSettings::LinePlacementFlags flags = 0; if ( chkLineAbove->isChecked() ) @@ -907,22 +931,26 @@ void QgsDiagramProperties::on_mDiagramStackedWidget_currentChanged( int index ) mDiagramOptionsListWidget->blockSignals( false ); } -void QgsDiagramProperties::on_mPlacementComboBox_currentIndexChanged( int index ) +void QgsDiagramProperties::updatePlacementWidgets() { - QgsDiagramLayerSettings::Placement currentPlacement = ( QgsDiagramLayerSettings::Placement )mPlacementComboBox->itemData( index ).toInt(); - if ( currentPlacement == QgsDiagramLayerSettings::AroundPoint || - ( currentPlacement == QgsDiagramLayerSettings::Line && mLayer->geometryType() == QgsWkbTypes::LineGeometry ) ) + QWidget *curWdgt = stackedPlacement->currentWidget(); + + if ( ( curWdgt == pagePoint && radAroundPoint->isChecked() ) + || ( curWdgt == pageLine && radAroundLine->isChecked() ) + || ( curWdgt == pagePolygon && radAroundCentroid->isChecked() ) ) { mDiagramDistanceLabel->setEnabled( true ); mDiagramDistanceSpinBox->setEnabled( true ); + mDistanceDDBtn->setEnabled( true ); } else { mDiagramDistanceLabel->setEnabled( false ); mDiagramDistanceSpinBox->setEnabled( false ); + mDistanceDDBtn->setEnabled( false ); } - bool linePlacementEnabled = mLayer->geometryType() == QgsWkbTypes::LineGeometry && currentPlacement == QgsDiagramLayerSettings::Line; + bool linePlacementEnabled = mLayer->geometryType() == QgsWkbTypes::LineGeometry && ( curWdgt == pageLine && radAroundLine->isChecked() ); // && currentPlacement == QgsDiagramLayerSettings::Line; chkLineAbove->setEnabled( linePlacementEnabled ); chkLineBelow->setEnabled( linePlacementEnabled ); chkLineOn->setEnabled( linePlacementEnabled ); diff --git a/src/app/qgsdiagramproperties.h b/src/app/qgsdiagramproperties.h index d1748bcb9f1..2205ae309d8 100644 --- a/src/app/qgsdiagramproperties.h +++ b/src/app/qgsdiagramproperties.h @@ -50,13 +50,19 @@ class APP_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr void on_mEngineSettingsButton_clicked(); void showAddAttributeExpressionDialog(); void on_mDiagramStackedWidget_currentChanged( int index ); - void on_mPlacementComboBox_currentIndexChanged( int index ); + void updatePlacementWidgets(); void scalingTypeChanged(); void showSizeLegendDialog(); protected: QgsVectorLayer *mLayer = nullptr; + //! Point placement button group + QButtonGroup *mPlacePointBtnGrp = nullptr; + //! Line placement button group + QButtonGroup *mPlaceLineBtnGrp = nullptr; + //! Polygon placement button group + QButtonGroup *mPlacePolygonBtnGrp = nullptr; private: diff --git a/src/ui/qgsdiagrampropertiesbase.ui b/src/ui/qgsdiagrampropertiesbase.ui index 8a36c50b108..2440ac4cff4 100755 --- a/src/ui/qgsdiagrampropertiesbase.ui +++ b/src/ui/qgsdiagrampropertiesbase.ui @@ -538,7 +538,7 @@ 0 - -73 + 0 626 494 @@ -1357,153 +1357,7 @@ 0 - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - QFrame::NoFrame - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - Placement - - - - - - - Distance - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - Above line - - - true - - - - - - - Below line - - - - - - - - 0 - 0 - - - - On line - - - - - - - Line orientation dependent position - - - - - - - - - + Data defined @@ -1609,7 +1463,91 @@ - + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + Above line + + + true + + + + + + + Below line + + + + + + + + 0 + 0 + + + + On line + + + + + + + Line orientation dependent position + + + + + + + + + Priority @@ -1670,6 +1608,282 @@ + + + + QFrame::NoFrame + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Distance + + + + + + + + 0 + 0 + + + + + + + + Placement + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Sunken + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Labels are placed in an equal radius circle around point features. + + + Around Point + + + true + + + + + + + Labels are placed at a fixed offset from the point. + + + Over Point + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Over Line + + + + + + + + 0 + 0 + + + + Around Line + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Inside Polygon + + + + + + + + 0 + 0 + + + + Around Centroid + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Over Centroid + + + false + + + + + + + + 0 + 0 + + + + Using Perimeter + + + + + + + @@ -1713,8 +1927,8 @@ 0 0 - 642 - 421 + 106 + 161 @@ -1812,7 +2026,7 @@ QFrame::Plain - + 0 @@ -1932,8 +2146,8 @@ 0 0 - 642 - 421 + 341 + 81 @@ -2009,36 +2223,20 @@ 1 - QgsColorButton - QToolButton -
        qgscolorbutton.h
        + QgsCollapsibleGroupBox + QGroupBox +
        qgscollapsiblegroupbox.h
        1
        - - QgsDoubleSpinBox - QDoubleSpinBox -
        qgsdoublespinbox.h
        -
        - - QgsFontButton - QToolButton -
        qgsfontbutton.h
        -
        QgsSpinBox QSpinBox
        qgsspinbox.h
        - QgsPropertyOverrideButton - QToolButton -
        qgspropertyoverridebutton.h
        -
        - - QgsCollapsibleGroupBox - QGroupBox -
        qgscollapsiblegroupbox.h
        - 1 + QgsDoubleSpinBox + QDoubleSpinBox +
        qgsdoublespinbox.h
        QgsUnitSelectionWidget @@ -2046,23 +2244,39 @@
        qgsunitselectionwidget.h
        1
        + + QgsColorButton + QToolButton +
        qgscolorbutton.h
        + 1 +
        + + QgsPropertyOverrideButton + QToolButton +
        qgspropertyoverridebutton.h
        +
        QgsFieldExpressionWidget QWidget
        qgsfieldexpressionwidget.h
        1
        - - QgsScaleRangeWidget - QWidget -
        qgsscalerangewidget.h
        -
        QgsOpacityWidget QWidget
        qgsopacitywidget.h
        1
        + + QgsFontButton + QToolButton +
        qgsfontbutton.h
        +
        + + QgsScaleRangeWidget + QWidget +
        qgsscalerangewidget.h
        +
        mDiagramTypeComboBox @@ -2106,7 +2320,14 @@ mIncreaseSmallDiagramsCheck mIncreaseMinimumSizeSpinBox scrollArea_6 - mPlacementComboBox + radAroundPoint + radOverPoint + radAroundLine + radOverLine + radAroundCentroid + radInsidePolygon + radOverCentroid + radPolygonPerimeter mDiagramDistanceSpinBox mDistanceDDBtn chkLineAbove @@ -2125,6 +2346,7 @@ scrollArea_2 mCheckBoxAttributeLegend mButtonSizeLegendSettings + mPlacementComboBox From e677e1f4ed0d734445cbc2e5a212ca9e3f125776 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Sat, 2 Sep 2017 22:43:04 +0200 Subject: [PATCH 301/364] Implement logic to use radio buttons for diagram placement --- src/app/qgsdiagramproperties.cpp | 85 ++++++++++++++++++++++++------ src/app/qgsdiagramproperties.h | 4 +- src/ui/qgsdiagrampropertiesbase.ui | 28 ++-------- 3 files changed, 75 insertions(+), 42 deletions(-) diff --git a/src/app/qgsdiagramproperties.cpp b/src/app/qgsdiagramproperties.cpp index 5adeaf87aeb..b83155b5f2a 100644 --- a/src/app/qgsdiagramproperties.cpp +++ b/src/app/qgsdiagramproperties.cpp @@ -124,7 +124,7 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare // set placement methods page based on geometry type - switch ( mLayer->geometryType() ) + switch ( layerType ) { case QgsWkbTypes::PointGeometry: stackedPlacement->setCurrentWidget( pagePoint ); @@ -139,32 +139,31 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare mLinePlacementFrame->setVisible( false ); break; case QgsWkbTypes::NullGeometry: - break; case QgsWkbTypes::UnknownGeometry: - qFatal( "unknown geometry type unexpected" ); + break; } //insert placement options // setup point placement button group mPlacePointBtnGrp = new QButtonGroup( this ); - mPlacePointBtnGrp->addButton( radAroundPoint, ( int )QgsDiagramLayerSettings::AroundPoint ); - mPlacePointBtnGrp->addButton( radOverPoint, ( int )QgsDiagramLayerSettings::OverPoint ); + mPlacePointBtnGrp->addButton( radAroundPoint ); + mPlacePointBtnGrp->addButton( radOverPoint ); mPlacePointBtnGrp->setExclusive( true ); connect( mPlacePointBtnGrp, static_cast( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets ); - // setup line placement button group (assigned enum id currently unused) + // setup line placement button group mPlaceLineBtnGrp = new QButtonGroup( this ); - mPlaceLineBtnGrp->addButton( radAroundLine, ( int )QgsDiagramLayerSettings::Line ); - mPlaceLineBtnGrp->addButton( radOverLine, ( int )QgsDiagramLayerSettings::Horizontal ); + mPlaceLineBtnGrp->addButton( radAroundLine ); + mPlaceLineBtnGrp->addButton( radOverLine ); mPlaceLineBtnGrp->setExclusive( true ); connect( mPlaceLineBtnGrp, static_cast( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets ); - // setup polygon placement button group (assigned enum id currently unused) + // setup polygon placement button group mPlacePolygonBtnGrp = new QButtonGroup( this ); - mPlacePolygonBtnGrp->addButton( radAroundCentroid, ( int )QgsDiagramLayerSettings::AroundPoint ); - mPlacePolygonBtnGrp->addButton( radOverCentroid, ( int )QgsDiagramLayerSettings::OverPoint ); - mPlacePolygonBtnGrp->addButton( radPolygonPerimeter, ( int )QgsDiagramLayerSettings::Line ); - mPlacePolygonBtnGrp->addButton( radInsidePolygon, ( int )QgsDiagramLayerSettings::Horizontal ); + mPlacePolygonBtnGrp->addButton( radAroundCentroid ); + mPlacePolygonBtnGrp->addButton( radOverCentroid ); + mPlacePolygonBtnGrp->addButton( radPolygonPerimeter ); + mPlacePolygonBtnGrp->addButton( radInsidePolygon ); mPlacePolygonBtnGrp->setExclusive( true ); connect( mPlacePolygonBtnGrp, static_cast( &QButtonGroup::buttonClicked ), this, &QgsDiagramProperties::updatePlacementWidgets ); @@ -242,6 +241,7 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare case QgsWkbTypes::PointGeometry: radAroundPoint->setChecked( true ); break; + case QgsWkbTypes::LineGeometry: radAroundLine->setChecked( true ); chkLineAbove->setChecked( true ); @@ -249,9 +249,11 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare chkLineOn->setChecked( false ); chkLineOrientationDependent->setChecked( false ); break; + case QgsWkbTypes::PolygonGeometry: radAroundCentroid->setChecked( true ); break; + case QgsWkbTypes::UnknownGeometry: case QgsWkbTypes::NullGeometry: break; @@ -394,13 +396,39 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *pare mDiagramDistanceSpinBox->setValue( dls->distance() ); mPrioritySlider->setValue( dls->priority() ); mZIndexSpinBox->setValue( dls->zIndex() ); - mPlacementComboBox->setCurrentIndex( mPlacementComboBox->findData( dls->placement() ) ); + + switch ( dls->placement() ) + { + case QgsDiagramLayerSettings::AroundPoint: + radAroundPoint->setChecked( true ); + radAroundCentroid->setChecked( true ); + break; + + case QgsDiagramLayerSettings::OverPoint: + radOverPoint->setChecked( true ); + radOverCentroid->setChecked( true ); + break; + + case QgsDiagramLayerSettings::Line: + radAroundLine->setChecked( true ); + radPolygonPerimeter->setChecked( true ); + break; + + case QgsDiagramLayerSettings::Horizontal: + radOverLine->setChecked( true ); + radInsidePolygon->setChecked( true ); + break; + + default: + break; + } chkLineAbove->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::AboveLine ); chkLineBelow->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::BelowLine ); chkLineOn->setChecked( dls->linePlacementFlags() & QgsDiagramLayerSettings::OnLine ); if ( !( dls->linePlacementFlags() & QgsDiagramLayerSettings::MapOrientation ) ) chkLineOrientationDependent->setChecked( true ); + updatePlacementWidgets(); mShowAllCheckBox->setChecked( dls->showAllDiagrams() ); @@ -845,7 +873,32 @@ void QgsDiagramProperties::apply() dls.setPriority( mPrioritySlider->value() ); dls.setZIndex( mZIndexSpinBox->value() ); dls.setShowAllDiagrams( mShowAllCheckBox->isChecked() ); - //dls.setPlacement( ( QgsDiagramLayerSettings::Placement )mPlacementComboBox->currentData().toInt() ); + + QWidget *curWdgt = stackedPlacement->currentWidget(); + if ( ( curWdgt == pagePoint && radAroundPoint->isChecked() ) + || ( curWdgt == pagePolygon && radAroundCentroid->isChecked() ) ) + { + dls.setPlacement( QgsDiagramLayerSettings::AroundPoint ); + } + else if ( ( curWdgt == pagePoint && radOverPoint->isChecked() ) + || ( curWdgt == pagePolygon && radOverCentroid->isChecked() ) ) + { + dls.setPlacement( QgsDiagramLayerSettings::OverPoint ); + } + else if ( ( curWdgt == pageLine && radAroundLine->isChecked() ) + || ( curWdgt == pagePolygon && radPolygonPerimeter->isChecked() ) ) + { + dls.setPlacement( QgsDiagramLayerSettings::Line ); + } + else if ( ( curWdgt == pageLine && radOverLine->isChecked() ) + || ( curWdgt == pagePolygon && radInsidePolygon->isChecked() ) ) + { + dls.setPlacement( QgsDiagramLayerSettings::Horizontal ); + } + else + { + qFatal( "Invalid settings" ); + } QgsDiagramLayerSettings::LinePlacementFlags flags = 0; if ( chkLineAbove->isChecked() ) @@ -950,7 +1003,7 @@ void QgsDiagramProperties::updatePlacementWidgets() mDistanceDDBtn->setEnabled( false ); } - bool linePlacementEnabled = mLayer->geometryType() == QgsWkbTypes::LineGeometry && ( curWdgt == pageLine && radAroundLine->isChecked() ); // && currentPlacement == QgsDiagramLayerSettings::Line; + bool linePlacementEnabled = mLayer->geometryType() == QgsWkbTypes::LineGeometry && ( curWdgt == pageLine && radAroundLine->isChecked() ); chkLineAbove->setEnabled( linePlacementEnabled ); chkLineBelow->setEnabled( linePlacementEnabled ); chkLineOn->setEnabled( linePlacementEnabled ); diff --git a/src/app/qgsdiagramproperties.h b/src/app/qgsdiagramproperties.h index 2205ae309d8..330f561c26d 100644 --- a/src/app/qgsdiagramproperties.h +++ b/src/app/qgsdiagramproperties.h @@ -54,7 +54,7 @@ class APP_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr void scalingTypeChanged(); void showSizeLegendDialog(); - protected: + private: QgsVectorLayer *mLayer = nullptr; //! Point placement button group @@ -64,8 +64,6 @@ class APP_EXPORT QgsDiagramProperties : public QWidget, private Ui::QgsDiagramPr //! Polygon placement button group QButtonGroup *mPlacePolygonBtnGrp = nullptr; - private: - enum Columns { ColumnAttributeExpression = 0, diff --git a/src/ui/qgsdiagrampropertiesbase.ui b/src/ui/qgsdiagrampropertiesbase.ui index 2440ac4cff4..5665a8a10f2 100755 --- a/src/ui/qgsdiagrampropertiesbase.ui +++ b/src/ui/qgsdiagrampropertiesbase.ui @@ -1626,31 +1626,14 @@ 0 - + Distance - - - - - 0 - 0 - - - - - - - - Placement - - - - + @@ -1677,7 +1660,7 @@ - + 0 0 @@ -1927,8 +1910,8 @@ 0 0 - 106 - 161 + 642 + 421 @@ -2346,7 +2329,6 @@ scrollArea_2 mCheckBoxAttributeLegend mButtonSizeLegendSettings - mPlacementComboBox From 399c2ca0dc1856a709e9e807b536ed33f6706231 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Mon, 4 Sep 2017 10:47:56 +0200 Subject: [PATCH 302/364] Removing placement button groups declaration --- src/app/qgsdiagramproperties.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/qgsdiagramproperties.cpp b/src/app/qgsdiagramproperties.cpp index b83155b5f2a..a9310014108 100644 --- a/src/app/qgsdiagramproperties.cpp +++ b/src/app/qgsdiagramproperties.cpp @@ -59,9 +59,6 @@ QgsExpressionContext QgsDiagramProperties::createExpressionContext() const QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer *layer, QWidget *parent, QgsMapCanvas *canvas ) : QWidget( parent ) - , mPlacePointBtnGrp( nullptr ) - , mPlaceLineBtnGrp( nullptr ) - , mPlacePolygonBtnGrp( nullptr ) , mMapCanvas( canvas ) { mLayer = layer; From 2d073d6fafeffda8a8009367cde79591b396e3f5 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 15:11:57 +0200 Subject: [PATCH 303/364] Remove all hardcoded source selects: rely on the registry The data source manager dialog is now clean from hardcoded provider keys and there is a mean to add items to the dialog via plugins (C++, providers and Python). --- src/gui/qgsdatasourcemanagerdialog.cpp | 82 +++++++-------------- src/gui/qgsdatasourcemanagerdialog.h | 3 +- src/gui/qgssourceselectprovider.h | 4 +- src/gui/qgssourceselectproviderregistry.cpp | 6 +- src/gui/qgssourceselectproviderregistry.h | 11 +-- 5 files changed, 42 insertions(+), 64 deletions(-) diff --git a/src/gui/qgsdatasourcemanagerdialog.cpp b/src/gui/qgsdatasourcemanagerdialog.cpp index e0d7b227047..164b81bad11 100644 --- a/src/gui/qgsdatasourcemanagerdialog.cpp +++ b/src/gui/qgsdatasourcemanagerdialog.cpp @@ -22,8 +22,11 @@ #include "qgsbrowserdockwidget.h" #include "qgssettings.h" #include "qgsproviderregistry.h" +#include "qgssourceselectprovider.h" +#include "qgssourceselectproviderregistry.h" #include "qgsabstractdatasourcewidget.h" #include "qgsmapcanvas.h" +#include "qgsgui.h" QgsDataSourceManagerDialog::QgsDataSourceManagerDialog( QWidget *parent, QgsMapCanvas *canvas, Qt::WindowFlags fl ) : QgsOptionsDialogBase( QStringLiteral( "Data Source Manager" ), parent, fl ), @@ -55,39 +58,18 @@ QgsDataSourceManagerDialog::QgsDataSourceManagerDialog( QWidget *parent, QgsMapC connect( mBrowserWidget, &QgsBrowserDockWidget::connectionsChanged, this, &QgsDataSourceManagerDialog::connectionsChanged ); connect( this, &QgsDataSourceManagerDialog::updateProjectHome, mBrowserWidget, &QgsBrowserDockWidget::updateProjectHome ); - // Add data provider dialogs - - providerDialog( QStringLiteral( "ogr" ), tr( "Vector" ), QStringLiteral( "/mActionAddOgrLayer.svg" ) ); - - providerDialog( QStringLiteral( "gdal" ), tr( "Raster" ), QStringLiteral( "/mActionAddRasterLayer.svg" ) ); - - providerDialog( QStringLiteral( "delimitedtext" ), tr( "Delimited Text" ), QStringLiteral( "/mActionAddDelimitedTextLayer.svg" ) ); - -#ifdef HAVE_POSTGRESQL - providerDialog( QStringLiteral( "postgres" ), tr( "PostgreSQL" ), QStringLiteral( "/mActionAddPostgisLayer.svg" ) ); -#endif - - providerDialog( QStringLiteral( "spatialite" ), tr( "SpatiaLite" ), QStringLiteral( "/mActionAddSpatiaLiteLayer.svg" ) ); - - providerDialog( QStringLiteral( "mssql" ), tr( "MSSQL" ), QStringLiteral( "/mActionAddMssqlLayer.svg" ) ); - - providerDialog( QStringLiteral( "DB2" ), tr( "DB2" ), QStringLiteral( "/mActionAddDb2Layer.svg" ) ); - -#ifdef HAVE_ORACLE - providerDialog( QStringLiteral( "oracle" ), tr( "Oracle" ), QStringLiteral( "/mActionAddOracleLayer.svg" ) ); -#endif - - providerDialog( QStringLiteral( "virtual" ), tr( "Virtual Layer" ), QStringLiteral( "/mActionAddVirtualLayer.svg" ) ); - - providerDialog( QStringLiteral( "wms" ), tr( "WMS" ), QStringLiteral( "/mActionAddWmsLayer.svg" ) ); - - providerDialog( QStringLiteral( "wcs" ), tr( "WCS" ), QStringLiteral( "/mActionAddWcsLayer.svg" ) ); - - providerDialog( QStringLiteral( "WFS" ), tr( "WFS" ), QStringLiteral( "/mActionAddWfsLayer.svg" ) ); - - providerDialog( QStringLiteral( "arcgismapserver" ), tr( "ArcGIS Map Server" ), QStringLiteral( "/mActionAddAmsLayer.svg" ) ); - - providerDialog( QStringLiteral( "arcgisfeatureserver" ), tr( "ArcGIS Feature Server" ), QStringLiteral( "/mActionAddAfsLayer.svg" ) ); + // Add provider dialogs + const QList sourceSelectProviders = QgsGui::sourceSelectProviderRegistry()->providers( ) ; + for ( const auto provider : sourceSelectProviders ) + { + QgsAbstractDataSourceWidget *dlg = provider->createDataSourceWidget( this ); + if ( !dlg ) + { + QMessageBox::warning( this, provider->name(), tr( "Cannot get %1 select dialog from source select provider %2." ).arg( provider->name(), provider->providerKey() ) ); + continue; + } + addProviderDialog( dlg, provider->providerKey(), provider->text(), provider->icon( ) ); + } } @@ -140,31 +122,21 @@ void QgsDataSourceManagerDialog::vectorLayersAdded( const QStringList &layerQStr } -QgsAbstractDataSourceWidget *QgsDataSourceManagerDialog::providerDialog( const QString providerKey, const QString providerName, const QString icon, QString title ) +void QgsDataSourceManagerDialog::addProviderDialog( QgsAbstractDataSourceWidget *dlg, const QString &providerKey, const QString &providerName, const QIcon &icon, QString toolTip ) { - QgsAbstractDataSourceWidget *dlg = dynamic_cast( QgsProviderRegistry::instance()->createSelectionWidget( providerKey, this, Qt::Widget, QgsProviderRegistry::WidgetMode::Embedded ) ); - if ( !dlg ) + mPageNames.append( providerKey ); + ui->mOptionsStackedWidget->addWidget( dlg ); + QListWidgetItem *layerItem = new QListWidgetItem( providerName, ui->mOptionsListWidget ); + layerItem->setToolTip( toolTip.isEmpty() ? tr( "Add %1 layer" ).arg( providerName ) : toolTip ); + layerItem->setIcon( icon ); + // Set crs and extent from canvas + if ( mMapCanvas ) { - QMessageBox::warning( this, providerName, tr( "Cannot get %1 select dialog from provider %2." ).arg( providerName, providerKey ) ); - return nullptr; - } - else - { - mPageNames.append( providerKey ); - ui->mOptionsStackedWidget->addWidget( dlg ); - QListWidgetItem *layerItem = new QListWidgetItem( providerName, ui->mOptionsListWidget ); - layerItem->setToolTip( title.isEmpty() ? tr( "Add %1 layer" ).arg( providerName ) : title ); - layerItem->setIcon( QgsApplication::getThemeIcon( icon ) ); - // Set crs and extent from canvas - if ( mMapCanvas ) - { - dlg->setMapCanvas( mMapCanvas ); - } - connect( dlg, &QgsAbstractDataSourceWidget::rejected, this, &QgsDataSourceManagerDialog::reject ); - connect( dlg, &QgsAbstractDataSourceWidget::accepted, this, &QgsDataSourceManagerDialog::accept ); - makeConnections( dlg, providerKey ); - return dlg; + dlg->setMapCanvas( mMapCanvas ); } + connect( dlg, &QgsAbstractDataSourceWidget::rejected, this, &QgsDataSourceManagerDialog::reject ); + connect( dlg, &QgsAbstractDataSourceWidget::accepted, this, &QgsDataSourceManagerDialog::accept ); + makeConnections( dlg, providerKey ); } void QgsDataSourceManagerDialog::makeConnections( QgsAbstractDataSourceWidget *dlg, const QString &providerKey ) diff --git a/src/gui/qgsdatasourcemanagerdialog.h b/src/gui/qgsdatasourcemanagerdialog.h index 5feaf3058b6..222008ce853 100644 --- a/src/gui/qgsdatasourcemanagerdialog.h +++ b/src/gui/qgsdatasourcemanagerdialog.h @@ -114,8 +114,7 @@ class GUI_EXPORT QgsDataSourceManagerDialog : public QgsOptionsDialogBase, priva void providerDialogsRefreshRequested(); private: - // Return the dialog from the provider - QgsAbstractDataSourceWidget *providerDialog( const QString providerKey, const QString providerName, const QString icon, QString title = QString() ); + void addProviderDialog( QgsAbstractDataSourceWidget *dlg, const QString &providerKey, const QString &providerName, const QIcon &icon, QString toolTip = QString() ); void makeConnections( QgsAbstractDataSourceWidget *dlg, const QString &providerKey ); Ui::QgsDataSourceManagerDialog *ui; QgsBrowserDockWidget *mBrowserWidget = nullptr; diff --git a/src/gui/qgssourceselectprovider.h b/src/gui/qgssourceselectprovider.h index eeb12799184..bca6549340b 100644 --- a/src/gui/qgssourceselectprovider.h +++ b/src/gui/qgssourceselectprovider.h @@ -19,6 +19,8 @@ #include "qgis_gui.h" #include "qgis.h" +#include "qgsguiutils.h" +#include "qgsproviderregistry.h" #include "qgsabstractdatasourcewidget.h" class QString; @@ -61,7 +63,7 @@ class GUI_EXPORT QgsSourceSelectProvider /** Create a new instance of QgsAbstractDataSourceWidget (or null). * Caller takes responsibility of deleting created. */ - virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr ) const = 0 SIP_FACTORY; + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const = 0 SIP_FACTORY; }; diff --git a/src/gui/qgssourceselectproviderregistry.cpp b/src/gui/qgssourceselectproviderregistry.cpp index b57b9c22a59..1fd0ceb9b4d 100644 --- a/src/gui/qgssourceselectproviderregistry.cpp +++ b/src/gui/qgssourceselectproviderregistry.cpp @@ -47,11 +47,15 @@ void QgsSourceSelectProviderRegistry::addProvider( QgsSourceSelectProvider *prov } ); } -void QgsSourceSelectProviderRegistry::removeProvider( QgsSourceSelectProvider *provider ) +bool QgsSourceSelectProviderRegistry::removeProvider( QgsSourceSelectProvider *provider ) { int index = mProviders.indexOf( provider ); if ( index >= 0 ) + { delete mProviders.takeAt( index ); + return true; + } + return false; } QgsSourceSelectProvider *QgsSourceSelectProviderRegistry::providerByName( const QString &name ) diff --git a/src/gui/qgssourceselectproviderregistry.h b/src/gui/qgssourceselectproviderregistry.h index ea24fda8352..76cd59b14f2 100644 --- a/src/gui/qgssourceselectproviderregistry.h +++ b/src/gui/qgssourceselectproviderregistry.h @@ -49,16 +49,17 @@ class GUI_EXPORT QgsSourceSelectProviderRegistry //! Get list of available providers QList< QgsSourceSelectProvider *> providers(); - //! Add a provider implementation. Takes ownership of the object. + //! Add a \a provider implementation. Takes ownership of the object. void addProvider( QgsSourceSelectProvider *provider SIP_TRANSFER ); - //! Remove provider implementation from the list (provider object is deleted) - void removeProvider( QgsSourceSelectProvider *provider ); + //! Remove \a provider implementation from the list (\a provider object is deleted) + //! \returns true if the provider was actually removed and deleted + bool removeProvider( QgsSourceSelectProvider *provider SIP_TRANSFER ); - //! Return a provider by name or nullptr if not found + //! Return a provider by \a name or nullptr if not found QgsSourceSelectProvider *providerByName( const QString &name ); - //! Return a (possibly empty) list of providers by data provider's key + //! Return a (possibly empty) list of providers by data \a providerkey QList providersByKey( const QString &providerKey ); From 50e0a01008793b6a7713db98194be6545034119a Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 15:15:20 +0200 Subject: [PATCH 304/364] Sip updates for source select provider and registry --- python/gui/qgssourceselectprovider.sip | 2 +- python/gui/qgssourceselectproviderregistry.sip | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/python/gui/qgssourceselectprovider.sip b/python/gui/qgssourceselectprovider.sip index e6bcfcdf1e4..231afb286ce 100644 --- a/python/gui/qgssourceselectprovider.sip +++ b/python/gui/qgssourceselectprovider.sip @@ -59,7 +59,7 @@ Text for the menu item entry, it will be visible to the user so make sure it's t :rtype: int %End - virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = 0 ) const = 0 /Factory/; + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = 0, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const = 0 /Factory/; %Docstring Create a new instance of QgsAbstractDataSourceWidget (or null). Caller takes responsibility of deleting created. diff --git a/python/gui/qgssourceselectproviderregistry.sip b/python/gui/qgssourceselectproviderregistry.sip index 24ef8d8c206..617480c0350 100644 --- a/python/gui/qgssourceselectproviderregistry.sip +++ b/python/gui/qgssourceselectproviderregistry.sip @@ -43,23 +43,24 @@ Get list of available providers void addProvider( QgsSourceSelectProvider *provider /Transfer/ ); %Docstring -Add a provider implementation. Takes ownership of the object. +Add a ``provider`` implementation. Takes ownership of the object. %End - void removeProvider( QgsSourceSelectProvider *provider ); + bool removeProvider( QgsSourceSelectProvider *provider /Transfer/ ); %Docstring -Remove provider implementation from the list (provider object is deleted) +:return: true if the provider was actually removed and deleted + :rtype: bool %End QgsSourceSelectProvider *providerByName( const QString &name ); %Docstring -Return a provider by name or None if not found +Return a provider by ``name`` or None if not found :rtype: QgsSourceSelectProvider %End QList providersByKey( const QString &providerKey ); %Docstring -Return a (possibly empty) list of providers by data provider's key +Return a (possibly empty) list of providers by data ``providerkey`` :rtype: list of QgsSourceSelectProvider %End From fd7bcb368dca400e85ef2a3ec1c15eedeef0a7c7 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 15:16:20 +0200 Subject: [PATCH 305/364] Updates tests for source select registry --- tests/src/python/test_qgssourceselectprovider.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/src/python/test_qgssourceselectprovider.py b/tests/src/python/test_qgssourceselectprovider.py index f73519d434c..61ee975b74e 100644 --- a/tests/src/python/test_qgssourceselectprovider.py +++ b/tests/src/python/test_qgssourceselectprovider.py @@ -120,6 +120,18 @@ class TestQgsSourceSelectProvider(unittest.TestCase): # Get not existent by key self.assertEqual(len(registry.providersByKey('Oh This Is Missing!')), 0) + def testRemoveProvider(self): + registry = QgsSourceSelectProviderRegistry() + registry.addProvider(ConcreteSourceSelectProvider()) + registry.addProvider(ConcreteSourceSelectProvider2()) + self.assertEqual(['MyTestProviderKey', 'MyName'], [p.name() for p in registry.providers() if p.providerKey().startswith('MyTestProviderKey')]) + + self.assertTrue(registry.removeProvider(registry.providerByName('MyName'))) + self.assertEqual(['MyTestProviderKey'], [p.name() for p in registry.providers() if p.providerKey().startswith('MyTestProviderKey')]) + + self.assertTrue(registry.removeProvider(registry.providerByName('MyTestProviderKey'))) + self.assertEqual([], [p.name() for p in registry.providers() if p.providerKey().startswith('MyTestProviderKey')]) + def testRegistry(self): registry = QgsSourceSelectProviderRegistry() self._testRegistry(registry) @@ -127,6 +139,9 @@ class TestQgsSourceSelectProvider(unittest.TestCase): def testRegistrySingleton(self): registry = QgsGui.sourceSelectProviderRegistry() self._testRegistry(registry) + # Check that at least OGR and GDAL are here + self.assertTrue(registry.providersByKey('ogr')) + self.assertTrue(registry.providersByKey('gdal')) if __name__ == '__main__': From d47fdbc79cc7fc4dfd2188d218332d759c831c78 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 15:16:39 +0200 Subject: [PATCH 306/364] Implement sourceSelectProviders() for all providers --- src/providers/arcgisrest/qgsafsprovider.cpp | 35 +++++++++++++++++ src/providers/arcgisrest/qgsamsprovider.cpp | 34 +++++++++++++++++ src/providers/arcgisrest/qgsamssourceselect.h | 2 +- src/providers/db2/qgsdb2provider.cpp | 32 ++++++++++++++++ .../qgsdelimitedtextprovider.cpp | 32 ++++++++++++++++ src/providers/gdal/qgsgdalprovider.cpp | 36 ++++++++++++++++++ src/providers/mssql/qgsmssqlprovider.cpp | 32 ++++++++++++++++ src/providers/ogr/qgsogrprovider.cpp | 12 +++++- src/providers/oracle/qgsoracleprovider.cpp | 35 ++++++++++++++++- .../postgres/qgspostgresprovider.cpp | 31 +++++++++++++++ .../spatialite/qgsspatialiteprovider.cpp | 33 ++++++++++++++++ .../virtual/qgsvirtuallayerprovider.cpp | 35 +++++++++++++++++ src/providers/wcs/qgswcsprovider.cpp | 34 +++++++++++++++++ src/providers/wfs/qgswfsprovider.cpp | 34 +++++++++++++++++ src/providers/wms/qgswmsprovider.cpp | 38 +++++++++++++++++++ 15 files changed, 451 insertions(+), 4 deletions(-) diff --git a/src/providers/arcgisrest/qgsafsprovider.cpp b/src/providers/arcgisrest/qgsafsprovider.cpp index 29a8d43c25c..90fd8a059a6 100644 --- a/src/providers/arcgisrest/qgsafsprovider.cpp +++ b/src/providers/arcgisrest/qgsafsprovider.cpp @@ -23,6 +23,11 @@ #include "geometry/qgsgeometry.h" #include "qgsnetworkaccessmanager.h" +#ifdef HAVE_GUI +#include "qgsafssourceselect.h" +#include "qgssourceselectprovider.h" +#endif + #include #include #include @@ -196,3 +201,33 @@ void QgsAfsProvider::reloadData() { mSharedData->mCache.clear(); } + + +#ifdef HAVE_GUI + +//! Provider for AFS layers source select +class QgsAfsSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "arcgisfeatureserver" ); } + virtual QString text() const override { return QObject::tr( "ArcGIS Feature Server" ); } + virtual int ordering() const override { return 140; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddAfsLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsAfsSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsAfsSourceSelectProvider; + + return providers; +} +#endif diff --git a/src/providers/arcgisrest/qgsamsprovider.cpp b/src/providers/arcgisrest/qgsamsprovider.cpp index c97676e4f2c..6beb42553eb 100644 --- a/src/providers/arcgisrest/qgsamsprovider.cpp +++ b/src/providers/arcgisrest/qgsamsprovider.cpp @@ -24,6 +24,11 @@ #include "qgsfeaturestore.h" #include "qgsgeometry.h" +#ifdef HAVE_GUI +#include "qgsamssourceselect.h" +#include "qgssourceselectprovider.h" +#endif + #include #include #include @@ -449,3 +454,32 @@ void QgsAmsProvider::readBlock( int /*bandNo*/, const QgsRectangle &viewExtent, } std::memcpy( data, mCachedImage.constBits(), mCachedImage.bytesPerLine() * mCachedImage.height() ); } + +#ifdef HAVE_GUI + +//! Provider for AMS layers source select +class QgsAmsSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "arcgismapserver" ); } + virtual QString text() const override { return QObject::tr( "ArcGIS Map Server" ); } + virtual int ordering() const override { return 130; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddAmsLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsAmsSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsAmsSourceSelectProvider; + + return providers; +} +#endif diff --git a/src/providers/arcgisrest/qgsamssourceselect.h b/src/providers/arcgisrest/qgsamssourceselect.h index c97d134a22a..3f42ae6aaa6 100644 --- a/src/providers/arcgisrest/qgsamssourceselect.h +++ b/src/providers/arcgisrest/qgsamssourceselect.h @@ -28,7 +28,7 @@ class QgsAmsSourceSelect: public QgsArcGisServiceSourceSelect Q_OBJECT public: - QgsAmsSourceSelect( QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::None ); + QgsAmsSourceSelect( QWidget *parent = nullptr, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::None ); protected: bool connectToService( const QgsOwsConnection &connection ) override; diff --git a/src/providers/db2/qgsdb2provider.cpp b/src/providers/db2/qgsdb2provider.cpp index ad6ab4b412d..9fa6911ea71 100644 --- a/src/providers/db2/qgsdb2provider.cpp +++ b/src/providers/db2/qgsdb2provider.cpp @@ -27,6 +27,7 @@ #ifdef HAVE_GUI #include "qgsdb2sourceselect.h" +#include "qgssourceselectprovider.h" #endif static const QString PROVIDER_KEY = QStringLiteral( "DB2" ); @@ -1748,3 +1749,34 @@ QGISEXTERN QgsVectorLayerExporter::ExportError createEmptyLayer( oldToNewAttrIdxMap, errorMessage, options ); } + + +#ifdef HAVE_GUI + +//! Provider for DB2 source select +class QgsDb2SourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "DB2" ); } + virtual QString text() const override { return QObject::tr( "DB2" ); } + virtual int ordering() const override { return 70; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddDb2Layer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsDb2SourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsDb2SourceSelectProvider; + + return providers; +} + +#endif diff --git a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp index 35a4285bda2..492bcb8a01d 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp @@ -41,6 +41,9 @@ #include "qgsspatialindex.h" #include "qgis.h" #include "qgsproviderregistry.h" +#ifdef HAVE_GUI +#include "qgssourceselectprovider.h" +#endif #include "qgsdelimitedtextfeatureiterator.h" #include "qgsdelimitedtextfile.h" @@ -1196,4 +1199,33 @@ QGISEXTERN QgsDelimitedTextSourceSelect *selectWidget( QWidget *parent, Qt::Wind { return new QgsDelimitedTextSourceSelect( parent, fl, widgetMode ); } + +//! Provider for delimited text source select +class QgsDelimitedTextSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "delimitedtext" ); } + virtual QString text() const override { return QObject::tr( "Delimited Text" ); } + virtual int ordering() const override { return 30; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddDelimitedTextLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsDelimitedTextSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsDelimitedTextSourceSelectProvider; + + return providers; +} + #endif + + diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 99770ecf33c..39cdd6cf49d 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -35,6 +35,11 @@ #include "qgspointxy.h" #include "qgssettings.h" +#ifdef HAVE_GUI +#include "qgssourceselectprovider.h" +#include "qgsgdalsourceselect.h" +#endif + #include #include #include @@ -3041,3 +3046,34 @@ QGISEXTERN void cleanupProvider() // nothing to do here, QgsApplication takes care of // calling GDALDestroyDriverManager() } + + +#ifdef HAVE_GUI + +//! Provider for gdal raster source select +class QgsGdalRasterSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "gdal" ); } + virtual QString text() const override { return QObject::tr( "Raster" ); } + virtual int ordering() const override { return 20; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddRasterLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsGdalSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsGdalRasterSourceSelectProvider; + + return providers; +} + +#endif diff --git a/src/providers/mssql/qgsmssqlprovider.cpp b/src/providers/mssql/qgsmssqlprovider.cpp index 325f30f78a8..97a818a1df0 100644 --- a/src/providers/mssql/qgsmssqlprovider.cpp +++ b/src/providers/mssql/qgsmssqlprovider.cpp @@ -49,6 +49,7 @@ #ifdef HAVE_GUI #include "qgsmssqlsourceselect.h" +#include "qgssourceselectprovider.h" #endif static const QString TEXT_PROVIDER_KEY = QStringLiteral( "mssql" ); @@ -2307,3 +2308,34 @@ QGISEXTERN QString getStyleById( const QString &uri, QString styleId, QString &e } return style; } + + +#ifdef HAVE_GUI + +//! Provider for msssql raster source select +class QgsMssqlSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "mssql" ); } + virtual QString text() const override { return QObject::tr( "MSSQL" ); } + virtual int ordering() const override { return 60; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddMssqlLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsMssqlSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsMssqlSourceSelectProvider ; + + return providers; +} + +#endif diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 701046fbd4a..221c538f361 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -35,8 +35,12 @@ email : sherman at mrcc.com #include "qgsogrdataitems.h" #include "qgsgeopackagedataitems.h" #include "qgswkbtypes.h" + +#ifdef HAVE_GUI #include "qgssourceselectprovider.h" #include "qgsogrsourceselect.h" +#endif + #include "qgis.h" @@ -4349,6 +4353,7 @@ QGISEXTERN bool deleteLayer( const QString &uri, QString &errCause ) return false; } +#ifdef HAVE_GUI //! Provider for OGR vector source select class QgsOgrVectorSourceSelectProvider : public QgsSourceSelectProvider @@ -4357,10 +4362,11 @@ class QgsOgrVectorSourceSelectProvider : public QgsSourceSelectProvider virtual QString providerKey() const override { return QStringLiteral( "ogr" ); } virtual QString text() const override { return QObject::tr( "Vector" ); } + virtual int ordering() const override { return 10; } virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddOgrLayer.svg" ) ); } - virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr ) const override + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override { - return new QgsOgrSourceSelect( parent ); + return new QgsOgrSourceSelect( parent, fl, widgetMode ); } }; @@ -4374,3 +4380,5 @@ QGISEXTERN QList *sourceSelectProviders() return providers; } + +#endif diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp index 207635ddb81..7ceba8f2666 100644 --- a/src/providers/oracle/qgsoracleprovider.cpp +++ b/src/providers/oracle/qgsoracleprovider.cpp @@ -33,6 +33,7 @@ #ifdef HAVE_GUI #include "qgsoraclesourceselect.h" +#include "qgssourceselectprovider.h" #endif #include @@ -3582,4 +3583,36 @@ QGISEXTERN QString getStyleById( const QString &uri, QString styleId, QString &e return style; } -// vim: set sw=2 : + +#ifdef HAVE_GUI + +//! Provider for Oracle source select +class QgsOracleSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "oracle" ); } + virtual QString text() const override { return QObject::tr( "Oracle" ); } + virtual int ordering() { return 80; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddOracleLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsOracleSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsOracleSourceSelectProvider; + + return providers; +} + +#endif + + +// vim: set sw=2 diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index 9c178ce6ec9..a891dd7dcc0 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -40,6 +40,7 @@ #ifdef HAVE_GUI #include "qgspgsourceselect.h" +#include "qgssourceselectprovider.h" #endif const QString POSTGRES_KEY = QStringLiteral( "postgres" ); @@ -4752,6 +4753,36 @@ QGISEXTERN void cleanupProvider() QgsPostgresConnPool::cleanupInstance(); } + +#ifdef HAVE_GUI + +//! Provider for postgres source select +class QgsPostgresSourceSelectProvider : public QgsSourceSelectProvider //#spellok +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "postgres" ); } + virtual QString text() const override { return QObject::tr( "PostgreSQL" ); } + virtual int ordering() const override { return 40; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddPostgisLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsPgSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsPostgresSourceSelectProvider; //#spellok + + return providers; +} +#endif + // ---------- QgsPostgresSharedData::QgsPostgresSharedData() diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 0077957f147..c7e189b9faa 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -34,6 +34,11 @@ email : a.furieri@lqt.it #include "qgsjsonutils.h" #include "qgsvectorlayer.h" +#ifdef HAVE_GUI +#include "qgssourceselectprovider.h" +#include "qgsspatialitesourceselect.h" +#endif + #include #include #include @@ -5917,3 +5922,31 @@ QGISEXTERN void cleanupProvider() QgsSqliteHandle::closeAll(); } +#ifdef HAVE_GUI + +//! Provider for spatialite source select +class QgsSpatialiteSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "spatialite" ); } + virtual QString text() const override { return QObject::tr( "SpatiaLite" ); } + virtual int ordering() const override { return 50; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddSpatiaLiteLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsSpatiaLiteSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsSpatialiteSourceSelectProvider; + + return providers; +} +#endif diff --git a/src/providers/virtual/qgsvirtuallayerprovider.cpp b/src/providers/virtual/qgsvirtuallayerprovider.cpp index d8b613aaf93..0bd6a675b69 100644 --- a/src/providers/virtual/qgsvirtuallayerprovider.cpp +++ b/src/providers/virtual/qgsvirtuallayerprovider.cpp @@ -36,6 +36,11 @@ extern "C" #include "qgsvirtuallayersqlitemodule.h" #include "qgsvirtuallayerqueryparser.h" +#ifdef HAVE_GUI +#include "qgssourceselectprovider.h" +#include "qgsvirtuallayersourceselect.h" +#endif + const QString VIRTUAL_LAYER_KEY = QStringLiteral( "virtual" ); const QString VIRTUAL_LAYER_DESCRIPTION = QStringLiteral( "Virtual layer data provider" ); @@ -647,3 +652,33 @@ QGISEXTERN bool isProvider() QGISEXTERN void cleanupProvider() { } + + +#ifdef HAVE_GUI + +//! Provider for virtual layers source select +class QgsVirtualSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "virtual" ); } + virtual QString text() const override { return QObject::tr( "Virtual Layer" ); } + virtual int ordering() const override { return 90; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddVirtualLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsVirtualLayerSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsVirtualSourceSelectProvider; + + return providers; +} +#endif diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp index 5944575783c..5a9f0bd5cb8 100644 --- a/src/providers/wcs/qgswcsprovider.cpp +++ b/src/providers/wcs/qgswcsprovider.cpp @@ -32,6 +32,11 @@ #include "qgsmessagelog.h" #include "qgsexception.h" +#ifdef HAVE_GUI +#include "qgswcssourceselect.h" +#include "qgssourceselectprovider.h" +#endif + #include #include #include @@ -1925,3 +1930,32 @@ void QgsWcsDownloadHandler::canceled() mCacheReply->abort(); } } + +#ifdef HAVE_GUI + +//! Provider for WCS layers source select +class QgsWcsSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "wcs" ); } + virtual QString text() const override { return QObject::tr( "WCS" ); } + virtual int ordering() const override { return 110; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddWcsLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsWCSSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsWcsSourceSelectProvider; + + return providers; +} +#endif diff --git a/src/providers/wfs/qgswfsprovider.cpp b/src/providers/wfs/qgswfsprovider.cpp index a83f79e8d27..a50e3600b94 100644 --- a/src/providers/wfs/qgswfsprovider.cpp +++ b/src/providers/wfs/qgswfsprovider.cpp @@ -35,6 +35,11 @@ #include "qgswfsutils.h" #include "qgssettings.h" +#ifdef HAVE_GUI +#include "qgswfssourceselect.h" +#include "qgssourceselectprovider.h" +#endif + #include #include #include @@ -1698,3 +1703,32 @@ QGISEXTERN bool isProvider() return true; } + +#ifdef HAVE_GUI + +//! Provider for WFS layers source select +class QgsWfsSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "WFS" ); } + virtual QString text() const override { return QObject::tr( "WFS" ); } + virtual int ordering() const override { return 120; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddWfsLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsWFSSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsWfsSourceSelectProvider; + + return providers; +} +#endif diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index 1fb5b6256de..fc399c4c22f 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -47,6 +47,13 @@ #include "qgsexception.h" #include "qgssettings.h" + +#ifdef HAVE_GUI +#include "qgswmssourceselect.h" +#include "qgssourceselectprovider.h" +#endif + + #include #include #include @@ -4218,3 +4225,34 @@ QgsCachedImageFetcher::start() { QTimer::singleShot( 1, this, SLOT( send() ) ); } + + +#ifdef HAVE_GUI + +//! Provider for WMS layers source select +class QgsWmsSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + virtual QString providerKey() const override { return QStringLiteral( "wms" ); } + virtual QString text() const override { return QObject::tr( "WMS" ); } + virtual int ordering() const override { return 100; } + virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddWmsLayer.svg" ) ); } + virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override + { + return new QgsWMSSourceSelect( parent, fl, widgetMode ); + } +}; + + +QGISEXTERN QList *sourceSelectProviders() +{ + QList *providers = new QList(); + + *providers + << new QgsWmsSourceSelectProvider; + + return providers; +} +#endif + From 217e70067f6b2c1d9be506e01e5f458277b4bf62 Mon Sep 17 00:00:00 2001 From: Sandro Mani Date: Sat, 2 Sep 2017 22:51:00 +0200 Subject: [PATCH 307/364] [OGR] Attempt to use actual ogr_fid also if subset string is set If a subset string is set on an OGR layer, the feature iterator returns features with ids taken from a sequence starting from 0, regardless of the original feature id. This causes a mismatch in the data shown by the identify results table and the attribute widget. --- src/providers/ogr/qgsogrfeatureiterator.cpp | 31 ++++++++++-- src/providers/ogr/qgsogrfeatureiterator.h | 1 + src/providers/ogr/qgsogrprovider.cpp | 32 ++++++++++-- src/providers/ogr/qgsogrprovider.h | 2 +- tests/src/python/test_provider_ogr_sqlite.py | 51 ++++++++++++++++++++ 5 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/providers/ogr/qgsogrfeatureiterator.cpp b/src/providers/ogr/qgsogrfeatureiterator.cpp index 60d82e0fa5d..85e8bbb6a68 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.cpp +++ b/src/providers/ogr/qgsogrfeatureiterator.cpp @@ -43,6 +43,7 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool , mConn( nullptr ) , ogrLayer( nullptr ) , mSubsetStringSet( false ) + , mOrigFidAdded( false ) , mFetchGeometry( false ) , mExpressionCompiled( false ) , mFilterFids( mRequest.filterFids() ) @@ -69,7 +70,7 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool if ( !mSource->mSubsetString.isEmpty() ) { - ogrLayer = QgsOgrProviderUtils::setSubsetString( ogrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString ); + ogrLayer = QgsOgrProviderUtils::setSubsetString( ogrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString, mOrigFidAdded ); if ( !ogrLayer ) { return; @@ -197,7 +198,24 @@ bool QgsOgrFeatureIterator::nextFeatureFilterExpression( QgsFeature &f ) bool QgsOgrFeatureIterator::fetchFeatureWithId( QgsFeatureId id, QgsFeature &feature ) const { feature.setValid( false ); - OGRFeatureH fet = OGR_L_GetFeature( ogrLayer, FID_TO_NUMBER( id ) ); + OGRFeatureH fet; + if ( mOrigFidAdded ) + { + OGR_L_ResetReading( ogrLayer ); + while ( ( fet = OGR_L_GetNextFeature( ogrLayer ) ) ) + { + if ( OGR_F_GetFieldAsInteger64( fet, 0 ) == id ) + { + break; + } + OGR_F_Destroy( fet ); + } + } + else + { + fet = OGR_L_GetFeature( ogrLayer, FID_TO_NUMBER( id ) ); + } + if ( !fet ) { return false; @@ -323,7 +341,14 @@ void QgsOgrFeatureIterator::getFeatureAttribute( OGRFeatureH ogrFet, QgsFeature bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature &feature ) const { - feature.setId( OGR_F_GetFID( fet ) ); + if ( mOrigFidAdded ) + { + feature.setId( OGR_F_GetFieldAsInteger64( fet, 0 ) ); + } + else + { + feature.setId( OGR_F_GetFID( fet ) ); + } feature.initAttributes( mSource->mFields.count() ); feature.setFields( mSource->mFields ); // allow name-based attribute lookups diff --git a/src/providers/ogr/qgsogrfeatureiterator.h b/src/providers/ogr/qgsogrfeatureiterator.h index c4c34e90b53..24b9cbddde0 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.h +++ b/src/providers/ogr/qgsogrfeatureiterator.h @@ -75,6 +75,7 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSourcefromUnicode( subsetString ); else { - sql = "SELECT * FROM " + quotedIdentifier( layerName, ogrDriverName ); + QByteArray fidColumn = OGR_L_GetFIDColumn( layer ); + + sql = QByteArray( "SELECT " ); + if ( !fidColumn.isEmpty() ) + { + sql += fidColumn + " as orig_ogc_fid, "; + origFidAddAttempted = true; + } + sql += "* FROM " + quotedIdentifier( layerName, ogrDriverName ); sql += " WHERE " + encoding->fromUnicode( subsetString ); } QgsDebugMsg( QString( "SQL: %1" ).arg( encoding->toUnicode( sql ) ) ); - return OGR_DS_ExecuteSQL( ds, sql.constData(), nullptr, nullptr ); + OGRLayerH subsetLayer = OGR_DS_ExecuteSQL( ds, sql.constData(), nullptr, nullptr ); + + // Check if first column is orig_ogc_fid + if ( origFidAddAttempted && subsetLayer ) + { + OGRFeatureDefnH fdef = OGR_L_GetLayerDefn( subsetLayer ); + if ( OGR_FD_GetFieldCount( fdef ) > 0 ) + { + OGRFieldDefnH fldDef = OGR_FD_GetFieldDefn( fdef, 0 ); + origFidAdded = qstrcmp( OGR_Fld_GetNameRef( fldDef ), "orig_ogc_fid" ) == 0; + } + } + + return subsetLayer; } void QgsOgrProvider::open( OpenMode mode ) diff --git a/src/providers/ogr/qgsogrprovider.h b/src/providers/ogr/qgsogrprovider.h index c4d1df3de08..625ca95bf8b 100644 --- a/src/providers/ogr/qgsogrprovider.h +++ b/src/providers/ogr/qgsogrprovider.h @@ -264,7 +264,7 @@ class QgsOgrProviderUtils { public: static void setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes, bool firstAttrIsFid ); - static OGRLayerH setSubsetString( OGRLayerH layer, OGRDataSourceH ds, QTextCodec *encoding, const QString &subsetString ); + static OGRLayerH setSubsetString( OGRLayerH layer, OGRDataSourceH ds, QTextCodec *encoding, const QString &subsetString, bool &origFidAdded ); static QByteArray quotedIdentifier( QByteArray field, const QString &ogrDriverName ); /** Quote a value for placement in a SQL string. diff --git a/tests/src/python/test_provider_ogr_sqlite.py b/tests/src/python/test_provider_ogr_sqlite.py index 76dea274a8f..2a7c19a7923 100644 --- a/tests/src/python/test_provider_ogr_sqlite.py +++ b/tests/src/python/test_provider_ogr_sqlite.py @@ -199,6 +199,57 @@ class TestPyQgsOGRProviderSqlite(unittest.TestCase): self.assertTrue(vl.dataProvider().defaultValue(5).secsTo(QTime.currentTime()) < 1) self.assertTrue(vl.dataProvider().defaultValue(6).secsTo(QDateTime.currentDateTime()) < 1) + def testSubsetStringFids(self): + """ tests that feature ids are stable even if a subset string is set """ + + tmpfile = os.path.join(self.basetestpath, 'subsetStringFids.sqlite') + ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile) + lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid']) + lyr.CreateField(ogr.FieldDefn('type', ogr.OFTInteger)) + lyr.CreateField(ogr.FieldDefn('value', ogr.OFTInteger)) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetFID(0) + f.SetField(0, 1) + f.SetField(1, 11) + lyr.CreateFeature(f) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetFID(1) + f.SetField(0, 1) + f.SetField(1, 12) + lyr.CreateFeature(f) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetFID(2) + f.SetField(0, 1) + f.SetField(1, 13) + lyr.CreateFeature(f) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetFID(3) + f.SetField(0, 2) + f.SetField(1, 14) + lyr.CreateFeature(f) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetFID(4) + f.SetField(0, 2) + f.SetField(1, 15) + lyr.CreateFeature(f) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetFID(5) + f.SetField(0, 2) + f.SetField(1, 16) + lyr.CreateFeature(f) + f = None + ds = None + + vl = QgsVectorLayer(tmpfile + "|subset=type=2", 'test', 'ogr') + self.assertTrue(vl.isValid()) + + req = QgsFeatureRequest() + req.setFilterExpression("value=16") + it = vl.getFeatures(req) + f = QgsFeature() + self.assertTrue(it.nextFeature(f)) + self.assertTrue(f.id() == 5) + if __name__ == '__main__': unittest.main() From 884b58c56dc336637e4d1451e45545563d8d09e1 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 17:35:48 +0200 Subject: [PATCH 308/364] Resistance is futile, you will be sipified --- python/core/core_auto.sip | 1 - python/gui/gui_auto.sip | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 934e1e8a281..37cfc8ee399 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -405,4 +405,3 @@ %Include symbology/qgsarrowsymbollayer.sip %Include composer/qgscomposerutils.sip %Include qgsuserprofile.sip - diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index a6ff6baf17d..86e9b31afab 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -21,6 +21,8 @@ %Include qgsvertexmarker.sip %Include qgsfiledownloader.sip %Include qgsabstractdatasourcewidget.sip +%Include qgssourceselectprovider.sip +%Include qgssourceselectproviderregistry.sip %Include attributetable/qgsfeaturemodel.sip %Include auth/qgsauthauthoritieseditor.sip %Include auth/qgsauthcertificateinfo.sip @@ -297,6 +299,3 @@ %Include locator/qgslocatorfilter.sip %Include locator/qgslocatorwidget.sip %Include qgsadvanceddigitizingcanvasitem.sip -%Include qgssourceselectprovider.sip -%Include qgssourceselectproviderregistry.sip - From 04af8372a4d3ea29bade7b44c80d7e17de240452 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 20:38:06 +0200 Subject: [PATCH 309/364] Fix build warnings --- src/gui/qgsabstractdatasourcewidget.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/qgsabstractdatasourcewidget.h b/src/gui/qgsabstractdatasourcewidget.h index 897a69f25a3..d4831001243 100644 --- a/src/gui/qgsabstractdatasourcewidget.h +++ b/src/gui/qgsabstractdatasourcewidget.h @@ -96,8 +96,8 @@ class GUI_EXPORT QgsAbstractDataSourceWidget : public QDialog /** Emitted when a layer needs to be replaced * \param oldId old layer ID * \param source URI of the layer - * \params name of the layer - * \params provider key + * \param name of the layer + * \param provider key */ void replaceVectorLayer( const QString &oldId, const QString &source, const QString &name, const QString &provider ); From 14f9cd19bcb131d7cbe46b1bf75698e8731d860d Mon Sep 17 00:00:00 2001 From: Sandro Mani Date: Wed, 29 Mar 2017 10:43:54 +0200 Subject: [PATCH 310/364] [Globe] Adapt for QGIS API changes, add Qt5 compatibility, support osgEarth up to current git master --- cmake/FindOSGEARTH.cmake | 8 +- src/plugins/globe/CMakeLists.txt | 8 +- src/plugins/globe/CMakeModules/FindOSG.cmake | 4 +- src/plugins/globe/WorldWindOptions | 91 ---------- .../featuresource/qgsglobefeatureoptions.h | 2 +- .../featuresource/qgsglobefeaturesource.cpp | 29 +++- .../featuresource/qgsglobefeaturesource.h | 12 +- .../featuresource/qgsglobefeatureutils.h | 15 +- src/plugins/globe/globe_plugin.cpp | 161 ++++++++++++------ src/plugins/globe/globe_plugin.h | 1 + src/plugins/globe/qgsglobefeatureidentify.cpp | 8 +- .../globe/qgsglobefrustumhighlight.cpp | 44 +++-- src/plugins/globe/qgsglobefrustumhighlight.h | 6 +- src/plugins/globe/qgsglobeplugindialog.cpp | 8 +- src/plugins/globe/qgsglobeplugindialog.ui | 6 +- src/plugins/globe/qgsglobetilesource.cpp | 48 +++--- src/plugins/globe/qgsglobetilesource.h | 30 +++- .../globe/qgsglobevectorlayerproperties.cpp | 8 +- .../globe/qgsglobevectorlayerproperties.h | 1 + .../qgsglobevectorlayerpropertiespage.ui | 4 +- src/plugins/globe/qgsglobewidget.cpp | 28 +-- src/plugins/globe/qgsglobewidget.h | 4 +- 22 files changed, 270 insertions(+), 256 deletions(-) delete mode 100644 src/plugins/globe/WorldWindOptions diff --git a/cmake/FindOSGEARTH.cmake b/cmake/FindOSGEARTH.cmake index 8a20895c6d7..fc1a6144d72 100644 --- a/cmake/FindOSGEARTH.cmake +++ b/cmake/FindOSGEARTH.cmake @@ -48,11 +48,11 @@ FIND_OSGEARTH_INCLUDE( OSGEARTH_ELEVATION_QUERY osgEarth/ElevationQuery ) ###### libraries ###### -MACRO( FIND_OSGEARTH_LIBRARY MYLIBRARY MYLIBRARYNAME ) +MACRO( FIND_OSGEARTH_LIBRARY MYLIBRARY ) FIND_LIBRARY(${MYLIBRARY} NAMES - ${MYLIBRARYNAME} + ${ARGN} PATHS ${OSGEARTH_DIR} $ENV{OSGEARTH_BUILD_DIR} @@ -93,8 +93,8 @@ FIND_OSGEARTH_LIBRARY( OSGEARTHFEATURES_LIBRARY_DEBUG osgEarthFeaturesd ) FIND_OSGEARTH_LIBRARY( OSGEARTHSYMBOLOGY_LIBRARY osgEarthSymbology ) FIND_OSGEARTH_LIBRARY( OSGEARTHSYMBOLOGY_LIBRARY_DEBUG osgEarthSymbologyd ) -FIND_OSGEARTH_LIBRARY( OSGEARTHQT_LIBRARY osgEarthQt ) -FIND_OSGEARTH_LIBRARY( OSGEARTHQT_LIBRARY_DEBUG osgEarthQtd ) +FIND_OSGEARTH_LIBRARY( OSGEARTHQT_LIBRARY osgEarthQt5 osgEarthQt) +FIND_OSGEARTH_LIBRARY( OSGEARTHQT_LIBRARY_DEBUG osgEarthQtd osgEarthQt5d) FIND_OSGEARTH_LIBRARY( OSGEARTHANNOTATION_LIBRARY osgEarthAnnotation ) FIND_OSGEARTH_LIBRARY( OSGEARTHANNOTATION_LIBRARY_DEBUG osgEarthAnnotationd ) diff --git a/src/plugins/globe/CMakeLists.txt b/src/plugins/globe/CMakeLists.txt index 0d3efd65b88..b8d42a77920 100644 --- a/src/plugins/globe/CMakeLists.txt +++ b/src/plugins/globe/CMakeLists.txt @@ -3,7 +3,7 @@ SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules ${CMAKE_MODULE_PA FIND_PACKAGE(OSG REQUIRED) FIND_PACKAGE(OSGEARTH REQUIRED) -FIND_PACKAGE(Threads) +FIND_PACKAGE(Qt5OpenGL REQUIRED) ######################################################## # Files @@ -55,18 +55,18 @@ QT5_WRAP_CPP (GLOBE_PLUGIN_MOC_SRCS ${GLOBE_PLUGIN_MOC_HDRS}) QT5_ADD_RESOURCES(GLOBE_PLUGIN_RCC_SRCS ${GLOBE_PLUGIN_RCCS}) ADD_LIBRARY (globeplugin SHARED ${GLOBE_PLUGIN_SRCS} ${GLOBE_PLUGIN_MOC_SRCS} ${GLOBE_PLUGIN_RCC_SRCS} ${GLOBE_PLUGIN_UIS_H} ${GLOBE_PLUGIN_HDRS}) - INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} + ${Qt5OpenGL_INCLUDE_DIRS} ${OSGEARTH_INCLUDE_DIR} ${OSG_INCLUDE_DIR} ${GEOS_INCLUDE_DIR} ${SIP_INCLUDE_DIR} ../../core + ../../core/expression ../../core/geometry ../../core/metadata ../../core/raster - ../../core/symbology ../../gui .. ${CMAKE_BINARY_DIR}/src/core @@ -76,7 +76,7 @@ INCLUDE_DIRECTORIES( TARGET_LINK_LIBRARIES(globeplugin qgis_core qgis_gui - ${QT_QTOPENGL_LIBRARY} + Qt5::OpenGL ${OSGDB_LIBRARY} ${OSGGA_LIBRARY} ${OSGUTIL_LIBRARY} diff --git a/src/plugins/globe/CMakeModules/FindOSG.cmake b/src/plugins/globe/CMakeModules/FindOSG.cmake index efc8c7173f1..c7ea888d7b6 100644 --- a/src/plugins/globe/CMakeModules/FindOSG.cmake +++ b/src/plugins/globe/CMakeModules/FindOSG.cmake @@ -106,8 +106,8 @@ FIND_OSG_LIBRARY( OSGVIEWER_LIBRARY_DEBUG osgViewerd ) FIND_OSG_LIBRARY( OSGGA_LIBRARY osgGA ) FIND_OSG_LIBRARY( OSGGA_LIBRARY_DEBUG osgGAd ) -FIND_OSG_LIBRARY( OSGQT_LIBRARY osgQt ) -FIND_OSG_LIBRARY( OSGQT_LIBRARY_DEBUG osgQtd ) +FIND_OSG_LIBRARY( OSGQT_LIBRARY osgQt5 osgQt ) +FIND_OSG_LIBRARY( OSGQT_LIBRARY_DEBUG osgQt5d osgQtd ) FIND_OSG_LIBRARY( OSGWIDGET_LIBRARY osgWidget ) FIND_OSG_LIBRARY( OSGWIDGET_LIBRARY_DEBUG osgWidgetd ) diff --git a/src/plugins/globe/WorldWindOptions b/src/plugins/globe/WorldWindOptions deleted file mode 100644 index 483d1e9f294..00000000000 --- a/src/plugins/globe/WorldWindOptions +++ /dev/null @@ -1,91 +0,0 @@ -/* -*-c++-*- */ -/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph - * Copyright 2008-2010 Pelican Mapping - * http://osgearth.org - * - * osgEarth is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see - */ -#ifndef OSGEARTH_DRIVER_WORLDWIND_DRIVEROPTIONS -#define OSGEARTH_DRIVER_WORLDWIND_DRIVEROPTIONS 1 - -#include -#include - - -namespace osgEarth { namespace Drivers -{ - using namespace osgEarth; - - class WorldWindOptions : public TileSourceOptions // NO EXPORT; header only - { - public: - optional& maxLOD() { return _maxLOD; } - const optional& maxLOD() const { return _maxLOD; } - - optional& elevationCachePath() { return _elevationCachePath; } - const optional& elevationCachePath() const { return _elevationCachePath; } - - optional& elevationURL() { return _elevationURL; } - const optional& elevationURL() const { return _elevationURL; } - - // image support NYI ... use the "tileservice" plugin instead - //optional& imageURL() { return _imageURL; } - //const optional& imageURL() const { return _imageURL; } - - public: - WorldWindOptions( const TileSourceOptions& opt =TileSourceOptions() ) - : TileSourceOptions( opt ) - , _imageURL( "http://s0.tileservice.worldwindcentral.com/getTile?" ) - , _elevationURL( "http://worldwind25.arc.nasa.gov/wwelevation/wwelevation.aspx?T=srtm30pluszip" ) - , _maxLOD( 11 ) - { - setDriver( "worldwind" ); - fromConfig( _conf ); - } - - public: - Config getConfig() const { - Config conf = TileSourceOptions::getConfig(); - conf.updateIfSet("image_url", _imageURL); - conf.updateIfSet("elevation_url", _elevationURL); - conf.updateIfSet("max_lod", _maxLOD); - conf.updateIfSet("elevation_cache", _elevationCachePath); - return conf; - } - - protected: - void mergeConfig( const Config& conf ) { - TileSourceOptions::mergeConfig( conf ); - fromConfig( conf ); - } - - private: - void fromConfig( const Config& conf ) { - conf.getIfSet("image_url", _imageURL); - conf.getIfSet("elevation_url", _elevationURL); - conf.getIfSet("max_lod", _maxLOD); - conf.getIfSet("elevation_cache", _elevationCachePath); - conf.getIfSet("worldwind_cache", _elevationCachePath); // back compat - } - - optional _imageURL; - optional _elevationURL; - optional _elevationCachePath; - optional _maxLOD; - }; - -} } // namespace osgEarth::Drivers - -#endif // OSGEARTH_DRIVER_WMS_DRIVEROPTIONS - diff --git a/src/plugins/globe/featuresource/qgsglobefeatureoptions.h b/src/plugins/globe/featuresource/qgsglobefeatureoptions.h index 3b9169c8ef8..1270587b5f6 100644 --- a/src/plugins/globe/featuresource/qgsglobefeatureoptions.h +++ b/src/plugins/globe/featuresource/qgsglobefeatureoptions.h @@ -74,7 +74,7 @@ class QgsGlobeFeatureOptions : public osgEarth::Features::FeatureSourceOptions / } osgEarth::optional mLayerId; - QgsVectorLayer *mLayer = nullptr; + QgsVectorLayer *mLayer = nullptr; }; #endif // QGSGLOBEFEATUREOPTIONS_H diff --git a/src/plugins/globe/featuresource/qgsglobefeaturesource.cpp b/src/plugins/globe/featuresource/qgsglobefeaturesource.cpp index 15744be9b22..f8f1a154925 100644 --- a/src/plugins/globe/featuresource/qgsglobefeaturesource.cpp +++ b/src/plugins/globe/featuresource/qgsglobefeaturesource.cpp @@ -12,6 +12,7 @@ * (at your option) any later version. * * * ***************************************************************************/ + #include #include @@ -27,32 +28,48 @@ #include "qgsglobefeaturesource.h" -QgsGlobeFeatureSource::QgsGlobeFeatureSource( const QgsGlobeFeatureOptions &options ) : - mOptions( options ), - mLayer( 0 ), - mProfile( 0 ) +QgsGlobeFeatureSource::QgsGlobeFeatureSource( const QgsGlobeFeatureOptions &options ) + : mOptions( options ) + , mLayer( 0 ) +#if OSGEARTH_VERSION_LESS_THAN(2, 8, 0) + , mProfile( 0 ) +#endif { } +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) +osgEarth::Status QgsGlobeFeatureSource::initialize( const osgDB::Options *dbOptions ) +{ +#else void QgsGlobeFeatureSource::initialize( const osgDB::Options *dbOptions ) { +#endif Q_UNUSED( dbOptions ) mLayer = mOptions.layer(); connect( mLayer, SIGNAL( attributeValueChanged( QgsFeatureId, int, QVariant ) ), this, SLOT( attributeValueChanged( QgsFeatureId, int, QVariant ) ) ); - connect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, const QgsGeometry & ) ), this, SLOT( geometryChanged( QgsFeatureId, const QgsGeometry & ) ) ); + connect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry ) ), this, SLOT( geometryChanged( QgsFeatureId, QgsGeometry ) ) ); // create the profile osgEarth::SpatialReference *ref = osgEarth::SpatialReference::create( mLayer->crs().toWkt().toStdString() ); if ( 0 == ref ) { std::cout << "Cannot find the spatial reference" << std::endl; +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) + return osgEarth::Status( osgEarth::Status::ConfigurationError ); +#else return; +#endif } QgsRectangle ext = mLayer->extent(); osgEarth::GeoExtent geoext( ref, ext.xMinimum(), ext.yMinimum(), ext.xMaximum(), ext.yMaximum() ); - mProfile = new osgEarth::Features::FeatureProfile( geoext ); mSchema = QgsGlobeFeatureUtils::schemaForFields( mLayer->pendingFields() ); +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) + setFeatureProfile( new osgEarth::Features::FeatureProfile( geoext ) ); + return osgEarth::Status( osgEarth::Status::NoError ); +#else + mProfile = new osgEarth::Features::FeatureProfile( geoext ); +#endif } osgEarth::Features::FeatureCursor *QgsGlobeFeatureSource::createFeatureCursor( const osgEarth::Symbology::Query &query ) diff --git a/src/plugins/globe/featuresource/qgsglobefeaturesource.h b/src/plugins/globe/featuresource/qgsglobefeaturesource.h index 6fc289c2dbd..d137e53c265 100644 --- a/src/plugins/globe/featuresource/qgsglobefeaturesource.h +++ b/src/plugins/globe/featuresource/qgsglobefeaturesource.h @@ -12,10 +12,12 @@ * (at your option) any later version. * * * ***************************************************************************/ + #ifndef QGSGLOBEFEATURESOURCE_H #define QGSGLOBEFEATURESOURCE_H #include +#include #include #include "qgsglobefeatureoptions.h" @@ -38,10 +40,16 @@ class QgsGlobeFeatureSource : public QObject, public osgEarth::Features::Feature const char *className() const override { return "QGISFeatureSource"; } const char *libraryName() const override { return "QGIS"; } - void initialize( const osgDB::Options *dbOptions ) override; +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) + osgEarth::Status initialize( const osgDB::Options *dbOptions ); +#else + void initialize( const osgDB::Options *dbOptions ); +#endif protected: +#if OSGEARTH_VERSION_LESS_THAN(2, 8, 0) const osgEarth::Features::FeatureProfile *createFeatureProfile() override { return mProfile; } +#endif const osgEarth::Features::FeatureSchema &getSchema() const override { return mSchema; } ~QgsGlobeFeatureSource() {} @@ -49,7 +57,9 @@ class QgsGlobeFeatureSource : public QObject, public osgEarth::Features::Feature private: QgsGlobeFeatureOptions mOptions; QgsVectorLayer *mLayer = nullptr; +#if OSGEARTH_VERSION_LESS_THAN(2, 8, 0) osgEarth::Features::FeatureProfile *mProfile = nullptr; +#endif osgEarth::Features::FeatureSchema mSchema; typedef std::map > FeatureMap_t; FeatureMap_t mFeatures; diff --git a/src/plugins/globe/featuresource/qgsglobefeatureutils.h b/src/plugins/globe/featuresource/qgsglobefeatureutils.h index 501a4354f9a..de5de21c7cf 100644 --- a/src/plugins/globe/featuresource/qgsglobefeatureutils.h +++ b/src/plugins/globe/featuresource/qgsglobefeatureutils.h @@ -36,7 +36,7 @@ class QgsGlobeFeatureUtils return QgsPoint( QgsWkbTypes::PointZ, pt.x(), pt.y(), pt.z() ); } - static inline osg::Vec3d pointFromQgsPointXY( const QgsPoint &pt ) + static inline osg::Vec3d pointFromQgsPoint( const QgsPoint &pt ) { return osg::Vec3d( pt.x(), pt.y(), pt.z() ); } @@ -47,7 +47,7 @@ class QgsGlobeFeatureUtils osgEarth::Features::LineString *retLineString = new osgEarth::Features::LineString(); for ( int iVtx = 0, nVtx = linearString->vertexCount(); iVtx < nVtx; ++iVtx ) { - retLineString->push_back( pointFromQgsPointXY( linearString->vertexAt( QgsVertexId( 0, 0, iVtx ) ) ) ); + retLineString->push_back( pointFromQgsPoint( linearString->vertexAt( QgsVertexId( 0, 0, iVtx ) ) ) ); } delete linearString; return retLineString; @@ -63,7 +63,7 @@ class QgsGlobeFeatureUtils // the outer ring for ( int iVtx = 0, nVtx = linearPolygon->vertexCount( 0, 0 ); iVtx < nVtx; ++iVtx ) { - retPoly->push_back( pointFromQgsPointXY( linearPolygon->vertexAt( QgsVertexId( 0, 0, iVtx ) ) ) ); + retPoly->push_back( pointFromQgsPoint( linearPolygon->vertexAt( QgsVertexId( 0, 0, iVtx ) ) ) ); } retPoly->rewind( osgEarth::Symbology::Ring::ORIENTATION_CCW ); @@ -72,7 +72,7 @@ class QgsGlobeFeatureUtils osgEarth::Features::Ring *innerRing = new osgEarth::Features::Ring(); for ( int iVtx = 0, nVtx = linearPolygon->vertexCount( 0, iRing ); iVtx < nVtx; ++iVtx ) { - innerRing->push_back( pointFromQgsPointXY( linearPolygon->vertexAt( QgsVertexId( 0, iRing, iVtx ) ) ) ); + innerRing->push_back( pointFromQgsPoint( linearPolygon->vertexAt( QgsVertexId( 0, iRing, iVtx ) ) ) ); } innerRing->rewind( osgEarth::Symbology::Ring::ORIENTATION_CW ); retPoly->getHoles().push_back( osg::ref_ptr( innerRing ) ); @@ -97,7 +97,7 @@ class QgsGlobeFeatureUtils case QgsWkbTypes::Point: { osgEarth::Features::PointSet *pointSet = new osgEarth::Features::PointSet(); - pointSet->push_back( pointFromQgsPointXY( *static_cast( geom.geometry() ) ) ); + pointSet->push_back( pointFromQgsPoint( *static_cast( geom.geometry() ) ) ); return pointSet; } @@ -107,7 +107,7 @@ class QgsGlobeFeatureUtils QgsMultiPointV2 *multiPoint = static_cast( geom.geometry() ); for ( int i = 0, n = multiPoint->numGeometries(); i < n; ++i ) { - pointSet->push_back( pointFromQgsPointXY( *static_cast( multiPoint->geometryN( i ) ) ) ); + pointSet->push_back( pointFromQgsPoint( *static_cast( multiPoint->geometryN( i ) ) ) ); } return pointSet; } @@ -156,7 +156,8 @@ class QgsGlobeFeatureUtils static osgEarth::Features::Feature *featureFromQgsFeature( QgsVectorLayer *layer, QgsFeature &feat ) { osgEarth::Features::Geometry *nGeom = geometryFromQgsGeometry( feat.geometry() ); - osgEarth::Features::Feature *retFeat = new osgEarth::Features::Feature( nGeom, 0, osgEarth::Style(), feat.id() ); + osgEarth::SpatialReference *ref = osgEarth::SpatialReference::create( layer->crs().toWkt().toStdString() ); + osgEarth::Features::Feature *retFeat = new osgEarth::Features::Feature( nGeom, ref, osgEarth::Style(), feat.id() ); const QgsFields &fields = layer->pendingFields(); const QgsAttributes &attrs = feat.attributes(); diff --git a/src/plugins/globe/globe_plugin.cpp b/src/plugins/globe/globe_plugin.cpp index cdf49656d57..abc8a6cce97 100644 --- a/src/plugins/globe/globe_plugin.cpp +++ b/src/plugins/globe/globe_plugin.cpp @@ -16,8 +16,6 @@ * * ***************************************************************************/ -// Include this first to avoid _POSIX_C_SOURCE redefined warnings -// see http://bytes.com/topic/python/answers/30009-warning-_posix_c_source-redefined #include "globe_plugin.h" #include "qgsglobeplugindialog.h" #include "qgsglobefeatureidentify.h" @@ -28,7 +26,6 @@ #include "featuresource/qgsglobefeatureoptions.h" #include -#include "qgsguiutils.h" #include #include #include @@ -36,12 +33,14 @@ #include #include #include -#include #include #include #include #include #include +#include +#include +#include #include #include @@ -63,6 +62,9 @@ #include #include #include +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) +#include +#endif #include #include #include @@ -94,7 +96,7 @@ static const QString sName = QObject::tr( "Globe" ); static const QString sDescription = QObject::tr( "Overlay data on a 3D globe" ); static const QString sCategory = QObject::tr( "Plugins" ); static const QString sPluginVersion = QObject::tr( "Version 1.0" ); -static const QgisPlugin::PLUGINTYPE sPluginType = QgisPlugin::UI; +static const QgisPlugin::PluginType sPluginType = QgisPlugin::UI; static const QString sIcon = ":/globe/icon.svg"; static const QString sExperimental = QString( "false" ); @@ -311,11 +313,9 @@ void GlobePlugin::run() setupProxy(); // Tile stats label -#ifdef GLOBE_SHOW_TILE_STATS mStatsLabel = new osgEarth::Util::Controls::LabelControl( "", 10 ); mStatsLabel->setPosition( 0, 0 ); osgEarth::Util::Controls::ControlCanvas::get( mOsgViewer )->addControl( mStatsLabel.get() ); -#endif mDockWidget = new QgsGlobeWidget( mQGisIface, mQGisIface->mainWindow() ); connect( mDockWidget, SIGNAL( destroyed( QObject * ) ), this, SLOT( reset() ) ); @@ -363,11 +363,19 @@ void GlobePlugin::run() // Add draped layer osgEarth::TileSourceOptions opts; opts.L2CacheSize() = 0; +#if OSGEARTH_VERSION_LESS_THAN( 2, 9, 0 ) opts.tileSize() = 128; - mTileSource = new QgsGlobeTileSource( opts ); +#endif + mTileSource = new QgsGlobeTileSource( mQGisIface->mapCanvas(), opts ); +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + mTileSource->open(); +#endif osgEarth::ImageLayerOptions options( "QGIS" ); options.driver()->L2CacheSize() = 0; +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + options.tileSize() = 128; +#endif options.cachePolicy() = osgEarth::CachePolicy::USAGE_NO_CACHE; mQgisMapLayer = new osgEarth::ImageLayer( options, mTileSource ); map->addImageLayer( mQgisMapLayer ); @@ -394,14 +402,18 @@ void GlobePlugin::run() mOsgViewer->getDatabasePager()->setDoPreCompile( true ); mViewerWidget = new osgEarth::QtGui::ViewerWidget( mOsgViewer ); + QGLFormat glf = QGLFormat::defaultFormat(); +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + glf.setVersion( 3, 3 ); + glf.setProfile( QGLFormat::CoreProfile ); +#endif if ( settings.value( "/Plugin-Globe/anti-aliasing", true ).toBool() && settings.value( "/Plugin-Globe/anti-aliasing-level", "" ).toInt() > 0 ) { - QGLFormat glf = QGLFormat::defaultFormat(); glf.setSampleBuffers( true ); glf.setSamples( settings.value( "/Plugin-Globe/anti-aliasing-level", "" ).toInt() ); - mViewerWidget->setFormat( glf ); } + mViewerWidget->setFormat( glf ); mDockWidget->setWidget( mViewerWidget ); mViewerWidget->setParent( mDockWidget ); @@ -432,6 +444,7 @@ void GlobePlugin::showSettings() void GlobePlugin::projectRead() { + setGlobeEnabled( false ); // Hide globe when new projects loaded, on some systems it is very slow loading a new project with globe enabled mSettingsDialog->readProjectSettings(); applyProjectSettings(); } @@ -476,22 +489,25 @@ void GlobePlugin::applyProjectSettings() QgsDebugMsg( "imageryLayersChanged: Globe Running, executing" ); osg::ref_ptr map = mMapNode->getMap(); - if ( map->getNumImageLayers() > 1 ) - { - mOsgViewer->getDatabasePager()->clear(); - } - // Remove image layers osgEarth::ImageLayerVector list; +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + map->getLayers( list ); +#else map->getImageLayers( list ); +#endif for ( osgEarth::ImageLayerVector::iterator i = list.begin(); i != list.end(); ++i ) { if ( *i != mQgisMapLayer ) map->removeImageLayer( *i ); } + if ( !list.empty() ) + { + mOsgViewer->getDatabasePager()->clear(); + } // Add image layers - foreach ( const QgsGlobePluginDialog::LayerDataSource &datasource, mImagerySources ) + for ( const QgsGlobePluginDialog::LayerDataSource &datasource : mImagerySources ) { osgEarth::ImageLayer *layer = 0; if ( "Raster" == datasource.type ) @@ -524,21 +540,24 @@ void GlobePlugin::applyProjectSettings() QgsDebugMsg( "elevationLayersChanged: Globe Running, executing" ); osg::ref_ptr map = mMapNode->getMap(); - if ( map->getNumElevationLayers() > 1 ) - { - mOsgViewer->getDatabasePager()->clear(); - } - // Remove elevation layers osgEarth::ElevationLayerVector list; +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + map->getLayers( list ); +#else map->getElevationLayers( list ); +#endif for ( osgEarth::ElevationLayerVector::iterator i = list.begin(); i != list.end(); ++i ) { map->removeElevationLayer( *i ); } + if ( !list.empty() ) + { + mOsgViewer->getDatabasePager()->clear(); + } // Add elevation layers - foreach ( const QgsGlobePluginDialog::LayerDataSource &datasource, mElevationSources ) + for ( const QgsGlobePluginDialog::LayerDataSource &datasource : mElevationSources ) { osgEarth::ElevationLayer *layer = 0; if ( "Raster" == datasource.type ) @@ -652,7 +671,6 @@ void GlobePlugin::syncExtent() QgsDistanceArea dist; dist.setSourceCrs( globeCrs ); - dist.setEllipsoidalMode( true ); dist.setEllipsoid( "WGS84" ); QgsPointXY ll = QgsPointXY( extent.xMinimum(), extent.yMinimum() ); @@ -781,13 +799,13 @@ void GlobePlugin::addModelLayer( QgsVectorLayer *vLayer, QgsGlobeVectorLayerConf QgsRenderContext ctx; if ( !vLayer->renderer()->symbols( ctx ).isEmpty() ) { - Q_FOREACH ( QgsSymbol *sym, vLayer->renderer()->symbols( ctx ) ) + for ( QgsSymbol *sym : vLayer->renderer()->symbols( ctx ) ) { if ( sym->type() == QgsSymbol::Line ) { osgEarth::LineSymbol *ls = style.getOrCreateSymbol(); QColor color = sym->color(); - ls->stroke()->color() = osg::Vec4f( color.redF(), color.greenF(), color.blueF(), color.alphaF() * ( 100.f - vLayer->layerTransparency() ) / 100.f ); + ls->stroke()->color() = osg::Vec4f( color.redF(), color.greenF(), color.blueF(), color.alphaF() * vLayer->opacity() ); ls->stroke()->width() = 1.0f; } else if ( sym->type() == QgsSymbol::Fill ) @@ -795,7 +813,7 @@ void GlobePlugin::addModelLayer( QgsVectorLayer *vLayer, QgsGlobeVectorLayerConf // TODO access border color, etc. osgEarth::PolygonSymbol *poly = style.getOrCreateSymbol(); QColor color = sym->color(); - poly->fill()->color() = osg::Vec4f( color.redF(), color.greenF(), color.blueF(), color.alphaF() * ( 100.f - vLayer->layerTransparency() ) / 100.f ); + poly->fill()->color() = osg::Vec4f( color.redF(), color.greenF(), color.blueF(), color.alphaF() * vLayer->opacity() ); style.addSymbol( poly ); } } @@ -803,10 +821,10 @@ void GlobePlugin::addModelLayer( QgsVectorLayer *vLayer, QgsGlobeVectorLayerConf else { osgEarth::PolygonSymbol *poly = style.getOrCreateSymbol(); - poly->fill()->color() = osg::Vec4f( 1.f, 0, 0, 1.f - vLayer->layerTransparency() / 255.f ); + poly->fill()->color() = osg::Vec4f( 1.f, 0, 0, vLayer->opacity() ); style.addSymbol( poly ); osgEarth::LineSymbol *ls = style.getOrCreateSymbol(); - ls->stroke()->color() = osg::Vec4f( 1.f, 0, 0, 1.f - vLayer->layerTransparency() / 255.f ); + ls->stroke()->color() = osg::Vec4f( 1.f, 0, 0, vLayer->opacity() ); ls->stroke()->width() = 1.0f; } @@ -842,17 +860,16 @@ void GlobePlugin::addModelLayer( QgsVectorLayer *vLayer, QgsGlobeVectorLayerConf { osgEarth::TextSymbol *textSymbol = style.getOrCreateSymbol(); textSymbol->declutter() = layerConfig->labelingDeclutter; - QgsPalLayerSettings lyr; - lyr.readFromLayer( vLayer ); - QString labelingExpr = lyr.getLabelExpression()->expression(); - textSymbol->content() = QString( "[%1]" ).arg( labelingExpr ).toStdString(); - textSymbol->font() = lyr.textFont.family().toStdString(); - textSymbol->size() = lyr.textFont.pointSize(); + QgsPalLayerSettings lyr = vLayer->labeling()->settings(); + textSymbol->content() = QString( "[%1]" ).arg( lyr.fieldName ).toStdString(); + textSymbol->font() = lyr.format().font().family().toStdString(); + textSymbol->size() = lyr.format().font().pointSize(); textSymbol->alignment() = osgEarth::TextSymbol::ALIGN_CENTER_TOP; osgEarth::Stroke stroke; - stroke.color() = osgEarth::Symbology::Color( lyr.bufferColor.redF(), lyr.bufferColor.greenF(), lyr.bufferColor.blueF(), lyr.bufferColor.alphaF() ); + QColor bufferColor = lyr.format().buffer().color(); + stroke.color() = osgEarth::Symbology::Color( bufferColor.redF(), bufferColor.greenF(), bufferColor.blueF(), bufferColor.alphaF() ); textSymbol->halo() = stroke; - textSymbol->haloOffset() = lyr.bufferSize; + textSymbol->haloOffset() = lyr.format().buffer().size(); } osgEarth::RenderSymbol *renderSymbol = style.getOrCreateSymbol(); @@ -889,32 +906,35 @@ void GlobePlugin::updateLayers() QgsRectangle dirtyExtent = getQGISLayerExtent(); mLayerExtents.clear(); - QStringList drapedLayers; - QStringList selectedLayers = mDockWidget->getSelectedLayers(); + QList drapedLayers; + QStringList selectedLayerIds = mDockWidget->getSelectedLayerIds(); // Disconnect any previous repaintRequested signals - foreach ( const QString &layerId, mTileSource->layerSet() ) + for ( QgsMapLayer *mapLayer : mTileSource->layers() ) { - QgsMapLayer *mapLayer = QgsProject::instance()->mapLayer( layerId ); if ( mapLayer ) disconnect( mapLayer, SIGNAL( repaintRequested() ), this, SLOT( layerChanged() ) ); if ( dynamic_cast( mapLayer ) ) disconnect( static_cast( mapLayer ), SIGNAL( layerTransparencyChanged( int ) ), this, SLOT( layerChanged() ) ); } osgEarth::ModelLayerVector modelLayers; +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + mMapNode->getMap()->getLayers( modelLayers ); +#else mMapNode->getMap()->getModelLayers( modelLayers ); - foreach ( const osg::ref_ptr &modelLayer, modelLayers ) +#endif + for ( const osg::ref_ptr &modelLayer : modelLayers ) { QgsMapLayer *mapLayer = QgsProject::instance()->mapLayer( QString::fromStdString( modelLayer->getName() ) ); if ( mapLayer ) disconnect( mapLayer, SIGNAL( repaintRequested() ), this, SLOT( layerChanged() ) ); if ( dynamic_cast( mapLayer ) ) disconnect( static_cast( mapLayer ), SIGNAL( layerTransparencyChanged( int ) ), this, SLOT( layerChanged() ) ); - if ( !selectedLayers.contains( QString::fromStdString( modelLayer->getName() ) ) ) + if ( !selectedLayerIds.contains( QString::fromStdString( modelLayer->getName() ) ) ) mMapNode->getMap()->removeModelLayer( modelLayer ); } - Q_FOREACH ( const QString &layerId, selectedLayers ) + for ( const QString &layerId : selectedLayerIds ) { QgsMapLayer *mapLayer = QgsProject::instance()->mapLayer( layerId ); connect( mapLayer, SIGNAL( repaintRequested() ), this, SLOT( layerChanged() ) ); @@ -928,18 +948,22 @@ void GlobePlugin::updateLayers() if ( layerConfig && ( layerConfig->renderingMode == QgsGlobeVectorLayerConfig::RenderingModeModelSimple || layerConfig->renderingMode == QgsGlobeVectorLayerConfig::RenderingModeModelAdvanced ) ) { +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + if ( !mMapNode->getMap()->getLayerByName( mapLayer->id().toStdString() ) ) +#else if ( !mMapNode->getMap()->getModelLayerByName( mapLayer->id().toStdString() ) ) +#endif addModelLayer( static_cast( mapLayer ), layerConfig ); } else { - drapedLayers.append( mapLayer->id() ); + drapedLayers.append( mapLayer ); QgsRectangle extent = QgsCoordinateTransformCache::instance()->transform( mapLayer->crs().authid(), GEO_EPSG_CRS_AUTHID ).transform( mapLayer->extent() ); mLayerExtents.insert( mapLayer->id(), extent ); } } - mTileSource->setLayerSet( drapedLayers ); + mTileSource->setLayers( drapedLayers ); QgsRectangle newExtent = getQGISLayerExtent(); if ( dirtyExtent.isNull() ) dirtyExtent = newExtent; @@ -970,37 +994,53 @@ void GlobePlugin::layerChanged( QgsMapLayer *mapLayer ) if ( layerConfig && ( layerConfig->renderingMode == QgsGlobeVectorLayerConfig::RenderingModeModelSimple || layerConfig->renderingMode == QgsGlobeVectorLayerConfig::RenderingModeModelAdvanced ) ) { // If was previously a draped layer, refresh the draped layer - if ( mTileSource->layerSet().contains( mapLayer->id() ) ) + if ( mTileSource->layers().contains( mapLayer ) ) { - QStringList layerSet = mTileSource->layerSet(); - layerSet.removeAll( mapLayer->id() ); - mTileSource->setLayerSet( layerSet ); + QList layers = mTileSource->layers(); + layers.removeAll( mapLayer ); + mTileSource->setLayers( layers ); QgsRectangle dirtyExtent = mLayerExtents[mapLayer->id()]; mLayerExtents.remove( mapLayer->id() ); refreshQGISMapLayer( dirtyExtent ); } +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + mMapNode->getMap()->removeLayer( mMapNode->getMap()->getLayerByName( mapLayer->id().toStdString() ) ); +#else mMapNode->getMap()->removeModelLayer( mMapNode->getMap()->getModelLayerByName( mapLayer->id().toStdString() ) ); +#endif addModelLayer( static_cast( mapLayer ), layerConfig ); } else { // Re-insert into layer set if necessary - if ( !mTileSource->layerSet().contains( mapLayer->id() ) ) + if ( !mTileSource->layers().contains( mapLayer ) ) { - QStringList layerSet; - foreach ( const QString &layer, mDockWidget->getSelectedLayers() ) + QList layers; + for ( const QString &layerId : mDockWidget->getSelectedLayerIds() ) { - if ( ! mMapNode->getMap()->getModelLayerByName( layer.toStdString() ) ) +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + if ( ! mMapNode->getMap()->getLayerByName( layerId.toStdString() ) ) +#else + if ( ! mMapNode->getMap()->getModelLayerByName( layerId.toStdString() ) ) +#endif { - layerSet.append( layer ); + QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerId ); + if ( layer ) + { + layers.append( layer ); + } } } - mTileSource->setLayerSet( layerSet ); + mTileSource->setLayers( layers ); QgsRectangle extent = QgsCoordinateTransformCache::instance()->transform( mapLayer->crs().authid(), GEO_EPSG_CRS_AUTHID ).transform( mapLayer->extent() ); mLayerExtents.insert( mapLayer->id(), extent ); } // Remove any model layer of that layer, in case one existed +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + mMapNode->getMap()->removeLayer( mMapNode->getMap()->getLayerByName( mapLayer->id().toStdString() ) ); +#else mMapNode->getMap()->removeModelLayer( mMapNode->getMap()->getModelLayerByName( mapLayer->id().toStdString() ) ); +#endif QgsRectangle layerExtent = QgsCoordinateTransformCache::instance()->transform( mapLayer->crs().authid(), GEO_EPSG_CRS_AUTHID ).transform( mapLayer->extent() ); QgsRectangle dirtyExtent = layerExtent; if ( mLayerExtents.contains( mapLayer->id() ) ) @@ -1025,11 +1065,19 @@ void GlobePlugin::rebuildQGISLayer() osgEarth::TileSourceOptions opts; opts.L2CacheSize() = 0; +#if OSGEARTH_VERSION_LESS_THAN( 2, 9, 0 ) opts.tileSize() = 128; - mTileSource = new QgsGlobeTileSource( opts ); +#endif + mTileSource = new QgsGlobeTileSource( mQGisIface->mapCanvas(), opts ); +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + mTileSource->open(); +#endif osgEarth::ImageLayerOptions options( "QGIS" ); options.driver()->L2CacheSize() = 0; +#if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 9, 0 ) + options.tileSize() = 128; +#endif options.cachePolicy() = osgEarth::CachePolicy::USAGE_NO_CACHE; mQgisMapLayer = new osgEarth::ImageLayer( options, mTileSource ); mMapNode->getMap()->addImageLayer( mQgisMapLayer ); @@ -1056,6 +1104,7 @@ void GlobePlugin::reset() mActionToggleGlobe->setChecked( false ); mActionToggleGlobe->blockSignals( false ); mMapNode->getMap()->removeImageLayer( mQgisMapLayer ); // abort any rendering + mTileSource->waitForFinished(); mOsgViewer = 0; mMapNode = 0; mRootNode = 0; diff --git a/src/plugins/globe/globe_plugin.h b/src/plugins/globe/globe_plugin.h index 256d2102816..6a825c87451 100644 --- a/src/plugins/globe/globe_plugin.h +++ b/src/plugins/globe/globe_plugin.h @@ -125,6 +125,7 @@ class GLOBE_EXPORT GlobePlugin : public QObject, public QgisPlugin osgEarth::QtGui::ViewerWidget *mViewerWidget = nullptr; QgsGlobeWidget *mDockWidget = nullptr; QgsGlobePluginDialog *mSettingsDialog = nullptr; + QString mBaseLayerUrl; QList mImagerySources; QList mElevationSources; diff --git a/src/plugins/globe/qgsglobefeatureidentify.cpp b/src/plugins/globe/qgsglobefeatureidentify.cpp index c6b8f5c6ac3..78e58142304 100644 --- a/src/plugins/globe/qgsglobefeatureidentify.cpp +++ b/src/plugins/globe/qgsglobefeatureidentify.cpp @@ -14,13 +14,11 @@ ***************************************************************************/ #include "qgsglobefeatureidentify.h" - #include "qgsmapcanvas.h" #include "qgsproject.h" -#include -#include -#include - +#include "qgsrubberband.h" +#include "qgsvectorlayer.h" +#include "qgslogger.h" #include "featuresource/qgsglobefeaturesource.h" #include diff --git a/src/plugins/globe/qgsglobefrustumhighlight.cpp b/src/plugins/globe/qgsglobefrustumhighlight.cpp index 96fe1fc7e88..b70df03023f 100644 --- a/src/plugins/globe/qgsglobefrustumhighlight.cpp +++ b/src/plugins/globe/qgsglobefrustumhighlight.cpp @@ -22,7 +22,7 @@ #include "qgsglobefrustumhighlight.h" QgsGlobeFrustumHighlightCallback::QgsGlobeFrustumHighlightCallback( osg::View *view, osgEarth::Terrain *terrain, QgsMapCanvas *mapCanvas, QColor color ) - : osg::NodeCallback() + : osg::Callback() , mView( view ) , mTerrain( terrain ) , mRubberBand( new QgsRubberBand( mapCanvas, QgsWkbTypes::PolygonGeometry ) ) @@ -36,23 +36,33 @@ QgsGlobeFrustumHighlightCallback::~QgsGlobeFrustumHighlightCallback() delete mRubberBand; } -void QgsGlobeFrustumHighlightCallback::operator()( osg::Node *, osg::NodeVisitor * ) +bool QgsGlobeFrustumHighlightCallback::run( osg::Object *object, osg::Object *data ) { - const osg::Viewport::value_type &width = mView->getCamera()->getViewport()->width(); - const osg::Viewport::value_type &height = mView->getCamera()->getViewport()->height(); - - osg::Vec3d corners[4]; - - mTerrain->getWorldCoordsUnderMouse( mView, 0, 0, corners[0] ); - mTerrain->getWorldCoordsUnderMouse( mView, 0, height - 1, corners[1] ); - mTerrain->getWorldCoordsUnderMouse( mView, width - 1, height - 1, corners[2] ); - mTerrain->getWorldCoordsUnderMouse( mView, width - 1, 0, corners[3] ); - - mRubberBand->reset( QgsWkbTypes::PolygonGeometry ); - for ( int i = 0; i < 4; i++ ) + osg::Node *node = dynamic_cast( object ); + osg::NodeVisitor *nv = dynamic_cast( data ); + if ( node && nv ) { - osg::Vec3d localCoords; - mSrs->transformFromWorld( corners[i], localCoords ); - mRubberBand->addPoint( QgsPointXY( localCoords.x(), localCoords.y() ) ); + const osg::Viewport::value_type &width = mView->getCamera()->getViewport()->width(); + const osg::Viewport::value_type &height = mView->getCamera()->getViewport()->height(); + + osg::Vec3d corners[4]; + + mTerrain->getWorldCoordsUnderMouse( mView, 0, 0, corners[0] ); + mTerrain->getWorldCoordsUnderMouse( mView, 0, height - 1, corners[1] ); + mTerrain->getWorldCoordsUnderMouse( mView, width - 1, height - 1, corners[2] ); + mTerrain->getWorldCoordsUnderMouse( mView, width - 1, 0, corners[3] ); + + mRubberBand->reset( QgsWkbTypes::PolygonGeometry ); + for ( int i = 0; i < 4; i++ ) + { + osg::Vec3d localCoords; + mSrs->transformFromWorld( corners[i], localCoords ); + mRubberBand->addPoint( QgsPointXY( localCoords.x(), localCoords.y() ) ); + } + return true; + } + else + { + return traverse( object, data ); } } diff --git a/src/plugins/globe/qgsglobefrustumhighlight.h b/src/plugins/globe/qgsglobefrustumhighlight.h index b536274be8e..48f9a56c443 100644 --- a/src/plugins/globe/qgsglobefrustumhighlight.h +++ b/src/plugins/globe/qgsglobefrustumhighlight.h @@ -16,7 +16,7 @@ #ifndef QGSGLOBEFRUSTUMHIGHLIGHT_H #define QGSGLOBEFRUSTUMHIGHLIGHT_H -#include +#include class QgsRubberBand; namespace osg { class View; } @@ -26,13 +26,13 @@ namespace osgEarth class SpatialReference; } -class QgsGlobeFrustumHighlightCallback : public osg::NodeCallback +struct QgsGlobeFrustumHighlightCallback : public osg::Callback { public: QgsGlobeFrustumHighlightCallback( osg::View *view, osgEarth::Terrain *terrain, QgsMapCanvas *mapCanvas, QColor color ); ~QgsGlobeFrustumHighlightCallback(); - void operator()( osg::Node *, osg::NodeVisitor * ) override; + bool run( osg::Object *object, osg::Object *data ) override; private: osg::View *mView = nullptr; diff --git a/src/plugins/globe/qgsglobeplugindialog.cpp b/src/plugins/globe/qgsglobeplugindialog.cpp index 306c68527ec..8b27a0e9326 100644 --- a/src/plugins/globe/qgsglobeplugindialog.cpp +++ b/src/plugins/globe/qgsglobeplugindialog.cpp @@ -210,7 +210,7 @@ void QgsGlobePluginDialog::readProjectSettings() { // Imagery settings mImageryTreeView->clear(); - foreach ( const LayerDataSource &ds, getImageryDataSources() ) + for ( const LayerDataSource &ds : getImageryDataSources() ) { QTreeWidgetItem *item = new QTreeWidgetItem( QStringList() << ds.type << ds.uri ); item->setFlags( item->flags() & ~Qt::ItemIsDropEnabled ); @@ -220,7 +220,7 @@ void QgsGlobePluginDialog::readProjectSettings() // Elevation settings mElevationTreeView->clear(); - foreach ( const LayerDataSource &ds, getElevationDataSources() ) + for ( const LayerDataSource &ds : getElevationDataSources() ) { QTreeWidgetItem *item = new QTreeWidgetItem( QStringList() << ds.type << ds.uri ); item->setFlags( item->flags() & ~Qt::ItemIsDropEnabled ); @@ -236,7 +236,7 @@ void QgsGlobePluginDialog::readProjectSettings() groupBoxSky->setChecked( QgsProject::instance()->readBoolEntry( "Globe-Plugin", "/skyEnabled", true ) ); checkBoxDateTime->setChecked( QgsProject::instance()->readBoolEntry( "Globe-Plugin", "/overrideDateTime", false ) ); dateTimeEditSky->setDateTime( QDateTime::fromString( QgsProject::instance()->readEntry( "Globe-Plugin", "/skyDateTime", QDateTime::currentDateTime().toString() ) ) ); - checkBoxSkyAutoAmbient->setChecked( QgsProject::instance()->readBoolEntry( "Globe-Plugin", "/skyAutoAmbient", true ) ); + checkBoxSkyAutoAmbient->setChecked( QgsProject::instance()->readBoolEntry( "Globe-Plugin", "/skyAutoAmbient", false ) ); horizontalSliderMinAmbient->setValue( QgsProject::instance()->readDoubleEntry( "Globe-Plugin", "/skyMinAmbient", 30. ) ); } @@ -485,7 +485,7 @@ QDateTime QgsGlobePluginDialog::getSkyDateTime() const bool QgsGlobePluginDialog::getSkyAutoAmbience() const { - return QgsProject::instance()->readBoolEntry( "Globe-Plugin", "/skyAutoAmbient", true ); + return QgsProject::instance()->readBoolEntry( "Globe-Plugin", "/skyAutoAmbient", false ); } double QgsGlobePluginDialog::getSkyMinAmbient() const diff --git a/src/plugins/globe/qgsglobeplugindialog.ui b/src/plugins/globe/qgsglobeplugindialog.ui index 7d20b23e14b..f043021d8c5 100644 --- a/src/plugins/globe/qgsglobeplugindialog.ui +++ b/src/plugins/globe/qgsglobeplugindialog.ui @@ -68,7 +68,7 @@ - Override Date / Time (UTC) + Override Date / Time (UTC): @@ -229,7 +229,7 @@ - Vertical scale + Vertical scale: @@ -602,7 +602,7 @@ - Sensitivity + Sensitivity: diff --git a/src/plugins/globe/qgsglobetilesource.cpp b/src/plugins/globe/qgsglobetilesource.cpp index 2fc2cb3a9b0..d69e0d30844 100644 --- a/src/plugins/globe/qgsglobetilesource.cpp +++ b/src/plugins/globe/qgsglobetilesource.cpp @@ -18,10 +18,11 @@ #include #include -#include "qgscoordinatereferencesystem.h" +#include "qgscrscache.h" #include "qgsglobetilesource.h" #include "qgscoordinatetransform.h" #include "qgslogger.h" +#include "qgsmapcanvas.h" #include "qgsmaprenderercustompainterjob.h" #include "qgsmaprendererparalleljob.h" @@ -73,7 +74,7 @@ QgsGlobeTileImage::QgsGlobeTileImage( QgsGlobeTileSource *tileSource, const QgsR #else QImage qImage( mTileData, mTileSize, mTileSize, QImage::Format_ARGB32_Premultiplied ); QPainter painter( &qImage ); - QgsMapRendererCustomPainterJob job( createSettings( qImage.logicalDpiX(), mTileSource->mLayerSet ), &painter ); + QgsMapRendererCustomPainterJob job( createSettings( qImage.logicalDpiX(), mTileSource->mLayers ), &painter ); job.renderSynchronously(); setImage( mTileSize, mTileSize, 1, 4, // width, height, depth, internal_format @@ -94,18 +95,16 @@ QgsGlobeTileImage::~QgsGlobeTileImage() #endif } -QgsMapSettings QgsGlobeTileImage::createSettings( int dpi, const QStringList &layerSet ) const +QgsMapSettings QgsGlobeTileImage::createSettings( int dpi, const QList &layers ) const { QgsMapSettings settings; settings.setBackgroundColor( QColor( Qt::transparent ) ); settings.setDestinationCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( GEO_EPSG_CRS_AUTHID ) ); - settings.setCrsTransformEnabled( true ); settings.setExtent( mTileExtent ); - settings.setLayers( layerSet ); + settings.setLayers( layers ); settings.setFlag( QgsMapSettings::DrawEditingInfo, false ); settings.setFlag( QgsMapSettings::DrawLabeling, false ); settings.setFlag( QgsMapSettings::DrawSelection, false ); - settings.setMapUnits( QgsUnitTypes::DistanceDegrees ); settings.setOutputSize( QSize( mTileSize, mTileSize ) ); settings.setOutputImageFormat( QImage::Format_ARGB32_Premultiplied ); settings.setOutputDpi( dpi ); @@ -179,6 +178,14 @@ void QgsGlobeTileUpdateManager::removeTile( QgsGlobeTileImage *tile ) } } +void QgsGlobeTileUpdateManager::waitForFinished() const +{ + if ( mRenderer ) + { + mRenderer->waitForFinished(); + } +} + void QgsGlobeTileUpdateManager::start() { if ( mRenderer == 0 && !mTileQueue.isEmpty() ) @@ -187,7 +194,7 @@ void QgsGlobeTileUpdateManager::start() #ifdef GLOBE_SHOW_TILE_STATS QgsGlobeTileStatistics::instance()->updateQueueTileCount( mTileQueue.size() ); #endif - mRenderer = new QgsMapRendererParallelJob( mCurrentTile->createSettings( mCurrentTile->dpi(), mLayerSet ) ); + mRenderer = new QgsMapRendererParallelJob( mCurrentTile->createSettings( mCurrentTile->dpi(), mLayers ) ); connect( mRenderer, SIGNAL( finished() ), this, SLOT( renderingFinished() ) ); mRenderer->start(); } @@ -214,20 +221,31 @@ void QgsGlobeTileUpdateManager::renderingFinished() /////////////////////////////////////////////////////////////////////////////// -QgsGlobeTileSource::QgsGlobeTileSource( const osgEarth::TileSourceOptions &options ) +QgsGlobeTileSource::QgsGlobeTileSource( QgsMapCanvas *canvas, const osgEarth::TileSourceOptions &options ) : TileSource( options ) + , mCanvas( canvas ) { osgEarth::GeoExtent geoextent( osgEarth::SpatialReference::get( "wgs84" ), -180., -90., 180., 90. ); osgEarth::DataExtentList extents; extents.push_back( geoextent ); getDataExtents() = extents; +#if OSGEARTH_VERSION_LESS_THAN(2, 9, 0) dirtyDataExtents(); +#endif } +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) +osgEarth::Status QgsGlobeTileSource::initialize( const osgDB::Options * /*dbOptions*/ ) +#else osgEarth::TileSource::Status QgsGlobeTileSource::initialize( const osgDB::Options * /*dbOptions*/ ) +#endif { setProfile( osgEarth::Registry::instance()->getGlobalGeodeticProfile() ); +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) + return osgEarth::Status( osgEarth::Status::NoError ); +#else return STATUS_OK; +#endif } osg::Image *QgsGlobeTileSource::createImage( const osgEarth::TileKey &key, osgEarth::ProgressCallback *progress ) @@ -250,9 +268,9 @@ osg::Image *QgsGlobeTileSource::createImage( const osgEarth::TileKey &key, osgEa void QgsGlobeTileSource::refresh( const QgsRectangle &dirtyExtent ) { - mTileUpdateManager.updateLayerSet( mLayerSet ); + mTileUpdateManager.updateLayerSet( mLayers ); mTileListLock.lock(); - foreach ( QgsGlobeTileImage *tile, mTiles ) + for ( QgsGlobeTileImage *tile : mTiles ) { if ( tile->extent().intersects( dirtyExtent ) ) { @@ -262,16 +280,6 @@ void QgsGlobeTileSource::refresh( const QgsRectangle &dirtyExtent ) mTileListLock.unlock(); } -void QgsGlobeTileSource::setLayerSet( const QStringList &layerSet ) -{ - mLayerSet = layerSet; -} - -const QStringList &QgsGlobeTileSource::layerSet() const -{ - return mLayerSet; -} - void QgsGlobeTileSource::addTile( QgsGlobeTileImage *tile ) { mTileListLock.lock(); diff --git a/src/plugins/globe/qgsglobetilesource.h b/src/plugins/globe/qgsglobetilesource.h index c2f3947dad2..7d9674cbf92 100644 --- a/src/plugins/globe/qgsglobetilesource.h +++ b/src/plugins/globe/qgsglobetilesource.h @@ -19,6 +19,7 @@ #define QGSGLOBETILESOURCE_H #include +#include #include #include #include @@ -30,6 +31,7 @@ class QgsCoordinateTransform; class QgsMapCanvas; +class QgsMapLayer; class QgsMapRenderer; class QgsMapSettings; class QgsGlobeTileSource; @@ -61,7 +63,7 @@ class QgsGlobeTileImage : public osg::Image QgsGlobeTileImage( QgsGlobeTileSource *tileSource, const QgsRectangle &tileExtent, int tileSize, int tileLod ); ~QgsGlobeTileImage(); bool requiresUpdateCall() const { return !mUpdatedImage.isNull(); } - QgsMapSettings createSettings( int dpi, const QStringList &layerSet ) const; + QgsMapSettings createSettings( int dpi, const QList &layers ) const; void setUpdatedImage( const QImage &image ) { mUpdatedImage = image; } int dpi() const { return mDpi; } const QgsRectangle &extent() { return mTileExtent; } @@ -71,7 +73,7 @@ class QgsGlobeTileImage : public osg::Image static bool lodSort( const QgsGlobeTileImage *lhs, const QgsGlobeTileImage *rhs ) { return lhs->mLod > rhs->mLod; } private: - QgsGlobeTileSource *mTileSource = nullptr; + osg::ref_ptr mTileSource; QgsRectangle mTileExtent; int mTileSize; unsigned char *mTileData; @@ -86,16 +88,17 @@ class QgsGlobeTileUpdateManager : public QObject public: QgsGlobeTileUpdateManager( QObject *parent = 0 ); ~QgsGlobeTileUpdateManager(); - void updateLayerSet( const QStringList &layerSet ) { mLayerSet = layerSet; } + void updateLayerSet( const QList &layers ) { mLayers = layers; } void addTile( QgsGlobeTileImage *tile ); void removeTile( QgsGlobeTileImage *tile ); + void waitForFinished() const; signals: void startRendering(); void cancelRendering(); private: - QStringList mLayerSet; + QList mLayers; QList mTileQueue; QgsGlobeTileImage *mCurrentTile = nullptr; QgsMapRendererParallelJob *mRenderer = nullptr; @@ -109,23 +112,34 @@ class QgsGlobeTileUpdateManager : public QObject class QgsGlobeTileSource : public osgEarth::TileSource { public: - QgsGlobeTileSource( const osgEarth::TileSourceOptions &options = osgEarth::TileSourceOptions() ); + QgsGlobeTileSource( QgsMapCanvas *canvas, const osgEarth::TileSourceOptions &options = osgEarth::TileSourceOptions() ); +#if OSGEARTH_VERSION_GREATER_OR_EQUAL(2, 8, 0) + osgEarth::Status initialize( const osgDB::Options *dbOptions ) override; +#else Status initialize( const osgDB::Options *dbOptions ) override; +#endif osg::Image *createImage( const osgEarth::TileKey &key, osgEarth::ProgressCallback *progress ) override; osg::HeightField *createHeightField( const osgEarth::TileKey &/*key*/, osgEarth::ProgressCallback * /*progress*/ ) override { return 0; } bool isDynamic() const override { return true; } + osgEarth::CachePolicy getCachePolicyHint( const osgEarth::Profile * /*profile*/ ) const override { return osgEarth::CachePolicy::NO_CACHE; } void refresh( const QgsRectangle &dirtyExtent ); - void setLayerSet( const QStringList &layerSet ); - const QStringList &layerSet() const; + void setLayers( const QList &layers ) { mLayers = layers; } + const QList &layers() const { return mLayers; } + + void waitForFinished() const + { + mTileUpdateManager.waitForFinished(); + } private: friend class QgsGlobeTileImage; QMutex mTileListLock; QList mTiles; - QStringList mLayerSet; + QgsMapCanvas *mCanvas = nullptr; + QList mLayers; QgsGlobeTileUpdateManager mTileUpdateManager; void addTile( QgsGlobeTileImage *tile ); diff --git a/src/plugins/globe/qgsglobevectorlayerproperties.cpp b/src/plugins/globe/qgsglobevectorlayerproperties.cpp index e4d64eae3e3..eb5c7444c6f 100644 --- a/src/plugins/globe/qgsglobevectorlayerproperties.cpp +++ b/src/plugins/globe/qgsglobevectorlayerproperties.cpp @@ -97,15 +97,11 @@ QgsGlobeVectorLayerPropertiesPage::QgsGlobeVectorLayerPropertiesPage( QgsVectorL checkBoxExtrusionFlatten->setChecked( layerConfig->extrusionFlatten ); spinBoxExtrusionWallGradient->setValue( layerConfig->extrusionWallGradient ); -#if OSGEARTH_VERSION_LESS_THAN(2, 7, 0) +#if OSGEARTH_VERSION_LESS_THAN(2, 7, 0) || OSGEARTH_VERSION_GREATER_THAN(2, 9, 0) groupBoxLabelingEnabled->setChecked( layerConfig->labelingEnabled ); checkBoxLabelingDeclutter->setChecked( layerConfig->labelingDeclutter ); #else -#ifdef _MSC_VER -#pragma message("TODO: labeling broken with osgEarth 2.7") -#else -#warning "TODO: labeling broken with osgEarth 2.7" -#endif + // labeling broken with osgEarth 2.8 groupBoxLabelingEnabled->setChecked( false ); checkBoxLabelingDeclutter->setChecked( false ); groupBoxLabelingEnabled->setVisible( false ); diff --git a/src/plugins/globe/qgsglobevectorlayerproperties.h b/src/plugins/globe/qgsglobevectorlayerproperties.h index 753cecdaa91..fe8365eb71f 100644 --- a/src/plugins/globe/qgsglobevectorlayerproperties.h +++ b/src/plugins/globe/qgsglobevectorlayerproperties.h @@ -32,6 +32,7 @@ class QListWidgetItem; class QgsGlobeVectorLayerConfig : public QObject { + Q_OBJECT public: enum RenderingMode { diff --git a/src/plugins/globe/qgsglobevectorlayerpropertiespage.ui b/src/plugins/globe/qgsglobevectorlayerpropertiespage.ui index 31b0156f813..0cef50d4fbf 100644 --- a/src/plugins/globe/qgsglobevectorlayerpropertiespage.ui +++ b/src/plugins/globe/qgsglobevectorlayerpropertiespage.ui @@ -17,7 +17,7 @@ - 0 + 1 @@ -233,7 +233,7 @@ - Rendering mode + Rendering mode: diff --git a/src/plugins/globe/qgsglobewidget.cpp b/src/plugins/globe/qgsglobewidget.cpp index a173628af5d..35370092a35 100644 --- a/src/plugins/globe/qgsglobewidget.cpp +++ b/src/plugins/globe/qgsglobewidget.cpp @@ -1,7 +1,7 @@ /*************************************************************************** qgsglobewidget.cpp --------------------- - begin : August 2010 + begin : August 2016 copyright : (C) 2016 Sandro Mani email : smani at sourcepole dot ch *************************************************************************** @@ -94,25 +94,25 @@ QgsGlobeWidget::QgsGlobeWidget( QgisInterface *iface, QWidget *parent ) void QgsGlobeWidget::updateLayerSelectionMenu() { QStringList prevLayers; - QStringList prevDisabledLayers; - QStringList prevEnabledLayers; - foreach ( QAction *action, mLayerSelectionMenu->actions() ) + QStringList prevDisabledLayerIds; + QStringList prevEnabledLayerIds; + for ( QAction *action : mLayerSelectionMenu->actions() ) { prevLayers.append( action->data().toString() ); if ( !action->isChecked() ) { - prevDisabledLayers.append( action->data().toString() ); + prevDisabledLayerIds.append( action->data().toString() ); } else { - prevEnabledLayers.append( action->data().toString() ); + prevEnabledLayerIds.append( action->data().toString() ); } } mLayerSelectionMenu->clear(); QString heightmap = QgsProject::instance()->readEntry( "Heightmap", "layer" ); // Use layerTreeRoot to get layers ordered as in the layer tree - foreach ( QgsLayerTreeLayer *layerTreeLayer, QgsProject::instance()->layerTreeRoot()->findLayers() ) + for ( QgsLayerTreeLayer *layerTreeLayer : QgsProject::instance()->layerTreeRoot()->findLayers() ) { QgsMapLayer *layer = layerTreeLayer->layer(); if ( !layer ) @@ -121,7 +121,7 @@ void QgsGlobeWidget::updateLayerSelectionMenu() layerAction->setData( layer->id() ); // Check if was not previously unchecked, unless it is a new layer with url=http in datasource layerAction->setCheckable( true ); - bool wasUnchecked = prevDisabledLayers.contains( layer->id() ); + bool wasUnchecked = prevDisabledLayerIds.contains( layer->id() ); bool isNew = !prevLayers.contains( layer->id() ); bool isRemote = layer->source().contains( "url=http" ); bool isHeightmap = layer->id() == heightmap; @@ -129,21 +129,21 @@ void QgsGlobeWidget::updateLayerSelectionMenu() connect( layerAction, SIGNAL( toggled( bool ) ), this, SIGNAL( layersChanged() ) ); mLayerSelectionMenu->addAction( layerAction ); } - if ( prevEnabledLayers != getSelectedLayers() ) + if ( prevEnabledLayerIds != getSelectedLayerIds() ) emit layersChanged(); } -QStringList QgsGlobeWidget::getSelectedLayers() const +QStringList QgsGlobeWidget::getSelectedLayerIds() const { - QStringList selectedLayers; - foreach ( QAction *layerAction, mLayerSelectionMenu->actions() ) + QStringList selectedLayerIds; + for ( QAction *layerAction : mLayerSelectionMenu->actions() ) { if ( layerAction->isChecked() ) { - selectedLayers.append( layerAction->data().toString() ); + selectedLayerIds.append( layerAction->data().toString() ); } } - return selectedLayers; + return selectedLayerIds; } void QgsGlobeWidget::contextMenuEvent( QContextMenuEvent *e ) diff --git a/src/plugins/globe/qgsglobewidget.h b/src/plugins/globe/qgsglobewidget.h index 5a81380e9fd..fef60431063 100644 --- a/src/plugins/globe/qgsglobewidget.h +++ b/src/plugins/globe/qgsglobewidget.h @@ -1,7 +1,7 @@ /*************************************************************************** qgsglobewidget.h --------------------- - begin : August 2010 + begin : August 2016 copyright : (C) 2016 Sandro Mani email : smani at sourcepole dot ch *************************************************************************** @@ -24,7 +24,7 @@ class QgsGlobeWidget : public QDockWidget Q_OBJECT public: QgsGlobeWidget( QgisInterface *iface, QWidget *parent = 0 ); - QStringList getSelectedLayers() const; + QStringList getSelectedLayerIds() const; signals: void layersChanged(); From e21630db42a71b3a8136573e962bbfdb2ddca5c5 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 4 Sep 2017 21:08:11 +0200 Subject: [PATCH 311/364] Sipify --- python/gui/qgsabstractdatasourcewidget.sip | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/gui/qgsabstractdatasourcewidget.sip b/python/gui/qgsabstractdatasourcewidget.sip index 8806f36bd4d..ddd83953a69 100644 --- a/python/gui/qgsabstractdatasourcewidget.sip +++ b/python/gui/qgsabstractdatasourcewidget.sip @@ -95,8 +95,8 @@ Emitted when a vector layer has been selected for addition Emitted when a layer needs to be replaced \param oldId old layer ID \param source URI of the layer - \params name of the layer - \params provider key + \param name of the layer + \param provider key %End From 1db62ac600d822d2cf347abcdb0c43655ad0e98d Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 4 Sep 2017 15:04:38 +0200 Subject: [PATCH 312/364] Make sure classes do not miss their key function --- src/core/layout/qgslayoutitemregistry.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/layout/qgslayoutitemregistry.h b/src/core/layout/qgslayoutitemregistry.h index 1ae53579bfc..6148fbe1ceb 100644 --- a/src/core/layout/qgslayoutitemregistry.h +++ b/src/core/layout/qgslayoutitemregistry.h @@ -263,7 +263,13 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject #ifndef SIP_RUN ///@cond TEMPORARY //simple item for testing +#ifdef ANDROID +// For some reason, the Android NDK toolchain requires this to link properly. +// Note to self: Please try to remove this again once Qt ships their libs built with gcc-5 +class CORE_EXPORT TestLayoutItem : public QgsLayoutItem +#else class TestLayoutItem : public QgsLayoutItem +#endif { Q_OBJECT @@ -274,15 +280,13 @@ class TestLayoutItem : public QgsLayoutItem //implement pure virtual methods int type() const override { return QgsLayoutItemRegistry::LayoutItem + 102; } - QString stringType() const override { return QStringLiteral( "ItemTest" ); } + virtual QString stringType() const override { return QStringLiteral( "ItemTest" ); } void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = nullptr ) override; private: QColor mColor; QgsFillSymbol *mShapeStyleSymbol = nullptr; }; - - ///@endcond #endif From c241c6a1d04438300069df101093ff2b7d69857d Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 4 Sep 2017 17:35:47 +0200 Subject: [PATCH 313/364] Fix include path usage from 3rd party --- src/core/layertree/qgslayertreemodellegendnode.h | 4 ++-- src/plugins/offline_editing/CMakeLists.txt | 1 + src/providers/virtual/CMakeLists.txt | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/layertree/qgslayertreemodellegendnode.h b/src/core/layertree/qgslayertreemodellegendnode.h index 00843bbd2b7..8008b12f30b 100644 --- a/src/core/layertree/qgslayertreemodellegendnode.h +++ b/src/core/layertree/qgslayertreemodellegendnode.h @@ -26,7 +26,7 @@ #include "qgis_core.h" #include "qgis.h" -#include "raster/qgsrasterdataprovider.h" // for QgsImageFetcher dtor visibility +#include "qgsrasterdataprovider.h" // for QgsImageFetcher dtor visibility class QgsLayerTreeLayer; class QgsLayerTreeModel; @@ -140,7 +140,7 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject QString mUserLabel; }; -#include "symbology/qgslegendsymbolitem.h" +#include "qgslegendsymbolitem.h" /** \ingroup core * Implementation of legend node interface for displaying preview of vector symbols and their labels diff --git a/src/plugins/offline_editing/CMakeLists.txt b/src/plugins/offline_editing/CMakeLists.txt index d3eec183714..4e6b6c0a724 100644 --- a/src/plugins/offline_editing/CMakeLists.txt +++ b/src/plugins/offline_editing/CMakeLists.txt @@ -56,6 +56,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/metadata ${CMAKE_SOURCE_DIR}/src/core/layertree ${CMAKE_SOURCE_DIR}/src/core/raster + ${CMAKE_SOURCE_DIR}/src/core/symbology ${CMAKE_SOURCE_DIR}/src/gui ${CMAKE_SOURCE_DIR}/src/gui/layertree ${CMAKE_SOURCE_DIR}/src/plugins diff --git a/src/providers/virtual/CMakeLists.txt b/src/providers/virtual/CMakeLists.txt index aee9cc97403..c19a28b9972 100644 --- a/src/providers/virtual/CMakeLists.txt +++ b/src/providers/virtual/CMakeLists.txt @@ -39,6 +39,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/expression ${CMAKE_SOURCE_DIR}/src/core/geometry ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/raster + ${CMAKE_SOURCE_DIR}/src/core/symbology ${CMAKE_SOURCE_DIR}/src/gui ${CMAKE_SOURCE_DIR}/src/gui/auth From ddb3198ef2888c2a552c074096744f8973293700 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 09:17:46 +1000 Subject: [PATCH 314/364] Add some more debugging to processing algorithm test Trying to pinpoint which algorithm occasionally segfaults during test runs --- python/plugins/processing/tests/AlgorithmsTestBase.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index c0620350994..e8f294ec140 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -83,7 +83,8 @@ class AlgorithmsTest(object): algorithm_tests = yaml.load(stream) if 'tests' in algorithm_tests and algorithm_tests['tests'] is not None: - for algtest in algorithm_tests['tests']: + for idx, algtest in enumerate(algorithm_tests['tests']): + print('About to start {} of {}: "{}"'.format(idx, len(algorithm_tests['tests']), algtest['name'])) yield self.check_algorithm, algtest['name'], algtest def check_algorithm(self, name, defs): @@ -102,6 +103,7 @@ class AlgorithmsTest(object): alg = ScriptAlgorithm(filePath) alg.initAlgorithm() else: + print('Running alg: "{}"'.format(defs['algorithm'])) alg = QgsApplication.processingRegistry().createAlgorithmById(defs['algorithm']) parameters = {} From b0f71e5c43a8658ac94a7538217428b4f5bebcf7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 12:23:49 +1000 Subject: [PATCH 315/364] Daily Q_FOREACH -> for conversion --- src/core/expression/qgsexpressionfunction.cpp | 37 +++++++------ src/core/expression/qgsexpressionnodeimpl.cpp | 53 +++++++++++-------- 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index ed7d148975d..ef059390d43 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -54,7 +54,8 @@ QVariant QgsExpressionFunction::run( QgsExpressionNode::NodeList *args, const Qg if ( args ) { int arg = 0; - Q_FOREACH ( QgsExpressionNode *n, args->list() ) + const QList< QgsExpressionNode * > argList = args->list(); + for ( QgsExpressionNode *n : argList ) { QVariant v; if ( lazyEval() ) @@ -201,7 +202,8 @@ bool QgsExpressionFunction::allParamsStatic( const QgsExpressionNodeFunction *no { if ( node && node->args() ) { - Q_FOREACH ( QgsExpressionNode *argNode, node->args()->list() ) + const QList< QgsExpressionNode * > argList = node->args()->list(); + for ( QgsExpressionNode *argNode : argList ) { if ( !argNode->isStatic( parent, context ) ) return false; @@ -891,7 +893,7 @@ static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionCo static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) { - Q_FOREACH ( const QVariant &value, values ) + for ( const QVariant &value : values ) { if ( value.isNull() ) continue; @@ -1329,7 +1331,7 @@ static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionC static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { QString concat; - Q_FOREACH ( const QVariant &value, values ) + for ( const QVariant &value : values ) { concat += QgsExpressionUtils::getStringValue( value, parent ); } @@ -1718,9 +1720,10 @@ static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressio QgsMultiPointV2 *mp = new QgsMultiPointV2(); - Q_FOREACH ( const QgsRingSequence &part, geom.geometry()->coordinateSequence() ) + const QgsCoordinateSequence sequence = geom.geometry()->coordinateSequence(); + for ( const QgsRingSequence &part : sequence ) { - Q_FOREACH ( const QgsPointSequence &ring, part ) + for ( const QgsPointSequence &ring : part ) { bool skipLast = false; if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() ) @@ -1745,11 +1748,11 @@ static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpress if ( geom.isNull() ) return QVariant(); - QList< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.geometry() ); + const QList< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.geometry() ); //OK, now we have a complete list of segmentized lines from the geometry QgsMultiLineString *ml = new QgsMultiLineString(); - Q_FOREACH ( QgsLineString *line, linesToProcess ) + for ( QgsLineString *line : linesToProcess ) { for ( int i = 0; i < line->numPoints() - 1; ++i ) { @@ -1932,7 +1935,7 @@ static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionCont QgsLineString *lineString = new QgsLineString(); lineString->clear(); - Q_FOREACH ( const QVariant &value, values ) + for ( const QVariant &value : values ) { QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent ); if ( geom.isNull() ) @@ -1987,7 +1990,7 @@ static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpression std::unique_ptr lineString( new QgsLineString() ); lineString->clear(); - Q_FOREACH ( const QVariant &value, values ) + for ( const QVariant &value : values ) { QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent ); if ( geom.isNull() ) @@ -2822,7 +2825,7 @@ static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionCo while ( orderedGeom->partCount() ) orderedGeom->removeGeometry( 0 ); - Q_FOREACH ( const QgsFeature &feature, partFeatures ) + for ( const QgsFeature &feature : qgsAsConst( partFeatures ) ) { orderedGeom->addGeometry( feature.geometry().geometry()->clone() ); } @@ -3624,7 +3627,7 @@ static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressi static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { QVariantList list; - Q_FOREACH ( const QVariant &cur, values ) + for ( const QVariant &cur : values ) { list += QgsExpressionUtils::getListValue( cur, parent ); } @@ -3669,7 +3672,8 @@ static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpression static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent ); - Q_FOREACH ( const QVariant &cur, QgsExpressionUtils::getListValue( values.at( 1 ), parent ) ) + const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent ); + for ( const QVariant &cur : array2 ) { if ( array1.contains( cur ) ) return QVariant( true ); @@ -3768,7 +3772,7 @@ static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionCon static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) { QVariantMap result; - Q_FOREACH ( const QVariant &cur, values ) + for ( const QVariant &cur : values ) { const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent ); for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it ) @@ -4153,7 +4157,8 @@ const QList &QgsExpression::Functions() orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) { - Q_FOREACH ( QgsExpressionNode *argNode, node->args()->list() ) + const QList< QgsExpressionNode *> argList = node->args()->list(); + for ( QgsExpressionNode *argNode : argList ) { if ( !argNode->isStatic( parent, context ) ) return false; @@ -4344,7 +4349,7 @@ const QList &QgsExpression::Functions() QgsExpressionContextUtils::registerContextFunctions(); //QgsExpression has ownership of all built-in functions - Q_FOREACH ( QgsExpressionFunction *func, sFunctions ) + for ( QgsExpressionFunction *func : qgsAsConst( sFunctions ) ) { sOwnedFunctions << func; sBuiltinFunctions << func->name(); diff --git a/src/core/expression/qgsexpressionnodeimpl.cpp b/src/core/expression/qgsexpressionnodeimpl.cpp index 91b51128434..8e1ab95566b 100644 --- a/src/core/expression/qgsexpressionnodeimpl.cpp +++ b/src/core/expression/qgsexpressionnodeimpl.cpp @@ -38,7 +38,8 @@ const char *QgsExpressionNodeUnaryOperator::UNARY_OPERATOR_TEXT[] = bool QgsExpressionNodeInOperator::needsGeometry() const { bool needs = false; - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + const QList< QgsExpressionNode * > nodeList = mList->list(); + for ( QgsExpressionNode *n : nodeList ) needs |= n->needsGeometry(); return needs; } @@ -59,7 +60,7 @@ void QgsExpressionNode::NodeList::append( QgsExpressionNode::NamedNode *node ) QgsExpressionNode::NodeList *QgsExpressionNode::NodeList::clone() const { NodeList *nl = new NodeList; - Q_FOREACH ( QgsExpressionNode *node, mList ) + for ( QgsExpressionNode *node : mList ) { nl->mList.append( node->clone() ); } @@ -72,7 +73,7 @@ QString QgsExpressionNode::NodeList::dump() const { QString msg; bool first = true; - Q_FOREACH ( QgsExpressionNode *n, mList ) + for ( QgsExpressionNode *n : mList ) { if ( !first ) msg += QLatin1String( ", " ); else first = false; @@ -749,7 +750,8 @@ QVariant QgsExpressionNodeInOperator::evalNode( QgsExpression *parent, const Qgs bool listHasNull = false; - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + const QList< QgsExpressionNode * > nodeList = mList->list(); + for ( QgsExpressionNode *n : nodeList ) { QVariant v2 = n->eval( parent, context ); ENSURE_NO_EVAL_ERROR; @@ -802,7 +804,8 @@ QgsExpressionNode::NodeType QgsExpressionNodeInOperator::nodeType() const bool QgsExpressionNodeInOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) { bool res = mNode->prepare( parent, context ); - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + const QList< QgsExpressionNode * > nodeList = mList->list(); + for ( QgsExpressionNode *n : nodeList ) { res = res && n->prepare( parent, context ); } @@ -826,7 +829,8 @@ bool QgsExpressionNodeInOperator::isStatic( QgsExpression *parent, const QgsExpr if ( !mNode->isStatic( parent, context ) ) return false; - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + const QList< QgsExpressionNode * > nodeList = mList->list(); + for ( QgsExpressionNode *n : nodeList ) { if ( !n->isStatic( parent, context ) ) return false; @@ -906,7 +910,8 @@ bool QgsExpressionNodeFunction::prepareNode( QgsExpression *parent, const QgsExp bool res = fd->prepare( this, parent, context ); if ( mArgs && !fd->lazyEval() ) { - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + const QList< QgsExpressionNode * > nodeList = mArgs->list(); + for ( QgsExpressionNode *n : nodeList ) { res = res && n->prepare( parent, context ); } @@ -934,7 +939,8 @@ QSet QgsExpressionNodeFunction::referencedColumns() const return functionColumns; } - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + const QList< QgsExpressionNode * > nodeList = mArgs->list(); + for ( QgsExpressionNode *n : nodeList ) { functionColumns.unite( n->referencedColumns() ); } @@ -962,7 +968,8 @@ QSet QgsExpressionNodeFunction::referencedVariables() const if ( !mArgs ) return functionVariables; - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + const QList< QgsExpressionNode * > nodeList = mArgs->list(); + for ( QgsExpressionNode *n : nodeList ) { functionVariables.unite( n->referencedVariables() ); } @@ -976,7 +983,8 @@ bool QgsExpressionNodeFunction::needsGeometry() const bool needs = QgsExpression::QgsExpression::Functions()[mFnIndex]->usesGeometry( this ); if ( mArgs ) { - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + const QList< QgsExpressionNode * > nodeList = mArgs->list(); + for ( QgsExpressionNode *n : nodeList ) needs |= n->needsGeometry(); } return needs; @@ -1044,7 +1052,8 @@ bool QgsExpressionNodeFunction::validateParams( int fnIndex, QgsExpressionNode:: //last check for bad names idx = 0; - Q_FOREACH ( const QString &name, args->names() ) + const QStringList nameList = args->names(); + for ( const QString &name : nameList ) { if ( !name.isEmpty() && !functionParams.contains( name ) ) { @@ -1247,7 +1256,7 @@ QgsExpressionNode::NodeType QgsExpressionNodeCondition::nodeType() const QVariant QgsExpressionNodeCondition::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) { - Q_FOREACH ( WhenThen *cond, mConditions ) + for ( WhenThen *cond : qgsAsConst( mConditions ) ) { QVariant vWhen = cond->mWhenExp->eval( parent, context ); QgsExpressionUtils::TVL tvl = QgsExpressionUtils::getTVLValue( vWhen, parent ); @@ -1274,7 +1283,7 @@ QVariant QgsExpressionNodeCondition::evalNode( QgsExpression *parent, const QgsE bool QgsExpressionNodeCondition::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) { bool res; - Q_FOREACH ( WhenThen *cond, mConditions ) + for ( WhenThen *cond : qgsAsConst( mConditions ) ) { res = cond->mWhenExp->prepare( parent, context ) & cond->mThenExp->prepare( parent, context ); @@ -1291,7 +1300,7 @@ bool QgsExpressionNodeCondition::prepareNode( QgsExpression *parent, const QgsEx QString QgsExpressionNodeCondition::dump() const { QString msg( QStringLiteral( "CASE" ) ); - Q_FOREACH ( WhenThen *cond, mConditions ) + for ( WhenThen *cond : mConditions ) { msg += QStringLiteral( " WHEN %1 THEN %2" ).arg( cond->mWhenExp->dump(), cond->mThenExp->dump() ); } @@ -1304,7 +1313,7 @@ QString QgsExpressionNodeCondition::dump() const QSet QgsExpressionNodeCondition::referencedColumns() const { QSet lst; - Q_FOREACH ( WhenThen *cond, mConditions ) + for ( WhenThen *cond : mConditions ) { lst += cond->mWhenExp->referencedColumns() + cond->mThenExp->referencedColumns(); } @@ -1318,7 +1327,7 @@ QSet QgsExpressionNodeCondition::referencedColumns() const QSet QgsExpressionNodeCondition::referencedVariables() const { QSet lst; - Q_FOREACH ( WhenThen *cond, mConditions ) + for ( WhenThen *cond : mConditions ) { lst += cond->mWhenExp->referencedVariables() + cond->mThenExp->referencedVariables(); } @@ -1331,7 +1340,7 @@ QSet QgsExpressionNodeCondition::referencedVariables() const bool QgsExpressionNodeCondition::needsGeometry() const { - Q_FOREACH ( WhenThen *cond, mConditions ) + for ( WhenThen *cond : mConditions ) { if ( cond->mWhenExp->needsGeometry() || cond->mThenExp->needsGeometry() ) @@ -1347,7 +1356,7 @@ bool QgsExpressionNodeCondition::needsGeometry() const QgsExpressionNode *QgsExpressionNodeCondition::clone() const { WhenThenList conditions; - Q_FOREACH ( WhenThen *wt, mConditions ) + for ( WhenThen *wt : mConditions ) conditions.append( wt->clone() ); QgsExpressionNodeCondition *copy = new QgsExpressionNodeCondition( conditions, mElseExp ? mElseExp->clone() : nullptr ); @@ -1357,7 +1366,7 @@ QgsExpressionNode *QgsExpressionNodeCondition::clone() const bool QgsExpressionNodeCondition::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const { - Q_FOREACH ( WhenThen *wt, mConditions ) + for ( WhenThen *wt : mConditions ) { if ( !wt->mWhenExp->isStatic( parent, context ) || !wt->mThenExp->isStatic( parent, context ) ) return false; @@ -1372,7 +1381,8 @@ bool QgsExpressionNodeCondition::isStatic( QgsExpression *parent, const QgsExpre QSet QgsExpressionNodeInOperator::referencedColumns() const { QSet lst( mNode->referencedColumns() ); - Q_FOREACH ( const QgsExpressionNode *n, mList->list() ) + const QList< QgsExpressionNode * > nodeList = mList->list(); + for ( const QgsExpressionNode *n : nodeList ) lst.unite( n->referencedColumns() ); return lst; } @@ -1380,7 +1390,8 @@ QSet QgsExpressionNodeInOperator::referencedColumns() const QSet QgsExpressionNodeInOperator::referencedVariables() const { QSet lst( mNode->referencedVariables() ); - Q_FOREACH ( const QgsExpressionNode *n, mList->list() ) + const QList< QgsExpressionNode * > nodeList = mList->list(); + for ( const QgsExpressionNode *n : nodeList ) lst.unite( n->referencedVariables() ); return lst; } From 72462ae2eb6cb96c3ad37fb804146f03e1bfc8d3 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Tue, 5 Sep 2017 09:59:40 +0700 Subject: [PATCH 316/364] [symbology] fix duplicate layers in symbol levels dialog (#5041) --- src/gui/symbology/qgssymbollevelsdialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/symbology/qgssymbollevelsdialog.cpp b/src/gui/symbology/qgssymbollevelsdialog.cpp index ee8c1394844..d0600361a6c 100644 --- a/src/gui/symbology/qgssymbollevelsdialog.cpp +++ b/src/gui/symbology/qgssymbollevelsdialog.cpp @@ -30,7 +30,6 @@ QgsSymbolLevelsDialog::QgsSymbolLevelsDialog( const QgsLegendSymbolList &list, bool usingSymbolLevels, QWidget *parent ) : QDialog( parent ) - , mList( list ) , mForceOrderingEnabled( false ) { setupUi( this ); From 25a18b9ca1a6f02024904bafcab95c3cf87a8b38 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 13:52:25 +1000 Subject: [PATCH 317/364] Replace use of deprecated QString::fromAscii --- src/analysis/raster/qgsalignraster.cpp | 2 +- src/app/qgshandlebadlayers.cpp | 2 +- src/core/qgsapplication.cpp | 2 +- src/core/qgsexpressionlexer.ll | 6 +++--- src/core/qgsmaplayer.cpp | 4 ++-- src/core/qgsmaprenderercustompainterjob.cpp | 2 +- src/core/qgsmaprendererparalleljob.cpp | 4 ++-- src/core/qgssqlstatementlexer.ll | 8 ++++---- .../evis/databaseconnection/evisdatabaseconnectiongui.cpp | 2 +- src/providers/delimitedtext/qgsdelimitedtextprovider.cpp | 2 +- .../delimitedtext/qgsdelimitedtextsourceselect.cpp | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/analysis/raster/qgsalignraster.cpp b/src/analysis/raster/qgsalignraster.cpp index 3b96d8e375e..0512ccd8a65 100644 --- a/src/analysis/raster/qgsalignraster.cpp +++ b/src/analysis/raster/qgsalignraster.cpp @@ -568,7 +568,7 @@ QgsAlignRaster::RasterInfo::RasterInfo( const QString &layerpath ) ( void ) GDALGetGeoTransform( mDataset, mGeoTransform ); // TODO: may be null or empty string - mCrsWkt = QString::fromAscii( GDALGetProjectionRef( mDataset ) ); + mCrsWkt = QString::fromLatin1( GDALGetProjectionRef( mDataset ) ); mBandCnt = GDALGetBandNumber( mDataset ); } diff --git a/src/app/qgshandlebadlayers.cpp b/src/app/qgshandlebadlayers.cpp index d211bb35985..3856718da93 100644 --- a/src/app/qgshandlebadlayers.cpp +++ b/src/app/qgshandlebadlayers.cpp @@ -236,7 +236,7 @@ void QgsHandleBadLayers::setFilename( int row, const QString &filename ) QUrl uriSource = QUrl::fromEncoded( datasource.toLatin1() ); QUrl uriDest = QUrl::fromLocalFile( filename ); uriDest.setQueryItems( uriSource.queryItems() ); - datasource = QString::fromAscii( uriDest.toEncoded() ); + datasource = QString::fromLatin1( uriDest.toEncoded() ); } } else diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index f0a2a6bfa80..e7cdf2fcc44 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -318,7 +318,7 @@ bool QgsApplication::notify( QObject *receiver, QEvent *event ) } catch ( std::exception &e ) { - QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) ); + QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) ); if ( qApp->thread() == QThread::currentThread() ) QMessageBox::critical( activeWindow(), tr( "Exception" ), e.what() ); } diff --git a/src/core/qgsexpressionlexer.ll b/src/core/qgsexpressionlexer.ll index 1dc68b5a850..e617598c843 100644 --- a/src/core/qgsexpressionlexer.ll +++ b/src/core/qgsexpressionlexer.ll @@ -186,14 +186,14 @@ string "'"{str_char}*"'" "," { return COMMA; } -{num_float} { yylval->numberFloat = cLocale.toDouble( QString::fromAscii(yytext) ); return NUMBER_FLOAT; } +{num_float} { yylval->numberFloat = cLocale.toDouble( QString::fromLatin1(yytext) ); return NUMBER_FLOAT; } {num_int} { bool ok; - yylval->numberInt = cLocale.toInt( QString::fromAscii(yytext), &ok ); + yylval->numberInt = cLocale.toInt( QString::fromLatin1(yytext), &ok ); if( ok ) return NUMBER_INT; - yylval->numberFloat = cLocale.toDouble( QString::fromAscii(yytext), &ok ); + yylval->numberFloat = cLocale.toDouble( QString::fromLatin1(yytext), &ok ); if( ok ) return NUMBER_FLOAT; diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index d950c5a2693..d0bcaeadc7a 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -257,7 +257,7 @@ bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, const QgsReadWr QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().readPath( urlSource.toLocalFile() ) ); urlDest.setQueryItems( urlSource.queryItems() ); - mDataSource = QString::fromAscii( urlDest.toEncoded() ); + mDataSource = QString::fromLatin1( urlDest.toEncoded() ); } else if ( provider == QLatin1String( "wms" ) ) { @@ -621,7 +621,7 @@ bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &docume QUrl urlSource = QUrl::fromEncoded( src.toLatin1() ); QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().writePath( urlSource.toLocalFile() ) ); urlDest.setQueryItems( urlSource.queryItems() ); - src = QString::fromAscii( urlDest.toEncoded() ); + src = QString::fromLatin1( urlDest.toEncoded() ); } else if ( vlayer && vlayer->providerType() == QLatin1String( "memory" ) ) { diff --git a/src/core/qgsmaprenderercustompainterjob.cpp b/src/core/qgsmaprenderercustompainterjob.cpp index 3e930cab863..6ddbcdbd631 100644 --- a/src/core/qgsmaprenderercustompainterjob.cpp +++ b/src/core/qgsmaprenderercustompainterjob.cpp @@ -223,7 +223,7 @@ void QgsMapRendererCustomPainterJob::staticRender( QgsMapRendererCustomPainterJo catch ( std::exception &e ) { Q_UNUSED( e ); - QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) ); + QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) ); } catch ( ... ) { diff --git a/src/core/qgsmaprendererparalleljob.cpp b/src/core/qgsmaprendererparalleljob.cpp index d43a9a2ba8e..a4fb51bc834 100644 --- a/src/core/qgsmaprendererparalleljob.cpp +++ b/src/core/qgsmaprendererparalleljob.cpp @@ -263,7 +263,7 @@ void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job ) catch ( std::exception &e ) { Q_UNUSED( e ); - QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) ); + QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) ); } catch ( ... ) { @@ -307,7 +307,7 @@ void QgsMapRendererParallelJob::renderLabelsStatic( QgsMapRendererParallelJob *s catch ( std::exception &e ) { Q_UNUSED( e ); - QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) ); + QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) ); } catch ( ... ) { diff --git a/src/core/qgssqlstatementlexer.ll b/src/core/qgssqlstatementlexer.ll index 06746541dfb..93336b819fb 100644 --- a/src/core/qgssqlstatementlexer.ll +++ b/src/core/qgssqlstatementlexer.ll @@ -164,18 +164,18 @@ string "'"{str_char}*"'" "," { return COMMA; } -{num_float} { yylval->numberFloat = cLocale.toDouble( QString::fromAscii(yytext) ); return NUMBER_FLOAT; } +{num_float} { yylval->numberFloat = cLocale.toDouble( QString::fromLatin1(yytext) ); return NUMBER_FLOAT; } {num_int} { bool ok; - yylval->numberInt = cLocale.toInt( QString::fromAscii(yytext), &ok ); + yylval->numberInt = cLocale.toInt( QString::fromLatin1(yytext), &ok ); if( ok ) return NUMBER_INT; - yylval->numberInt64 = cLocale.toLongLong( QString::fromAscii(yytext), &ok ); + yylval->numberInt64 = cLocale.toLongLong( QString::fromLatin1(yytext), &ok ); if( ok ) return NUMBER_INT64; - yylval->numberFloat = cLocale.toDouble( QString::fromAscii(yytext), &ok ); + yylval->numberFloat = cLocale.toDouble( QString::fromLatin1(yytext), &ok ); if( ok ) return NUMBER_FLOAT; diff --git a/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.cpp b/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.cpp index ed7fca92b14..9c9f5c36531 100644 --- a/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.cpp +++ b/src/plugins/evis/databaseconnection/evisdatabaseconnectiongui.cpp @@ -115,7 +115,7 @@ void eVisDatabaseConnectionGui::drawNewVectorLayer( const QString &layerName, co url.addQueryItem( QStringLiteral( "delimiterType" ), QStringLiteral( "regexp" ) ); url.addQueryItem( QStringLiteral( "xField" ), xCoordinate ); url.addQueryItem( QStringLiteral( "yField" ), yCoordinate ); - emit drawVectorLayer( QString::fromAscii( url.toEncoded() ), layerName, QStringLiteral( "delimitedtext" ) ); + emit drawVectorLayer( QString::fromLatin1( url.toEncoded() ), layerName, QStringLiteral( "delimitedtext" ) ); mTempOutputFileList->last()->close(); } } diff --git a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp index 35a4285bda2..bd39fe30d55 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp @@ -1090,7 +1090,7 @@ void QgsDelimitedTextProvider::setUriParameter( const QString ¶meter, const QUrl url = QUrl::fromEncoded( dataSourceUri().toLatin1() ); if ( url.hasQueryItem( parameter ) ) url.removeAllQueryItems( parameter ); if ( ! value.isEmpty() ) url.addQueryItem( parameter, value ); - setDataSourceUri( QString::fromAscii( url.toEncoded() ) ); + setDataSourceUri( QString::fromLatin1( url.toEncoded() ) ); } void QgsDelimitedTextProvider::onFileUpdated() diff --git a/src/providers/delimitedtext/qgsdelimitedtextsourceselect.cpp b/src/providers/delimitedtext/qgsdelimitedtextsourceselect.cpp index c20455d0b6c..f3e0a7e5ad1 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextsourceselect.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextsourceselect.cpp @@ -193,7 +193,7 @@ void QgsDelimitedTextSourceSelect::addButtonClicked() // add the layer to the map - emit addVectorLayer( QString::fromAscii( url.toEncoded() ), txtLayerName->text() ); + emit addVectorLayer( QString::fromLatin1( url.toEncoded() ), txtLayerName->text() ); if ( widgetMode() == QgsProviderRegistry::WidgetMode::None ) { accept(); From 31fcc67a69156e017996e68ad21a9c557aea154c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 13:55:25 +1000 Subject: [PATCH 318/364] Replace use of deprecated QHeaderView methods --- src/app/composer/qgsattributeselectiondialog.cpp | 4 ++-- src/app/composer/qgscomposer.cpp | 6 +++--- src/app/locator/qgslocatoroptionswidget.cpp | 2 +- src/app/qgsidentifyresultsdialog.cpp | 2 +- src/app/qgsprojectproperties.cpp | 6 +++--- src/app/qgsrasterlayerproperties.cpp | 4 ++-- src/gui/auth/qgsautheditorwidgets.cpp | 2 +- src/gui/qgsbrowserdockwidget.cpp | 2 +- src/gui/qgsorderbydialog.cpp | 6 +++--- src/gui/qgsprojectionselectiontreewidget.cpp | 8 ++++---- src/gui/qgstaskmanagerwidget.cpp | 2 +- src/gui/qgsvariableeditorwidget.cpp | 4 ++-- .../ui/qgsgeometrycheckerfixsummarydialog.cpp | 8 ++++---- src/plugins/grass/qgsgrassmoduleinput.cpp | 4 ++-- src/plugins/grass/qgsgrassnewmapset.cpp | 2 +- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/app/composer/qgsattributeselectiondialog.cpp b/src/app/composer/qgsattributeselectiondialog.cpp index 911bb683cc4..6d0abc16f9d 100644 --- a/src/app/composer/qgsattributeselectiondialog.cpp +++ b/src/app/composer/qgsattributeselectiondialog.cpp @@ -290,7 +290,7 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTa //set up models, views and delegates mColumnModel = new QgsComposerAttributeTableColumnModelV2( mComposerTable, mColumnsTableView ); mColumnsTableView->setModel( mColumnModel ); - mColumnsTableView->horizontalHeader()->setResizeMode( QHeaderView::Stretch ); + mColumnsTableView->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); mColumnSourceDelegate = new QgsComposerColumnSourceDelegate( vLayer, mColumnsTableView, mComposerTable ); mColumnsTableView->setItemDelegateForColumn( 0, mColumnSourceDelegate ); @@ -312,7 +312,7 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTa mSortedProxyModel->sort( 0, Qt::AscendingOrder ); mSortColumnTableView->setSortingEnabled( false ); mSortColumnTableView->setModel( mSortedProxyModel ); - mSortColumnTableView->horizontalHeader()->setResizeMode( QHeaderView::Stretch ); + mSortColumnTableView->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); } mOrderComboBox->insertItem( 0, tr( "Ascending" ) ); diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp index 394875ad291..11e09ac396a 100644 --- a/src/app/composer/qgscomposer.cpp +++ b/src/app/composer/qgscomposer.cpp @@ -591,9 +591,9 @@ QgsComposer::QgsComposer( QgsComposition *composition ) mItemsTreeView->setColumnWidth( 0, 30 ); mItemsTreeView->setColumnWidth( 1, 30 ); - mItemsTreeView->header()->setResizeMode( 0, QHeaderView::Fixed ); - mItemsTreeView->header()->setResizeMode( 1, QHeaderView::Fixed ); - mItemsTreeView->header()->setMovable( false ); + mItemsTreeView->header()->setSectionResizeMode( 0, QHeaderView::Fixed ); + mItemsTreeView->header()->setSectionResizeMode( 1, QHeaderView::Fixed ); + mItemsTreeView->header()->setSectionsMovable( false ); mItemsTreeView->setDragEnabled( true ); mItemsTreeView->setAcceptDrops( true ); diff --git a/src/app/locator/qgslocatoroptionswidget.cpp b/src/app/locator/qgslocatoroptionswidget.cpp index c0c0add99df..8972c237ba6 100644 --- a/src/app/locator/qgslocatoroptionswidget.cpp +++ b/src/app/locator/qgslocatoroptionswidget.cpp @@ -30,7 +30,7 @@ QgsLocatorOptionsWidget::QgsLocatorOptionsWidget( QgsLocatorWidget *locator, QWi mFiltersTreeView->setModel( mModel ); mFiltersTreeView->header()->setStretchLastSection( false ); - mFiltersTreeView->header()->setResizeMode( 0, QHeaderView::Stretch ); + mFiltersTreeView->header()->setSectionResizeMode( 0, QHeaderView::Stretch ); mConfigureFilterButton->setEnabled( false ); connect( mFiltersTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [ = ]( const QItemSelection & selected, const QItemSelection & ) diff --git a/src/app/qgsidentifyresultsdialog.cpp b/src/app/qgsidentifyresultsdialog.cpp index d7be19d0cc2..262e39a9c0a 100644 --- a/src/app/qgsidentifyresultsdialog.cpp +++ b/src/app/qgsidentifyresultsdialog.cpp @@ -447,7 +447,7 @@ void QgsIdentifyResultsDialog::addFeature( const QgsMapToolIdentify::IdentifyRes void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeature &f, const QMap &derivedAttributes ) { QTreeWidgetItem *layItem = layerItem( vlayer ); - lstResults->header()->setResizeMode( QHeaderView::ResizeToContents ); + lstResults->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); lstResults->header()->setStretchLastSection( false ); if ( !layItem ) diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index d443f9f91b3..1009c801401 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -259,7 +259,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa twIdentifyLayers->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Identifiable" ) ) ); twIdentifyLayers->setHorizontalHeaderItem( 3, new QTableWidgetItem( tr( "Read Only" ) ) ); twIdentifyLayers->setRowCount( mapLayers.size() ); - twIdentifyLayers->verticalHeader()->setResizeMode( QHeaderView::ResizeToContents ); + twIdentifyLayers->verticalHeader()->setSectionResizeMode( QHeaderView::ResizeToContents ); int i = 0; for ( QMap::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); ++it, i++ ) @@ -620,7 +620,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa } } twWFSLayers->setRowCount( j ); - twWFSLayers->verticalHeader()->setResizeMode( QHeaderView::ResizeToContents ); + twWFSLayers->verticalHeader()->setSectionResizeMode( QHeaderView::ResizeToContents ); mWCSUrlLineEdit->setText( QgsProject::instance()->readEntry( QStringLiteral( "WCSUrl" ), QStringLiteral( "/" ), QLatin1String( "" ) ) ); QStringList wcsLayerIdList = QgsProject::instance()->readListEntry( QStringLiteral( "WCSLayers" ), QStringLiteral( "/" ) ); @@ -659,7 +659,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa } } twWCSLayers->setRowCount( j ); - twWCSLayers->verticalHeader()->setResizeMode( QHeaderView::ResizeToContents ); + twWCSLayers->verticalHeader()->setSectionResizeMode( QHeaderView::ResizeToContents ); // Default Styles mStyle = QgsStyle::defaultStyle(); diff --git a/src/app/qgsrasterlayerproperties.cpp b/src/app/qgsrasterlayerproperties.cpp index 9599b2ee90b..835093b31e4 100644 --- a/src/app/qgsrasterlayerproperties.cpp +++ b/src/app/qgsrasterlayerproperties.cpp @@ -461,8 +461,8 @@ void QgsRasterLayerProperties::setupTransparencyTable( int nBands ) tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Percent Transparent" ) ) ); } - tableTransparency->horizontalHeader()->setResizeMode( 0, QHeaderView::Stretch ); - tableTransparency->horizontalHeader()->setResizeMode( 1, QHeaderView::Stretch ); + tableTransparency->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch ); + tableTransparency->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::Stretch ); } void QgsRasterLayerProperties::populateTransparencyTable( QgsRasterRenderer *renderer ) diff --git a/src/gui/auth/qgsautheditorwidgets.cpp b/src/gui/auth/qgsautheditorwidgets.cpp index b809c2a21a7..e7ea6f48b64 100644 --- a/src/gui/auth/qgsautheditorwidgets.cpp +++ b/src/gui/auth/qgsautheditorwidgets.cpp @@ -63,7 +63,7 @@ void QgsAuthMethodPlugins::setupTable() tblAuthPlugins->setColumnWidth( 0, 150 ); tblAuthPlugins->setColumnWidth( 1, 300 ); tblAuthPlugins->setRowCount( QgsAuthManager::instance()->authMethodsKeys().size() ); - tblAuthPlugins->verticalHeader()->setResizeMode( QHeaderView::ResizeToContents ); + tblAuthPlugins->verticalHeader()->setSectionResizeMode( QHeaderView::ResizeToContents ); tblAuthPlugins->setSortingEnabled( true ); tblAuthPlugins->setSelectionBehavior( QAbstractItemView::SelectRows ); } diff --git a/src/gui/qgsbrowserdockwidget.cpp b/src/gui/qgsbrowserdockwidget.cpp index 6dca2149f14..82b3f93d48b 100644 --- a/src/gui/qgsbrowserdockwidget.cpp +++ b/src/gui/qgsbrowserdockwidget.cpp @@ -122,7 +122,7 @@ void QgsBrowserDockWidget::showEvent( QShowEvent *e ) mBrowserView->setModel( mProxyModel ); // provide a horizontal scroll bar instead of using ellipse (...) for longer items mBrowserView->setTextElideMode( Qt::ElideNone ); - mBrowserView->header()->setResizeMode( 0, QHeaderView::ResizeToContents ); + mBrowserView->header()->setSectionResizeMode( 0, QHeaderView::ResizeToContents ); mBrowserView->header()->setStretchLastSection( false ); // selectionModel is created when model is set on tree diff --git a/src/gui/qgsorderbydialog.cpp b/src/gui/qgsorderbydialog.cpp index efddb60c5c6..86664790423 100644 --- a/src/gui/qgsorderbydialog.cpp +++ b/src/gui/qgsorderbydialog.cpp @@ -29,9 +29,9 @@ QgsOrderByDialog::QgsOrderByDialog( QgsVectorLayer *layer, QWidget *parent ) { setupUi( this ); - mOrderByTableWidget->horizontalHeader()->setResizeMode( QHeaderView::Stretch ); - mOrderByTableWidget->horizontalHeader()->setResizeMode( 1, QHeaderView::ResizeToContents ); - mOrderByTableWidget->horizontalHeader()->setResizeMode( 2, QHeaderView::ResizeToContents ); + mOrderByTableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); + mOrderByTableWidget->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::ResizeToContents ); + mOrderByTableWidget->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::ResizeToContents ); mOrderByTableWidget->installEventFilter( this ); diff --git a/src/gui/qgsprojectionselectiontreewidget.cpp b/src/gui/qgsprojectionselectiontreewidget.cpp index 4a09461beb8..5ec2d65037e 100644 --- a/src/gui/qgsprojectionselectiontreewidget.cpp +++ b/src/gui/qgsprojectionselectiontreewidget.cpp @@ -49,16 +49,16 @@ QgsProjectionSelectionTreeWidget::QgsProjectionSelectionTreeWidget( QWidget *par // Get the full path name to the sqlite3 spatial reference database. mSrsDatabaseFileName = QgsApplication::srsDatabaseFilePath(); - lstCoordinateSystems->header()->setResizeMode( AuthidColumn, QHeaderView::Stretch ); + lstCoordinateSystems->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch ); lstCoordinateSystems->header()->resizeSection( QgisCrsIdColumn, 0 ); - lstCoordinateSystems->header()->setResizeMode( QgisCrsIdColumn, QHeaderView::Fixed ); + lstCoordinateSystems->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed ); // Hide (internal) ID column lstCoordinateSystems->setColumnHidden( QgisCrsIdColumn, true ); - lstRecent->header()->setResizeMode( AuthidColumn, QHeaderView::Stretch ); + lstRecent->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch ); lstRecent->header()->resizeSection( QgisCrsIdColumn, 0 ); - lstRecent->header()->setResizeMode( QgisCrsIdColumn, QHeaderView::Fixed ); + lstRecent->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed ); // Hide (internal) ID column lstRecent->setColumnHidden( QgisCrsIdColumn, true ); diff --git a/src/gui/qgstaskmanagerwidget.cpp b/src/gui/qgstaskmanagerwidget.cpp index 173054a08a7..a0e5c311122 100644 --- a/src/gui/qgstaskmanagerwidget.cpp +++ b/src/gui/qgstaskmanagerwidget.cpp @@ -49,7 +49,7 @@ QgsTaskManagerWidget::QgsTaskManagerWidget( QgsTaskManager *manager, QWidget *pa mTreeView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); mTreeView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); mTreeView->header()->setStretchLastSection( false ); - mTreeView->header()->setResizeMode( 0, QHeaderView::Stretch ); + mTreeView->header()->setSectionResizeMode( 0, QHeaderView::Stretch ); vLayout->addWidget( mTreeView ); diff --git a/src/gui/qgsvariableeditorwidget.cpp b/src/gui/qgsvariableeditorwidget.cpp index e382a13c0ca..32072c6822a 100644 --- a/src/gui/qgsvariableeditorwidget.cpp +++ b/src/gui/qgsvariableeditorwidget.cpp @@ -273,8 +273,8 @@ QgsVariableEditorTree::QgsVariableEditorTree( QWidget *parent ) setAlternatingRowColors( true ); setEditTriggers( QAbstractItemView::AllEditTriggers ); setRootIsDecorated( false ); - header()->setMovable( false ); - header()->setResizeMode( QHeaderView::Interactive ); + header()->setSectionsMovable( false ); + header()->setSectionResizeMode( QHeaderView::Interactive ); mEditorDelegate = new VariableEditorDelegate( this, this ); setItemDelegate( mEditorDelegate ); diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp index b73186c4155..3ca612b9d34 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp @@ -97,10 +97,10 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome void QgsGeometryCheckerFixSummaryDialog::setupTable( QTableWidget *table ) { table->resizeColumnToContents( 0 ); - table->horizontalHeader()->setResizeMode( 1, QHeaderView::Stretch ); - table->horizontalHeader()->setResizeMode( 2, QHeaderView::Stretch ); - table->horizontalHeader()->setResizeMode( 3, QHeaderView::Stretch ); - table->horizontalHeader()->setResizeMode( 4, QHeaderView::Stretch ); + table->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::Stretch ); + table->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::Stretch ); + table->horizontalHeader()->setSectionResizeMode( 3, QHeaderView::Stretch ); + table->horizontalHeader()->setSectionResizeMode( 4, QHeaderView::Stretch ); table->setEditTriggers( QAbstractItemView::NoEditTriggers ); table->setSelectionBehavior( QAbstractItemView::SelectRows ); diff --git a/src/plugins/grass/qgsgrassmoduleinput.cpp b/src/plugins/grass/qgsgrassmoduleinput.cpp index f73ca596f67..ebd307f7031 100644 --- a/src/plugins/grass/qgsgrassmoduleinput.cpp +++ b/src/plugins/grass/qgsgrassmoduleinput.cpp @@ -754,8 +754,8 @@ void QgsGrassModuleInputSelectedView::setModel( QAbstractItemModel *model ) QTreeView::setModel( model ); header()->hide(); header()->setStretchLastSection( false ); - header()->setResizeMode( 0, QHeaderView::Stretch ); - header()->setResizeMode( 1, QHeaderView::Fixed ); + header()->setSectionResizeMode( 0, QHeaderView::Stretch ); + header()->setSectionResizeMode( 1, QHeaderView::Fixed ); header()->resizeSection( 1, 16 ); } diff --git a/src/plugins/grass/qgsgrassnewmapset.cpp b/src/plugins/grass/qgsgrassnewmapset.cpp index dd096f1f601..9805d6c4b6a 100644 --- a/src/plugins/grass/qgsgrassnewmapset.cpp +++ b/src/plugins/grass/qgsgrassnewmapset.cpp @@ -117,7 +117,7 @@ QgsGrassNewMapset::QgsGrassNewMapset( QgisInterface *iface, mMapsetsListView->clear(); mMapsetLineEdit->setValidator( new QRegExpValidator( rx, mMapsetLineEdit ) ); - mMapsetsListView->header()->setResizeMode( QHeaderView::ResizeToContents ); + mMapsetsListView->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); // FINISH mOpenNewMapsetCheckBox->setChecked( settings.value( QStringLiteral( "GRASS/newMapsetWizard/openMapset" ), true ).toBool() ); From 422963e995970cfa6f43c7dd75cee25860e79134 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 13:57:21 +1000 Subject: [PATCH 319/364] Replace use of deprecated QMouseEvent::posF() --- src/app/qgsmaptoolannotation.cpp | 36 +++++++++++++++---------------- src/app/qgsmaptoolannotation.h | 2 +- src/gui/layout/qgslayoutruler.cpp | 4 ++-- src/gui/layout/qgslayoutruler.h | 2 +- src/gui/qgscolorwidgets.cpp | 8 +++---- src/gui/qgscomposerruler.cpp | 6 +++--- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/app/qgsmaptoolannotation.cpp b/src/app/qgsmaptoolannotation.cpp index feab0a943fe..e358c28efbc 100644 --- a/src/app/qgsmaptoolannotation.cpp +++ b/src/app/qgsmaptoolannotation.cpp @@ -94,12 +94,12 @@ void QgsMapToolAnnotation::canvasPressEvent( QgsMapMouseEvent *e ) return; } - mLastMousePosition = e->posF(); + mLastMousePosition = e->pos(); QgsMapCanvasAnnotationItem *item = selectedItem(); if ( item ) { - mCurrentMoveAction = item->moveActionForPosition( e->posF() ); + mCurrentMoveAction = item->moveActionForPosition( e->pos() ); if ( mCurrentMoveAction != QgsMapCanvasAnnotationItem::NoAction ) { return; @@ -110,7 +110,7 @@ void QgsMapToolAnnotation::canvasPressEvent( QgsMapMouseEvent *e ) { //select a new item if there is one at this position mCanvas->scene()->clearSelection(); - QgsMapCanvasAnnotationItem *existingItem = itemAtPos( e->posF() ); + QgsMapCanvasAnnotationItem *existingItem = itemAtPos( e->pos() ); if ( existingItem ) { existingItem->setSelected( true ); @@ -124,8 +124,8 @@ void QgsMapToolAnnotation::canvasPressEvent( QgsMapMouseEvent *e ) QgsPointXY mapPos = transformCanvasToAnnotation( toMapCoordinates( e->pos() ), annotation ); annotation->setMapPosition( mapPos ); annotation->setMapPositionCrs( mCanvas->mapSettings().destinationCrs() ); - annotation->setRelativePosition( QPointF( e->posF().x() / mCanvas->width(), - e->posF().y() / mCanvas->height() ) ); + annotation->setRelativePosition( QPointF( e->pos().x() / mCanvas->width(), + e->pos().y() / mCanvas->height() ) ); annotation->setFrameSize( QSizeF( 200, 100 ) ); QgsProject::instance()->annotationManager()->addAnnotation( annotation ); @@ -184,17 +184,17 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e ) { QgsPointXY mapPos = transformCanvasToAnnotation( e->snapPoint(), annotation ); annotation->setMapPosition( mapPos ); - annotation->setRelativePosition( QPointF( e->posF().x() / mCanvas->width(), - e->posF().y() / mCanvas->height() ) ); + annotation->setRelativePosition( QPointF( e->pos().x() / mCanvas->width(), + e->pos().y() / mCanvas->height() ) ); item->update(); QgsProject::instance()->setDirty( true ); } else if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::MoveFramePosition ) { - QPointF newCanvasPos = item->pos() + ( e->posF() - mLastMousePosition ); + QPointF newCanvasPos = item->pos() + ( e->pos() - mLastMousePosition ); if ( annotation->hasFixedMapPosition() ) { - annotation->setFrameOffsetFromReferencePoint( annotation->frameOffsetFromReferencePoint() + ( e->posF() - mLastMousePosition ) ); + annotation->setFrameOffsetFromReferencePoint( annotation->frameOffsetFromReferencePoint() + ( e->pos() - mLastMousePosition ) ); annotation->setRelativePosition( QPointF( newCanvasPos.x() / mCanvas->width(), newCanvasPos.y() / mCanvas->height() ) ); } @@ -223,27 +223,27 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e ) mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown || mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp ) { - xmax += e->posF().x() - mLastMousePosition.x(); + xmax += e->pos().x() - mLastMousePosition.x(); } if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeft || mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown || mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp ) { - xmin += e->posF().x() - mLastMousePosition.x(); - relPosX = ( relPosX * mCanvas->width() + e->posF().x() - mLastMousePosition.x() ) / ( double )mCanvas->width(); + xmin += e->pos().x() - mLastMousePosition.x(); + relPosX = ( relPosX * mCanvas->width() + e->pos().x() - mLastMousePosition.x() ) / ( double )mCanvas->width(); } if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameUp || mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp || mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp ) { - ymin += e->posF().y() - mLastMousePosition.y(); - relPosY = ( relPosY * mCanvas->height() + e->posF().y() - mLastMousePosition.y() ) / ( double )mCanvas->height(); + ymin += e->pos().y() - mLastMousePosition.y(); + relPosY = ( relPosY * mCanvas->height() + e->pos().y() - mLastMousePosition.y() ) / ( double )mCanvas->height(); } if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameDown || mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown || mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown ) { - ymax += e->posF().y() - mLastMousePosition.y(); + ymax += e->pos().y() - mLastMousePosition.y(); } //switch min / max if necessary @@ -270,18 +270,18 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e ) } else if ( item ) { - QgsMapCanvasAnnotationItem::MouseMoveAction moveAction = item->moveActionForPosition( e->posF() ); + QgsMapCanvasAnnotationItem::MouseMoveAction moveAction = item->moveActionForPosition( e->pos() ); if ( mCanvas ) { mCanvas->setCursor( QCursor( item->cursorShapeForAction( moveAction ) ) ); } } - mLastMousePosition = e->posF(); + mLastMousePosition = e->pos(); } void QgsMapToolAnnotation::canvasDoubleClickEvent( QgsMapMouseEvent *e ) { - QgsMapCanvasAnnotationItem *item = itemAtPos( e->posF() ); + QgsMapCanvasAnnotationItem *item = itemAtPos( e->pos() ); if ( !item ) { return; diff --git a/src/app/qgsmaptoolannotation.h b/src/app/qgsmaptoolannotation.h index 605a585ac0a..79e6fd0ff0d 100644 --- a/src/app/qgsmaptoolannotation.h +++ b/src/app/qgsmaptoolannotation.h @@ -60,7 +60,7 @@ class APP_EXPORT QgsMapToolAnnotation: public QgsMapTool QgsPointXY transformCanvasToAnnotation( QgsPointXY p, QgsAnnotation *annotation ) const; QgsMapCanvasAnnotationItem::MouseMoveAction mCurrentMoveAction = QgsMapCanvasAnnotationItem::NoAction; - QPointF mLastMousePosition = QPointF( 0, 0 ); + QPoint mLastMousePosition = QPoint( 0, 0 ); }; #endif // QGSMAPTOOLANNOTATION_H diff --git a/src/gui/layout/qgslayoutruler.cpp b/src/gui/layout/qgslayoutruler.cpp index a8ed5602747..c6a715a00f0 100644 --- a/src/gui/layout/qgslayoutruler.cpp +++ b/src/gui/layout/qgslayoutruler.cpp @@ -571,7 +571,7 @@ void QgsLayoutRuler::setCursorPosition( QPointF position ) void QgsLayoutRuler::mouseMoveEvent( QMouseEvent *event ) { - mMarkerPos = event->posF(); + mMarkerPos = event->pos(); update(); QPointF displayPos; @@ -651,7 +651,7 @@ void QgsLayoutRuler::mouseMoveEvent( QMouseEvent *event ) } //update cursor position in status bar - displayPos = mTransform.inverted().map( event->posF() ); + displayPos = mTransform.inverted().map( event->pos() ); switch ( mOrientation ) { case Qt::Horizontal: diff --git a/src/gui/layout/qgslayoutruler.h b/src/gui/layout/qgslayoutruler.h index 5f5945ea765..7cc4f238c93 100644 --- a/src/gui/layout/qgslayoutruler.h +++ b/src/gui/layout/qgslayoutruler.h @@ -104,7 +104,7 @@ class GUI_EXPORT QgsLayoutRuler: public QWidget QgsLayoutView *mView = nullptr; QTransform mTransform; - QPointF mMarkerPos; + QPoint mMarkerPos; QFont mRulerFont; std::unique_ptr< QFontMetrics > mRulerFontMetrics; diff --git a/src/gui/qgscolorwidgets.cpp b/src/gui/qgscolorwidgets.cpp index 29e8caa363e..b030c3cf3d5 100644 --- a/src/gui/qgscolorwidgets.cpp +++ b/src/gui/qgscolorwidgets.cpp @@ -601,7 +601,7 @@ void QgsColorWheel::setColorFromPos( const QPointF pos ) void QgsColorWheel::mouseMoveEvent( QMouseEvent *event ) { - setColorFromPos( event->posF() ); + setColorFromPos( event->pos() ); QgsColorWidget::mouseMoveEvent( event ); } @@ -621,7 +621,7 @@ void QgsColorWheel::mousePressEvent( QMouseEvent *event ) { mClickedPart = QgsColorWheel::Wheel; } - setColorFromPos( event->posF() ); + setColorFromPos( event->pos() ); } void QgsColorWheel::mouseReleaseEvent( QMouseEvent *event ) @@ -1162,7 +1162,7 @@ void QgsColorRampWidget::setMarkerSize( const int markerSize ) void QgsColorRampWidget::mouseMoveEvent( QMouseEvent *event ) { - setColorFromPoint( event->posF() ); + setColorFromPoint( event->pos() ); QgsColorWidget::mouseMoveEvent( event ); } @@ -1191,7 +1191,7 @@ void QgsColorRampWidget::wheelEvent( QWheelEvent *event ) void QgsColorRampWidget::mousePressEvent( QMouseEvent *event ) { - setColorFromPoint( event->posF() ); + setColorFromPoint( event->pos() ); } void QgsColorRampWidget::keyPressEvent( QKeyEvent *event ) diff --git a/src/gui/qgscomposerruler.cpp b/src/gui/qgscomposerruler.cpp index 745f2bfa3d3..b3f39af1a0a 100644 --- a/src/gui/qgscomposerruler.cpp +++ b/src/gui/qgscomposerruler.cpp @@ -386,11 +386,11 @@ void QgsComposerRuler::setSceneTransform( const QTransform &transform ) void QgsComposerRuler::mouseMoveEvent( QMouseEvent *event ) { //qWarning( "QgsComposerRuler::mouseMoveEvent" ); - updateMarker( event->posF() ); - setSnapLinePosition( event->posF() ); + updateMarker( event->pos() ); + setSnapLinePosition( event->pos() ); //update cursor position in status bar - QPointF displayPos = mTransform.inverted().map( event->posF() ); + QPointF displayPos = mTransform.inverted().map( event->pos() ); if ( mDirection == Horizontal ) { //mouse is over a horizontal ruler, so don't show a y coordinate From 84774ee2ded210c1dab7b6ea46eacd0d5cb1cbbf Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 13:59:40 +1000 Subject: [PATCH 320/364] Replace use of qSort with std::sort --- src/app/nodetool/qgsnodetool.cpp | 2 +- src/core/qgsexpressionsorter.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/nodetool/qgsnodetool.cpp b/src/app/nodetool/qgsnodetool.cpp index 9de9941975e..6a7b321a9c9 100644 --- a/src/app/nodetool/qgsnodetool.cpp +++ b/src/app/nodetool/qgsnodetool.cpp @@ -1601,7 +1601,7 @@ void QgsNodeTool::deleteVertex() QList &vertexIds = it2.value(); bool res = QgsVectorLayer::Success; - qSort( vertexIds.begin(), vertexIds.end(), qGreater() ); + std::sort( vertexIds.begin(), vertexIds.end(), std::greater() ); Q_FOREACH ( int vertexId, vertexIds ) { if ( res != QgsVectorLayer::EmptyGeometry ) diff --git a/src/core/qgsexpressionsorter.h b/src/core/qgsexpressionsorter.h index 2717d762d54..ad91196091f 100644 --- a/src/core/qgsexpressionsorter.h +++ b/src/core/qgsexpressionsorter.h @@ -160,7 +160,7 @@ class QgsExpressionSorter delete expressionContext->popScope(); - qSort( indexedFeatures.begin(), indexedFeatures.end(), *this ); + std::sort( indexedFeatures.begin(), indexedFeatures.end(), *this ); features.clear(); From 77c4edfbff422aaefd3deb95d329b7c6e7d889dd Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 14:00:49 +1000 Subject: [PATCH 321/364] Replace use of deprecated Qt::escape --- src/core/qgsstringutils.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/qgsstringutils.cpp b/src/core/qgsstringutils.cpp index d35d5ee63ac..8accb752582 100644 --- a/src/core/qgsstringutils.cpp +++ b/src/core/qgsstringutils.cpp @@ -16,7 +16,6 @@ #include "qgsstringutils.h" #include #include -#include // for Qt::escape #include #include @@ -362,7 +361,7 @@ QString QgsStringUtils::insertLinks( const QString &string, bool *foundLinks ) { protoUrl.prepend( "http://" ); } - QString anchor = QStringLiteral( "%2" ).arg( Qt::escape( protoUrl ), Qt::escape( url ) ); + QString anchor = QStringLiteral( "%2" ).arg( protoUrl.toHtmlEscaped(), url.toHtmlEscaped() ); converted.replace( urlRegEx.pos( 1 ), url.length(), anchor ); offset = urlRegEx.pos( 1 ) + anchor.length(); } @@ -371,7 +370,7 @@ QString QgsStringUtils::insertLinks( const QString &string, bool *foundLinks ) { found = true; QString email = emailRegEx.cap( 1 ); - QString anchor = QStringLiteral( "%1" ).arg( Qt::escape( email ), Qt::escape( email ) ); + QString anchor = QStringLiteral( "%1" ).arg( email.toHtmlEscaped(), email.toHtmlEscaped() ); converted.replace( emailRegEx.pos( 1 ), email.length(), anchor ); offset = emailRegEx.pos( 1 ) + anchor.length(); } From b196d37c1e07156a917164e9dceef72c2abd772d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 14:01:16 +1000 Subject: [PATCH 322/364] Replace use of deprecated QShortcut string operator --- src/gui/qgsshortcutsmanager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/qgsshortcutsmanager.cpp b/src/gui/qgsshortcutsmanager.cpp index 0ecde77718d..18ad148e335 100644 --- a/src/gui/qgsshortcutsmanager.cpp +++ b/src/gui/qgsshortcutsmanager.cpp @@ -38,7 +38,7 @@ void QgsShortcutsManager::registerAllChildActions( QObject *object, bool recursi QList< QAction * > actions = object->findChildren< QAction * >(); Q_FOREACH ( QAction *a, actions ) { - registerAction( a, a->shortcut() ); + registerAction( a, a->shortcut().toString() ); } } else @@ -47,7 +47,7 @@ void QgsShortcutsManager::registerAllChildActions( QObject *object, bool recursi { if ( QAction *a = qobject_cast( child ) ) { - registerAction( a, a->shortcut() ); + registerAction( a, a->shortcut().toString() ); } } } @@ -60,7 +60,7 @@ void QgsShortcutsManager::registerAllChildShortcuts( QObject *object, bool recur QList< QShortcut * > shortcuts = object->findChildren< QShortcut * >(); Q_FOREACH ( QShortcut *s, shortcuts ) { - registerShortcut( s, s->key() ); + registerShortcut( s, s->key().toString() ); } } else @@ -69,7 +69,7 @@ void QgsShortcutsManager::registerAllChildShortcuts( QObject *object, bool recur { if ( QShortcut *s = qobject_cast( child ) ) { - registerShortcut( s, s->key() ); + registerShortcut( s, s->key().toString() ); } } } From 83e685853948a887daef76f5821d65ffd8badb1e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 14:02:15 +1000 Subject: [PATCH 323/364] Replace use of deprecated QSqlError::number --- src/providers/db2/qgsdb2dataitems.cpp | 6 +++--- src/providers/db2/qgsdb2geometrycolumns.cpp | 18 +++++++++--------- src/providers/db2/qgsdb2geometrycolumns.h | 4 ++-- src/providers/db2/qgsdb2provider.cpp | 10 +++++----- src/providers/db2/qgsdb2sourceselect.cpp | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/providers/db2/qgsdb2dataitems.cpp b/src/providers/db2/qgsdb2dataitems.cpp index 12c4e263272..a163b19f268 100644 --- a/src/providers/db2/qgsdb2dataitems.cpp +++ b/src/providers/db2/qgsdb2dataitems.cpp @@ -185,16 +185,16 @@ QVector QgsDb2ConnectionItem::createChildren() } QgsDb2GeometryColumns db2GC = QgsDb2GeometryColumns( db ); - int sqlcode = db2GC.open(); + QString sqlcode = db2GC.open(); /* Enabling the DB2 Spatial Extender creates the DB2GSE schema and tables, so the Extender is either not enabled or set up if SQLCODE -204 is returned. */ - if ( sqlcode == -204 ) + if ( sqlcode == QStringLiteral( "-204" ) ) { children.append( new QgsErrorItem( this, tr( "DB2 Spatial Extender is not enabled or set up." ), mPath + "/error" ) ); return children; } - else if ( sqlcode != 0 ) + else if ( !sqlcode.isEmpty() && sqlcode != QStringLiteral( "0" ) ) { children.append( new QgsErrorItem( this, db.lastError().text(), mPath + "/error" ) ); return children; diff --git a/src/providers/db2/qgsdb2geometrycolumns.cpp b/src/providers/db2/qgsdb2geometrycolumns.cpp index 894335aa8ed..d0accb9e33e 100644 --- a/src/providers/db2/qgsdb2geometrycolumns.cpp +++ b/src/providers/db2/qgsdb2geometrycolumns.cpp @@ -32,12 +32,12 @@ QgsDb2GeometryColumns::~QgsDb2GeometryColumns() mQuery.clear(); } -int QgsDb2GeometryColumns::open() +QString QgsDb2GeometryColumns::open() { return open( QString(), QString() ); } -int QgsDb2GeometryColumns::open( const QString &schemaName, const QString &tableName ) +QString QgsDb2GeometryColumns::open( const QString &schemaName, const QString &tableName ) { QString queryExtents( "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, TYPE_NAME, " "SRS_ID, SRS_NAME, MIN_X, MIN_Y, MAX_X, MAX_Y " @@ -46,7 +46,7 @@ int QgsDb2GeometryColumns::open( const QString &schemaName, const QString &table "SRS_ID, SRS_NAME " "FROM DB2GSE.ST_GEOMETRY_COLUMNS" ); mQuery = QSqlQuery( mDatabase ); - int sqlcode = 0; + QString nativeError; mEnvironment = ENV_LUW; if ( !schemaName.isEmpty() && !tableName.isEmpty() ) @@ -61,30 +61,30 @@ int QgsDb2GeometryColumns::open( const QString &schemaName, const QString &table if ( !mQuery.exec( queryExtents ) ) { QgsDebugMsg( "ST_Geometry_Columns query failed: " + mDatabase.lastError().text() ); - sqlcode = mQuery.lastError().number(); - QgsDebugMsg( QString( "SQLCODE: %1" ).arg( sqlcode ) ); + nativeError = mQuery.lastError().nativeErrorCode(); + QgsDebugMsg( QString( "SQLCODE: %1" ).arg( nativeError ) ); /* The MIN_X, MIN_Y, MAX_X, and MAX_Y columns are not available on z/OS (and LUW 9.5) so SQLCODE -206 is returned when specifying non-existent columns. */ - if ( mQuery.lastError().number() == -206 ) + if ( mQuery.lastError().nativeErrorCode() == QStringLiteral( "-206" ) ) { QgsDebugMsg( "Try query with no extents" ); mQuery.clear(); if ( !mQuery.exec( queryNoExtents ) ) { - QgsDebugMsg( QString( "SQLCODE: %1" ).arg( mQuery.lastError().number() ) ); + QgsDebugMsg( QString( "SQLCODE: %1" ).arg( mQuery.lastError().nativeErrorCode() ) ); } else { QgsDebugMsg( "success; must be z/OS" ); mEnvironment = ENV_ZOS; - sqlcode = 0; + nativeError.clear(); } } } // QgsDebugMsg( QString( "sqlcode: %1" ).arg( sqlcode ) ); - return sqlcode; + return nativeError; } bool QgsDb2GeometryColumns::isActive() diff --git a/src/providers/db2/qgsdb2geometrycolumns.h b/src/providers/db2/qgsdb2geometrycolumns.h index 8da8d31db62..1f3488935b7 100644 --- a/src/providers/db2/qgsdb2geometrycolumns.h +++ b/src/providers/db2/qgsdb2geometrycolumns.h @@ -37,8 +37,8 @@ class QgsDb2GeometryColumns // clazy:exclude=rule-of-three bool isActive(); void close(); - int open(); - int open( const QString &schemaName, const QString &tableName ); + QString open(); + QString open( const QString &schemaName, const QString &tableName ); bool populateLayerProperty( QgsDb2LayerProperty &layer ); int db2Environment(); diff --git a/src/providers/db2/qgsdb2provider.cpp b/src/providers/db2/qgsdb2provider.cpp index ad6ab4b412d..e880db93e0b 100644 --- a/src/providers/db2/qgsdb2provider.cpp +++ b/src/providers/db2/qgsdb2provider.cpp @@ -583,8 +583,8 @@ void QgsDb2Provider::updateStatistics() const QgsDebugMsg( QString( "mSRId: %1" ).arg( mSRId ) ); QgsDb2GeometryColumns gc( mDatabase ); - int rc = gc.open( mSchemaName, mTableName ); // returns SQLCODE if failure - if ( rc == 0 ) + QString rc = gc.open( mSchemaName, mTableName ); // returns SQLCODE if failure + if ( rc.isEmpty() || rc == QStringLiteral( "0" ) ) { mEnvironment = gc.db2Environment(); if ( -1 == mSRId ) @@ -1394,7 +1394,7 @@ QgsVectorLayerExporter::ExportError QgsDb2Provider::createEmptyLayer( const QStr sql = "DROP TABLE " + fullName; if ( !q.exec( sql ) ) { - if ( q.lastError().number() != -206 ) // -206 is "not found" just ignore + if ( q.lastError().nativeErrorCode() != QStringLiteral( "-206" ) ) // -206 is "not found" just ignore { QString lastError = q.lastError().text(); QgsDebugMsg( lastError ); @@ -1498,8 +1498,8 @@ QgsVectorLayerExporter::ExportError QgsDb2Provider::createEmptyLayer( const QStr // get the environment QgsDb2GeometryColumns gc( db ); - int rc = gc.open( schemaName, tableName ); // returns SQLCODE if failure - if ( rc == 0 ) + QString rc = gc.open( schemaName, tableName ); // returns SQLCODE if failure + if ( rc.isEmpty() || rc == QStringLiteral( "0" ) ) { db2Environment = gc.db2Environment(); } diff --git a/src/providers/db2/qgsdb2sourceselect.cpp b/src/providers/db2/qgsdb2sourceselect.cpp index d17d32b7bca..f7c24251d74 100644 --- a/src/providers/db2/qgsdb2sourceselect.cpp +++ b/src/providers/db2/qgsdb2sourceselect.cpp @@ -495,8 +495,8 @@ void QgsDb2SourceSelect::on_btnConnect_clicked() } QgsDb2GeometryColumns db2GC = QgsDb2GeometryColumns( db ); - int sqlcode = db2GC.open(); - if ( 0 != sqlcode ) + QString sqlcode = db2GC.open(); + if ( !sqlcode.isEmpty() && QStringLiteral( "0" ) != sqlcode ) { QMessageBox::warning( this, tr( "DB2GSE.ST_GEOMETRY_COLUMNS Not Found" ), tr( "DB2GSE.ST_GEOMETRY_COLUMNS not found. The DB2 Spatial Extender is not enabled or set up." ) ); From 1b5fbf98fed23fe400a6534f3861abe239495c32 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 14:03:16 +1000 Subject: [PATCH 324/364] Replace use of qCopy --- src/plugins/grass/qtermwidget/History.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/grass/qtermwidget/History.cpp b/src/plugins/grass/qtermwidget/History.cpp index f49b00becad..9830bd7c383 100644 --- a/src/plugins/grass/qtermwidget/History.cpp +++ b/src/plugins/grass/qtermwidget/History.cpp @@ -314,7 +314,7 @@ void HistoryScrollBuffer::addCellsVector(const QVector& cells) void HistoryScrollBuffer::addCells(const Character a[], int count) { HistoryLine newLine(count); - qCopy(a,a+count,newLine.begin()); + std::copy(a,a+count,newLine.begin()); addCellsVector(newLine); } @@ -730,7 +730,7 @@ void CompactHistoryScroll::addCellsVector ( const TextLine& cells ) void CompactHistoryScroll::addCells ( const Character a[], int count ) { TextLine newLine ( count ); - qCopy ( a,a+count,newLine.begin() ); + std::copy ( a,a+count,newLine.begin() ); addCellsVector ( newLine ); } From d08398f785b9421d0a8df66d34576cd3097da540 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 17:22:11 +1000 Subject: [PATCH 325/364] [FEATURE] Drop processing 'Select by Attribute Sum' algorithm Tagged as feature to be included in release notes. Because: - The use case for this algorithm is very unclear for users - the name does not describe what the algorithm does, and there's no help documentation available for the algorithm either. Given this I suspect that the algorithm is not being put into use. - The algorithm needs enhancement to be more useful. There's no logic in place which dictates how neighbouring features are chosen to dissolve into the selected feature (it's effectively random - you're just as likely to get a huge narrow polygon stretching across a map as you are a nice compact cluster). To be more useful the algorithm would need logic to either minimise the area of the dissolved feature, or minimise the total number of dissolved features, or ... ? --- python/plugins/processing/algs/help/qgis.yaml | 3 - .../algs/qgis/QGISAlgorithmProvider.py | 2 - .../algs/qgis/SelectByAttributeSum.py | 107 ------------------ 3 files changed, 112 deletions(-) delete mode 100644 python/plugins/processing/algs/qgis/SelectByAttributeSum.py diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index 9fa8531bd82..a00b5373441 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -525,9 +525,6 @@ qgis:saveselectedfeatures: > qgis:selectbyattribute: > This algorithm creates a selection in a vector layer. The criteria for selected features is defined based on the values of an attribute from the input layer. -qgis:selectbyattributesum: - - qgis:selectbyexpression: > This algorithm creates a selection in a vector layer. The criteria for selecting features is based on a QGIS expression. diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 1a532f004dc..c65e62cd440 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -174,7 +174,6 @@ from .ZonalStatistics import ZonalStatistics # from .ExtractByLocation import ExtractByLocation # from .SelectByLocation import SelectByLocation # from .SpatialJoin import SpatialJoin -# from .SelectByAttributeSum import SelectByAttributeSum pluginPath = os.path.normpath(os.path.join( os.path.split(os.path.dirname(__file__))[0], os.pardir)) @@ -192,7 +191,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # SelectByLocation(), # ExtractByLocation(), # SpatialJoin(), - # SelectByAttributeSum() # ] algs = [AddTableField(), Aggregate(), diff --git a/python/plugins/processing/algs/qgis/SelectByAttributeSum.py b/python/plugins/processing/algs/qgis/SelectByAttributeSum.py deleted file mode 100644 index 7552b89d0da..00000000000 --- a/python/plugins/processing/algs/qgis/SelectByAttributeSum.py +++ /dev/null @@ -1,107 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - SelectByAttributeSum.py - --------------------- - Date : April 2015 - Copyright : (C) 2015 by Alexander Bruy - Email : alexander dot bruy at gmail dot com -*************************************************************************** -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -*************************************************************************** -""" - -__author__ = 'Alexander Bruy' -__date__ = 'April 2015' -__copyright__ = '(C) 2015, Alexander Bruy' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -from qgis.core import (QgsApplication, - QgsSpatialIndex, - QgsFeatureRequest, - QgsProcessingUtils) - -from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterTableField -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputVector - - -class SelectByAttributeSum(QgisAlgorithm): - INPUT = 'INPUT' - FIELD = 'FIELD' - VALUE = 'VALUE' - OUTPUT = 'OUTPUT' - - def group(self): - return self.tr('Vector selection') - - def __init__(self): - super().__init__() - - def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input Layer'))) - self.addParameter(ParameterTableField(self.FIELD, - self.tr('Selection attribute'), - self.INPUT, - ParameterTableField.DATA_TYPE_NUMBER)) - self.addParameter(ParameterNumber(self.VALUE, self.tr('Value'))) - - self.addOutput(OutputVector(self.OUTPUT, self.tr('Selected (attribute sum)'), True)) - - def name(self): - return 'selectbyattributesum' - - def displayName(self): - return self.tr('Select by attribute sum') - - def processAlgorithm(self, parameters, context, feedback): - fileName = self.getParameterValue(self.INPUT) - layer = QgsProcessingUtils.mapLayerFromString(fileName, context) - fieldName = self.getParameterValue(self.FIELD) - value = self.getParameterValue(self.VALUE) - - selected = layer.selectedFeatureIds() - if len(selected) == 0: - GeoAlgorithmExecutionException( - self.tr('There is no selection in the input layer. ' - 'Select one feature and try again.')) - - ft = layer.selectedFeatures()[0] - geom = ft.geometry() - attrSum = ft[fieldName] - - idx = QgsSpatialIndex(layer.getFeatures(QgsFeatureRequest.setSubsetOfAttributes([])), feedback) - req = QgsFeatureRequest() - completed = False - while not completed: - intersected = idx.intersects(geom.boundingBox()) - if len(intersected) < 0: - feedback.pushInfo(self.tr('No adjacent features found.')) - break - - req = QgsFeatureRequest().setFilterFids(intersected).setSubsetOfAttributes([fieldName], layer.fields()) - for ft in layer.getFeatures(req): - tmpGeom = ft.geometry() - if tmpGeom.touches(geom): - geom = tmpGeom.combine(geom) - selected.append(ft.id()) - attrSum += ft[fieldName] - if attrSum >= value: - completed = True - break - - layer.selectByIds(selected) - self.setOutputValue(self.OUTPUT, fileName) From 8776122d78bab17140192d1b9e86af1ed70377a3 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 10:44:12 +1000 Subject: [PATCH 326/364] Show WKB type in vector layer information tab --- src/core/qgsvectorlayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 62c644b7b7a..3cf9510d9a1 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -3945,7 +3945,8 @@ QString QgsVectorLayer::htmlMetadata() const } else { - QString typeString( QgsWkbTypes::geometryDisplayString( geometryType() ) ); + QString typeString( QStringLiteral( "%1 (%2)" ).arg( QgsWkbTypes::geometryDisplayString( geometryType() ), + QgsWkbTypes::displayString( wkbType() ) ) ); myMetadata += QLatin1String( "
      \n" ); } From 3484eb019c2b8eb682e5a41ff9e6e4b14b9e97d4 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 10:50:08 +1000 Subject: [PATCH 327/364] [FEATURE] Native 'Promote to Multipart' algorithm This algorithm is basically the equivalent of the ST_Multi(...) command - it forces a feature's geometry to become multipart, regardless of the input geometry type. If input geometries are singlepart, they will output as multipart with just 1 part. If they are already multipart, they will be output unchanged. --- .../promote_multipart_already_multi.gfs | 32 ++++++++ .../promote_multipart_already_multi.gml | 42 ++++++++++ .../expected/promote_multipart_lines.gfs | 16 ++++ .../expected/promote_multipart_lines.gml | 48 ++++++++++++ .../expected/promote_multipart_points.gfs | 26 +++++++ .../expected/promote_multipart_points.gml | 77 +++++++++++++++++++ .../expected/promote_multipart_polys.gfs | 32 ++++++++ .../expected/promote_multipart_polys.gml | 58 ++++++++++++++ .../tests/testdata/qgis_algorithm_tests.yaml | 44 +++++++++++ src/core/processing/qgsnativealgorithms.cpp | 31 ++++++++ src/core/processing/qgsnativealgorithms.h | 23 ++++++ 11 files changed, 429 insertions(+) create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gml create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gml create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_points.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_points.gml create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gml diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gfs b/python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gfs new file mode 100644 index 00000000000..67e427b8dc5 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gfs @@ -0,0 +1,32 @@ + + + promote_multipart_already_multi + promote_multipart_already_multi + + 6 + EPSG:4326 + + 4 + 0.00000 + 9.00000 + -1.00000 + 6.00000 + + + Bname + Bname + String + 4 + + + Bintval + Bintval + Integer + + + Bfloatval + Bfloatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gml b/python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gml new file mode 100644 index 00000000000..d0720ba6a65 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_already_multi.gml @@ -0,0 +1,42 @@ + + + + + 0-1 + 96 + + + + + + 2,1 2,2 3,2 3,3 4,3 4,1 2,1 + Test + 1 + 0.123 + + + + + 7,-1 8,-1 8,3 7,3 7,-17,6 7,5 7,4 8,4 9,5 9,6 7,6 + + + + + 0,0 0,1 1,1 1,0 0,0 + Test + 2 + -0.123 + + + + + Test + 3 + 0 + + + diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gfs b/python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gfs new file mode 100644 index 00000000000..0bcc24ed070 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gfs @@ -0,0 +1,16 @@ + + + promote_multipart_lines + promote_multipart_lines + + 5 + EPSG:4326 + + 7 + -1.00000 + 11.00000 + -3.00000 + 5.00000 + + + diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gml b/python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gml new file mode 100644 index 00000000000..d1874f11d15 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_lines.gml @@ -0,0 +1,48 @@ + + + + + -1-3 + 115 + + + + + + 6,2 9,2 9,3 11,5 + + + + + -1,-1 1,-1 + + + + + 2,0 2,2 3,2 3,3 + + + + + 3,1 5,1 + + + + + 7,-3 10,-3 + + + + + 6,-3 10,1 + + + + + + + diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_points.gfs b/python/plugins/processing/tests/testdata/expected/promote_multipart_points.gfs new file mode 100644 index 00000000000..02cb3288a0d --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_points.gfs @@ -0,0 +1,26 @@ + + + promote_multipart_points + promote_multipart_points + + 4 + EPSG:4326 + + 9 + 0.00000 + 8.00000 + -5.00000 + 3.00000 + + + id + id + Integer + + + id2 + id2 + Integer + + + diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_points.gml b/python/plugins/processing/tests/testdata/expected/promote_multipart_points.gml new file mode 100644 index 00000000000..306726628fe --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_points.gml @@ -0,0 +1,77 @@ + + + + + 0-5 + 83 + + + + + + 1,1 + 1 + 2 + + + + + 3,3 + 2 + 1 + + + + + 2,2 + 3 + 0 + + + + + 5,2 + 4 + 2 + + + + + 4,1 + 5 + 1 + + + + + 0,-5 + 6 + 0 + + + + + 8,-1 + 7 + 0 + + + + + 7,-1 + 8 + 0 + + + + + 0,-1 + 9 + 0 + + + diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gfs b/python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gfs new file mode 100644 index 00000000000..76a85935712 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gfs @@ -0,0 +1,32 @@ + + + promote_multipart_polys + promote_multipart_polys + + 6 + EPSG:4326 + + 6 + -1.00000 + 10.00000 + -3.00000 + 6.00000 + + + name + name + String + 5 + + + intval + intval + Integer + + + floatval + floatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gml b/python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gml new file mode 100644 index 00000000000..3017c8055ef --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/promote_multipart_polys.gml @@ -0,0 +1,58 @@ + + + + + -1-3 + 106 + + + + + + -1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1 + aaaaa + 33 + 44.123456 + + + + + 5,5 6,4 4,4 5,5 + Aaaaa + -33 + 0 + + + + + 2,5 2,6 3,6 3,5 2,5 + bbaaa + 0.123 + + + + + 6,1 10,1 10,-3 6,-3 6,17,0 7,-2 9,-2 9,0 7,0 + ASDF + 0 + + + + + 120 + -100291.43213 + + + + + 3,2 6,1 6,-3 2,-1 2,2 3,2 + elim + 2 + 3.33 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index fc0c5b4c551..02fa6a9675d 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3393,3 +3393,47 @@ tests: OUTPUT: name: expected/convex_hull_by_feature.gml type: vector + + - algorithm: native:promotetomulti + name: Promote to multipart lines + params: + INPUT: + name: lines.gml + type: vector + results: + OUTPUT: + name: expected/promote_multipart_lines.gml + type: vector + + - algorithm: native:promotetomulti + name: Promote to multipart lines points + params: + INPUT: + name: points.gml + type: vector + results: + OUTPUT: + name: expected/promote_multipart_points.gml + type: vector + + - algorithm: native:promotetomulti + name: Promote to multipart polygons + params: + INPUT: + name: polys.gml + type: vector + results: + OUTPUT: + name: expected/promote_multipart_polys.gml + type: vector + + - algorithm: native:promotetomulti + name: Promote to multipart (already multipart) + params: + INPUT: + name: multipolys.gml + type: vector + results: + OUTPUT: + name: expected/promote_multipart_already_multi.gml + type: vector diff --git a/src/core/processing/qgsnativealgorithms.cpp b/src/core/processing/qgsnativealgorithms.cpp index 21ec2c908ba..b16b921e41a 100644 --- a/src/core/processing/qgsnativealgorithms.cpp +++ b/src/core/processing/qgsnativealgorithms.cpp @@ -72,6 +72,7 @@ void QgsNativeAlgorithms::loadAlgorithms() addAlgorithm( new QgsOrientedMinimumBoundingBoxAlgorithm() ); addAlgorithm( new QgsMinimumEnclosingCircleAlgorithm() ); addAlgorithm( new QgsConvexHullAlgorithm() ); + addAlgorithm( new QgsPromoteToMultipartAlgorithm() ); } void QgsCentroidAlgorithm::initAlgorithm( const QVariantMap & ) @@ -1258,6 +1259,36 @@ QgsFeature QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, Qg return f; } + +QString QgsPromoteToMultipartAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm takes a vector layer with singlepart geometries and generates a new one in which all geometries are " + "multipart. Input features which are already multipart features will remain unchanged.\n\n" + "This algorithm can be used to force geometries to multipart types in order to be compatibility with data providers " + "with strict singlepart/multipart compatibility checks." ); +} + +QgsPromoteToMultipartAlgorithm *QgsPromoteToMultipartAlgorithm::createInstance() const +{ + return new QgsPromoteToMultipartAlgorithm(); +} + +QgsWkbTypes::Type QgsPromoteToMultipartAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const +{ + return QgsWkbTypes::multiType( inputWkbType ); +} + +QgsFeature QgsPromoteToMultipartAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingFeedback * ) +{ + QgsFeature f = feature; + if ( f.hasGeometry() && !f.geometry().isMultipart() ) + { + QgsGeometry g = f.geometry(); + g.convertToMultiType(); + f.setGeometry( g ); + } + return f; +} ///@endcond diff --git a/src/core/processing/qgsnativealgorithms.h b/src/core/processing/qgsnativealgorithms.h index 89fdaa43b9a..ea0111413da 100644 --- a/src/core/processing/qgsnativealgorithms.h +++ b/src/core/processing/qgsnativealgorithms.h @@ -298,6 +298,29 @@ class QgsMultipartToSinglepartAlgorithm : public QgsProcessingAlgorithm }; +/** + * Native promote to multipart algorithm. + */ +class QgsPromoteToMultipartAlgorithm : public QgsProcessingFeatureBasedAlgorithm +{ + + public: + + QgsPromoteToMultipartAlgorithm() = default; + QString name() const override { return QStringLiteral( "promotetomulti" ); } + QString displayName() const override { return QObject::tr( "Promote to multipart" ); } + virtual QStringList tags() const override { return QObject::tr( "multi,single,multiple,convert,force,parts" ).split( ',' ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } + QString shortHelpString() const override; + QgsPromoteToMultipartAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + QString outputName() const override { return QObject::tr( "Multiparts" ); } + + QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override; + QgsFeature processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) override; + +}; /** * Remove null geometry algorithm. From 16c4f830b3ff33e82021de2e505b42a87ee554da Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 11:25:58 +1000 Subject: [PATCH 328/364] [FEATURE] New algorithm for 'collecting' geometries This is basically the equivalent of the dissolve algorithm, but instead of a dissolving overlapping geometries the geometries are instead just collected together into a multipart geometry. It's designed to slot between the 'promote to multipart' algorithm (which performs no collection of geometries - it just converts singleparts to multiparts with 1 part) and the more complex all-encompassing 'aggregate' algorithm. --- python/plugins/processing/gui/TestTools.py | 2 +- .../tests/testdata/expected/collect_all.gfs | 32 ++++++++++ .../tests/testdata/expected/collect_all.gml | 22 +++++++ .../testdata/expected/collect_one_field.gfs | 32 ++++++++++ .../testdata/expected/collect_one_field.gml | 50 ++++++++++++++++ .../testdata/expected/collect_two_fields.gfs | 32 ++++++++++ .../testdata/expected/collect_two_fields.gml | 58 +++++++++++++++++++ .../tests/testdata/qgis_algorithm_tests.yaml | 39 +++++++++++++ src/core/processing/qgsnativealgorithms.cpp | 56 ++++++++++++++++-- src/core/processing/qgsnativealgorithms.h | 37 +++++++++++- 10 files changed, 353 insertions(+), 7 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/expected/collect_all.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/collect_all.gml create mode 100644 python/plugins/processing/tests/testdata/expected/collect_one_field.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/collect_one_field.gml create mode 100644 python/plugins/processing/tests/testdata/expected/collect_two_fields.gfs create mode 100644 python/plugins/processing/tests/testdata/expected/collect_two_fields.gml diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index ca54882ef47..d224a7dc74d 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -231,7 +231,7 @@ def createTest(text): params[param.name()] = float(token) elif isinstance(param, QgsProcessingParameterEnum): params[param.name()] = int(token) - else: + elif token: if token[0] == '"': token = token[1:] if token[-1] == '"': diff --git a/python/plugins/processing/tests/testdata/expected/collect_all.gfs b/python/plugins/processing/tests/testdata/expected/collect_all.gfs new file mode 100644 index 00000000000..118efeffe0d --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/collect_all.gfs @@ -0,0 +1,32 @@ + + + collect_all + collect_all + + 6 + EPSG:4326 + + 1 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + name + name + String + 2 + + + intval + intval + Integer + + + floatval + floatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/collect_all.gml b/python/plugins/processing/tests/testdata/expected/collect_all.gml new file mode 100644 index 00000000000..964d4008f30 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/collect_all.gml @@ -0,0 +1,22 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + + -1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-16.24145873320538,-0.054510556621882 7.24145873320538,-1.05451055662188 5.24145873320538,-1.05451055662188 6.24145873320538,-0.0545105566218822,5 2,6 3,6 3,5 2,53,2 6,1 6,-3 2,-1 2,2 3,22.44337811900192,4.42360844529751 2.44337811900192,5.42360844529751 3.44337811900192,5.42360844529751 3.44337811900192,4.42360844529751 2.44337811900192,4.423608445297514.17255278310941,4.82264875239923 4.17255278310941,5.82264875239923 5.17255278310941,5.82264875239923 5.17255278310941,4.82264875239923 4.17255278310941,4.822648752399238.16295585412668,2.73877159309021 8.16295585412668,3.73877159309021 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 8.16295585412668,2.738771593090212.62072936660269,5.08867562380038 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 3.62072936660269,5.08867562380038 2.62072936660269,5.08867562380038 + aa + 1 + 44.123456 + + + diff --git a/python/plugins/processing/tests/testdata/expected/collect_one_field.gfs b/python/plugins/processing/tests/testdata/expected/collect_one_field.gfs new file mode 100644 index 00000000000..a8dcd157ff8 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/collect_one_field.gfs @@ -0,0 +1,32 @@ + + + collect_one_field + collect_one_field + + 6 + EPSG:4326 + + 5 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + name + name + String + 2 + + + intval + intval + Integer + + + floatval + floatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/collect_one_field.gml b/python/plugins/processing/tests/testdata/expected/collect_one_field.gml new file mode 100644 index 00000000000..51dd681198d --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/collect_one_field.gml @@ -0,0 +1,50 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + + -1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-13,2 6,1 6,-3 2,-1 2,2 3,2 + aa + 1 + 44.123456 + + + + + 6.24145873320538,-0.054510556621882 7.24145873320538,-1.05451055662188 5.24145873320538,-1.05451055662188 6.24145873320538,-0.054510556621882 + dd + 0 + + + + + 2,5 2,6 3,6 3,5 2,52.44337811900192,4.42360844529751 2.44337811900192,5.42360844529751 3.44337811900192,5.42360844529751 3.44337811900192,4.42360844529751 2.44337811900192,4.423608445297514.17255278310941,4.82264875239923 4.17255278310941,5.82264875239923 5.17255278310941,5.82264875239923 5.17255278310941,4.82264875239923 4.17255278310941,4.822648752399232.62072936660269,5.08867562380038 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 3.62072936660269,5.08867562380038 2.62072936660269,5.08867562380038 + bb + 1 + 0.123 + + + + + 8.16295585412668,2.73877159309021 8.16295585412668,3.73877159309021 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 8.16295585412668,2.73877159309021 + cc + 0.123 + + + + + 120 + -100291.43213 + + + diff --git a/python/plugins/processing/tests/testdata/expected/collect_two_fields.gfs b/python/plugins/processing/tests/testdata/expected/collect_two_fields.gfs new file mode 100644 index 00000000000..8ad47d9b9ab --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/collect_two_fields.gfs @@ -0,0 +1,32 @@ + + + collect_two_fields + collect_two_fields + + 6 + EPSG:4326 + + 6 + -1.00000 + 9.16296 + -3.00000 + 6.08868 + + + name + name + String + 2 + + + intval + intval + Integer + + + floatval + floatval + Real + + + diff --git a/python/plugins/processing/tests/testdata/expected/collect_two_fields.gml b/python/plugins/processing/tests/testdata/expected/collect_two_fields.gml new file mode 100644 index 00000000000..546ada415b7 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/collect_two_fields.gml @@ -0,0 +1,58 @@ + + + + + -1-3 + 9.1629558541266826.088675623800385 + + + + + + 2.62072936660269,5.08867562380038 2.62072936660269,6.08867562380038 3.62072936660269,6.08867562380038 3.62072936660269,5.08867562380038 2.62072936660269,5.08867562380038 + bb + 2 + 0.123 + + + + + -1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-13,2 6,1 6,-3 2,-1 2,2 3,2 + aa + 1 + 44.123456 + + + + + 8.16295585412668,2.73877159309021 8.16295585412668,3.73877159309021 9.16295585412668,3.73877159309021 9.16295585412668,2.73877159309021 8.16295585412668,2.73877159309021 + cc + 0.123 + + + + + 6.24145873320538,-0.054510556621882 7.24145873320538,-1.05451055662188 5.24145873320538,-1.05451055662188 6.24145873320538,-0.054510556621882 + dd + 0 + + + + + 120 + -100291.43213 + + + + + 2,5 2,6 3,6 3,5 2,52.44337811900192,4.42360844529751 2.44337811900192,5.42360844529751 3.44337811900192,5.42360844529751 3.44337811900192,4.42360844529751 2.44337811900192,4.423608445297514.17255278310941,4.82264875239923 4.17255278310941,5.82264875239923 5.17255278310941,5.82264875239923 5.17255278310941,4.82264875239923 4.17255278310941,4.82264875239923 + bb + 1 + 0.123 + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 02fa6a9675d..6d3f36318c6 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -3437,3 +3437,42 @@ tests: OUTPUT: name: expected/promote_multipart_already_multi.gml type: vector + + - algorithm: native:collect + name: Test (native:collect) + params: + INPUT: + name: dissolve_polys.gml + type: vector + results: + OUTPUT: + name: expected/collect_all.gml + type: vector + + - algorithm: native:collect + name: Test (native:collect) + params: + FIELD: + - name + INPUT: + name: dissolve_polys.gml + type: vector + results: + OUTPUT: + name: expected/collect_one_field.gml + type: vector + + - algorithm: native:collect + name: Test (native:collect) + params: + FIELD: + - intval + - name + INPUT: + name: dissolve_polys.gml + type: vector + results: + OUTPUT: + name: expected/collect_two_fields.gml + type: vector + diff --git a/src/core/processing/qgsnativealgorithms.cpp b/src/core/processing/qgsnativealgorithms.cpp index b16b921e41a..cb473b0b027 100644 --- a/src/core/processing/qgsnativealgorithms.cpp +++ b/src/core/processing/qgsnativealgorithms.cpp @@ -25,6 +25,8 @@ #include "qgsgeometryengine.h" #include "qgswkbtypes.h" +#include + ///@cond PRIVATE QgsNativeAlgorithms::QgsNativeAlgorithms( QObject *parent ) @@ -62,6 +64,7 @@ void QgsNativeAlgorithms::loadAlgorithms() addAlgorithm( new QgsCentroidAlgorithm() ); addAlgorithm( new QgsClipAlgorithm() ); addAlgorithm( new QgsDissolveAlgorithm() ); + addAlgorithm( new QgsCollectAlgorithm() ); addAlgorithm( new QgsExtractByAttributeAlgorithm() ); addAlgorithm( new QgsExtractByExpressionAlgorithm() ); addAlgorithm( new QgsMultipartToSinglepartAlgorithm() ); @@ -243,7 +246,8 @@ QgsDissolveAlgorithm *QgsDissolveAlgorithm::createInstance() const return new QgsDissolveAlgorithm(); } -QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) +QVariantMap QgsCollectorAlgorithm::processCollection( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, + std::function& )> collector, int maxQueueLength ) { std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) @@ -289,10 +293,10 @@ QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap ¶meter if ( f.hasGeometry() && f.geometry() ) { geomQueue.append( f.geometry() ); - if ( geomQueue.length() > 10000 ) + if ( maxQueueLength > 0 && geomQueue.length() > maxQueueLength ) { // queue too long, combine it - QgsGeometry tempOutputGeometry = QgsGeometry::unaryUnion( geomQueue ); + QgsGeometry tempOutputGeometry = collector( geomQueue ); geomQueue.clear(); geomQueue << tempOutputGeometry; } @@ -302,7 +306,7 @@ QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap ¶meter current++; } - outputFeature.setGeometry( QgsGeometry::unaryUnion( geomQueue ) ); + outputFeature.setGeometry( collector( geomQueue ) ); sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); } else @@ -355,7 +359,7 @@ QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap ¶meter QgsFeature outputFeature; if ( geometryHash.contains( attrIt.key() ) ) { - QgsGeometry geom = QgsGeometry::unaryUnion( geometryHash.value( attrIt.key() ) ); + QgsGeometry geom = collector( geometryHash.value( attrIt.key() ) ); if ( !geom.isMultipart() ) { geom.convertToMultiType(); @@ -375,6 +379,23 @@ QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap ¶meter return outputs; } +QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) +{ + return processCollection( parameters, context, feedback, []( const QList< QgsGeometry > &parts )->QgsGeometry + { + return QgsGeometry::unaryUnion( parts ); + }, 10000 ); +} + +QVariantMap QgsCollectAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) +{ + return processCollection( parameters, context, feedback, []( const QList< QgsGeometry > &parts )->QgsGeometry + { + return QgsGeometry::collectGeometry( parts ); + } ); +} + + void QgsClipAlgorithm::initAlgorithm( const QVariantMap & ) { addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) ); @@ -1289,6 +1310,31 @@ QgsFeature QgsPromoteToMultipartAlgorithm::processFeature( const QgsFeature &fea } return f; } + + +void QgsCollectAlgorithm::initAlgorithm( const QVariantMap & ) +{ + addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) ); + addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Unique ID fields" ), QVariant(), + QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, true, true ) ); + + addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Collected" ) ) ); +} + +QString QgsCollectAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm takes a vector layer and collects its geometries into new multipart geometries. One or more attributes can " + "be specified to collect only geometries belonging to the same class (having the same value for the specified attributes), alternatively " + "all geometries can be collected.\n\n" + "All output geometries will be converted to multi geometries, even those with just a single part. " + "This algorithm does not dissolve overlapping geometries - they will be collected together without modifying the shape of each geometry part." ); +} + +QgsCollectAlgorithm *QgsCollectAlgorithm::createInstance() const +{ + return new QgsCollectAlgorithm(); +} + ///@endcond diff --git a/src/core/processing/qgsnativealgorithms.h b/src/core/processing/qgsnativealgorithms.h index ea0111413da..1a6d86f3c82 100644 --- a/src/core/processing/qgsnativealgorithms.h +++ b/src/core/processing/qgsnativealgorithms.h @@ -131,10 +131,21 @@ class QgsBufferAlgorithm : public QgsProcessingAlgorithm }; +/** + * Base class for dissolve/collect type algorithms. + */ +class QgsCollectorAlgorithm : public QgsProcessingAlgorithm +{ + protected: + + QVariantMap processCollection( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, + std::function& )> collector, int maxQueueLength = 0 ); +}; + /** * Native dissolve algorithm. */ -class QgsDissolveAlgorithm : public QgsProcessingAlgorithm +class QgsDissolveAlgorithm : public QgsCollectorAlgorithm { public: @@ -155,6 +166,30 @@ class QgsDissolveAlgorithm : public QgsProcessingAlgorithm }; +/** + * Native collect geometries algorithm. + */ +class QgsCollectAlgorithm : public QgsCollectorAlgorithm +{ + + public: + + QgsCollectAlgorithm() = default; + void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override; + QString name() const override { return QStringLiteral( "collect" ); } + QString displayName() const override { return QObject::tr( "Collect geometries" ); } + virtual QStringList tags() const override { return QObject::tr( "union,combine,collect,multipart,parts,single" ).split( ',' ); } + QString group() const override { return QObject::tr( "Vector geometry" ); } + QString shortHelpString() const override; + QgsCollectAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + + virtual QVariantMap processAlgorithm( const QVariantMap ¶meters, + QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; + +}; + /** * Native extract by attribute algorithm. */ From a55fbd8ef349a52c166b69b70ce6cfccad8c42aa Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 11:31:14 +1000 Subject: [PATCH 329/364] [FEATURE] Remove Singleparts to Multiparts algorithm This algorithm is no longer required - it's been replaced by the 'Promote to multipart' and 'Collect geometries" algorithms. Tagged as feature to remember to include in release notes --- python/plugins/processing/algs/help/qgis.yaml | 3 - .../algs/qgis/QGISAlgorithmProvider.py | 2 - .../algs/qgis/SinglePartsToMultiparts.py | 123 ------------------ .../tests/testdata/qgis_algorithm_tests.yaml | 2 +- 4 files changed, 1 insertion(+), 129 deletions(-) delete mode 100644 python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index 3c795620708..c118ffe208b 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -544,9 +544,6 @@ qgis:simplifygeometries: > The algorithm gives a choice of simplification methods, including distance based (the "Douglas-Peucker" algorithm), area based ("Visvalingam" algorithm) and snapping geometries to grid. -qgis:singlepartstomultipart: - - qgis:singlesidedbuffer: > This algorithm buffers lines by a specified distance on one side of the line only. diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index f059a9cdc49..9a887bb75bc 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -144,7 +144,6 @@ from .ShortestPathLayerToPoint import ShortestPathLayerToPoint from .ShortestPathPointToLayer import ShortestPathPointToLayer from .ShortestPathPointToPoint import ShortestPathPointToPoint from .SimplifyGeometries import SimplifyGeometries -from .SinglePartsToMultiparts import SinglePartsToMultiparts from .SingleSidedBuffer import SingleSidedBuffer from .Slope import Slope from .Smooth import Smooth @@ -292,7 +291,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider): ShortestPathPointToLayer(), ShortestPathPointToPoint(), SimplifyGeometries(), - SinglePartsToMultiparts(), SingleSidedBuffer(), Slope(), Smooth(), diff --git a/python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py b/python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py deleted file mode 100644 index b9ce604d8f1..00000000000 --- a/python/plugins/processing/algs/qgis/SinglePartsToMultiparts.py +++ /dev/null @@ -1,123 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - SinglePartsToMultiparts.py - --------------------- - Date : August 2012 - Copyright : (C) 2012 by Victor Olaya - Email : volayaf at gmail dot com -*************************************************************************** -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -*************************************************************************** -""" -from builtins import str - -__author__ = 'Victor Olaya' -__date__ = 'August 2012' -__copyright__ = '(C) 2012, Victor Olaya' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -import os - -from qgis.PyQt.QtGui import QIcon - -from qgis.core import (QgsFeature, - QgsFeatureSink, - QgsGeometry, - QgsWkbTypes, - QgsProcessingUtils, - NULL, - QgsProcessingParameterFeatureSource, - QgsProcessingParameterField, - QgsProcessingParameterFeatureSink) - -from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm - -pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] - - -class SinglePartsToMultiparts(QgisAlgorithm): - - INPUT = 'INPUT' - FIELD = 'FIELD' - OUTPUT = 'OUTPUT' - - def icon(self): - return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'single_to_multi.png')) - - def group(self): - return self.tr('Vector geometry') - - def __init__(self): - super().__init__() - - def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Input layer'))) - self.addParameter(QgsProcessingParameterField(self.FIELD, - self.tr('Unique ID field'), parentLayerParameterName=self.INPUT)) - - self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Multipart'))) - - def name(self): - return 'singlepartstomultipart' - - def displayName(self): - return self.tr('Singleparts to multipart') - - def processAlgorithm(self, parameters, context, feedback): - source = self.parameterAsSource(parameters, self.INPUT, context) - field_name = self.parameterAsString(parameters, self.FIELD, context) - - geom_type = QgsWkbTypes.multiType(source.wkbType()) - - (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, - source.fields(), geom_type, source.sourceCrs()) - - index = source.fields().lookupField(field_name) - - collection_geom = {} - collection_attrs = {} - - features = source.getFeatures() - total = 100.0 / source.featureCount() if source.featureCount() else 0 - for current, feature in enumerate(features): - if feedback.isCanceled(): - break - - atMap = feature.attributes() - idVar = atMap[index] - if idVar in [None, NULL] or not feature.hasGeometry(): - sink.addFeature(feature, QgsFeatureSink.FastInsert) - feedback.setProgress(int(current * total)) - continue - - key = str(idVar).strip() - if key not in collection_geom: - collection_geom[key] = [] - collection_attrs[key] = atMap - - inGeom = feature.geometry() - collection_geom[key].append(inGeom) - - feedback.setProgress(int(current * total)) - - for key, geoms in collection_geom.items(): - if feedback.isCanceled(): - break - - feature = QgsFeature() - feature.setAttributes(collection_attrs[key]) - feature.setGeometry(QgsGeometry.collectGeometry(geoms)) - sink.addFeature(feature, QgsFeatureSink.FastInsert) - - return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 6d3f36318c6..0bcff7fcfa1 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2683,7 +2683,7 @@ tests: name: expected/mean_coordinates.gml type: vector - - algorithm: qgis:singlepartstomultipart + - algorithm: native:collect name: single part to multipart params: FIELD: id From 08c5c4b18aae7e0074756a5b2a91eb493751243a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 19:46:26 +1000 Subject: [PATCH 330/364] Add notes to see related algorithms in algorithm help --- src/core/processing/qgsnativealgorithms.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/processing/qgsnativealgorithms.cpp b/src/core/processing/qgsnativealgorithms.cpp index cb473b0b027..c2f5ad87b43 100644 --- a/src/core/processing/qgsnativealgorithms.cpp +++ b/src/core/processing/qgsnativealgorithms.cpp @@ -1286,7 +1286,8 @@ QString QgsPromoteToMultipartAlgorithm::shortHelpString() const return QObject::tr( "This algorithm takes a vector layer with singlepart geometries and generates a new one in which all geometries are " "multipart. Input features which are already multipart features will remain unchanged.\n\n" "This algorithm can be used to force geometries to multipart types in order to be compatibility with data providers " - "with strict singlepart/multipart compatibility checks." ); + "with strict singlepart/multipart compatibility checks.\n\n" + "See the 'Collect geometries' or 'Aggregate' algorithms for alternative options." ); } QgsPromoteToMultipartAlgorithm *QgsPromoteToMultipartAlgorithm::createInstance() const @@ -1327,7 +1328,8 @@ QString QgsCollectAlgorithm::shortHelpString() const "be specified to collect only geometries belonging to the same class (having the same value for the specified attributes), alternatively " "all geometries can be collected.\n\n" "All output geometries will be converted to multi geometries, even those with just a single part. " - "This algorithm does not dissolve overlapping geometries - they will be collected together without modifying the shape of each geometry part." ); + "This algorithm does not dissolve overlapping geometries - they will be collected together without modifying the shape of each geometry part.\n\n" + "See the 'Promote to multipart' or 'Aggregate' algorithms for alternative options." ); } QgsCollectAlgorithm *QgsCollectAlgorithm::createInstance() const From 7627851026872294e89b87f027d2c56903d6a081 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 5 Sep 2017 19:46:49 +1000 Subject: [PATCH 331/364] Fix bad strings --- python/plugins/processing/algs/help/qgis.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index c118ffe208b..624722ab4f2 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -153,7 +153,7 @@ qgis:dissolve: > If the geometries to be dissolved are spatially separated from each other the output will be multi geometries. In case the input is a polygon layer, common boundaries of adjacent polygons being dissolved will get erased. qgis:distancematrix: > - This algorithms creates a table containing a distance matrix, with distances between all the points in a points layer. + This algorithm creates a table containing a distance matrix, with distances between all the points in a points layer. qgis:distancetonearesthub: > Given a layer with source point and another one representing destination points, this algorithm computes the distance between each source point and the closest destination one. @@ -277,7 +277,7 @@ qgis:joinattributestable: > The additional attributes and their values are taken from a second vector layer. An attribute is selected in each of them to define the join criteria. qgis:keepnbiggestparts: > - This algorithms takes a polygon layer and creates a new polygon layer in which multipart geometries have been removed, leaving only the n largest (in terms of area) parts. + This algorithm takes a polygon layer and creates a new polygon layer in which multipart geometries have been removed, leaving only the n largest (in terms of area) parts. qgis:lineintersections: > This algorithm creates point features where the lines in the Intersect layer intersect the lines in the Input layer. From 4ea4478bad555876984c873c9b03b26b02b6ec40 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 5 Sep 2017 10:16:10 +0200 Subject: [PATCH 332/364] Thread safety for feature counter Make sure that results from a feature counter will only be delivered on the main thread and that they will be discarded if the layer is deleted meanwhile. --- python/core/qgsvectorlayer.sip | 4 ---- src/core/qgsvectorlayer.cpp | 15 +++++++++++++-- src/core/qgsvectorlayer.h | 7 +++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/python/core/qgsvectorlayer.sip b/python/core/qgsvectorlayer.sip index 8334b6d64d6..f86e57b537b 100644 --- a/python/core/qgsvectorlayer.sip +++ b/python/core/qgsvectorlayer.sip @@ -1787,10 +1787,6 @@ Returns the current blending mode for features :rtype: bool %End - - protected slots: - void invalidateSymbolCountedFlag(); - signals: void selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect ); diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 62c644b7b7a..9a410c9ab02 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -770,8 +770,8 @@ bool QgsVectorLayer::countSymbolFeatures() if ( !mFeatureCounter ) { mFeatureCounter = new QgsVectorLayerFeatureCounter( this ); - connect( mFeatureCounter, &QgsTask::taskCompleted, [ = ]() { onSymbolsCounted(); mFeatureCounter = nullptr; } ); - connect( mFeatureCounter, &QgsTask::taskTerminated, [ = ]() { mFeatureCounter = nullptr; } ); + connect( mFeatureCounter, &QgsTask::taskCompleted, this, &QgsVectorLayer::onFeatureCounterCompleted ); + connect( mFeatureCounter, &QgsTask::taskTerminated, this, &QgsVectorLayer::onFeatureCounterTerminated ); QgsApplication::taskManager()->addTask( mFeatureCounter ); } @@ -4034,6 +4034,17 @@ void QgsVectorLayer::invalidateSymbolCountedFlag() mSymbolFeatureCounted = false; } +void QgsVectorLayer::onFeatureCounterCompleted() +{ + onSymbolsCounted(); + mFeatureCounter = nullptr; +} + +void QgsVectorLayer::onFeatureCounterTerminated() +{ + mFeatureCounter = nullptr; +} + void QgsVectorLayer::onJoinedFieldsChanged() { // some of the fields of joined layers have changed -> we need to update this layer's fields too diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 69d1488bcd4..9dfc060b0e7 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1662,10 +1662,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte */ bool startEditing(); - - protected slots: - void invalidateSymbolCountedFlag(); - signals: /** @@ -1901,6 +1897,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte void symbolFeatureCountMapChanged(); private slots: + void invalidateSymbolCountedFlag(); + void onFeatureCounterCompleted(); + void onFeatureCounterTerminated(); void onJoinedFieldsChanged(); void onFeatureDeleted( QgsFeatureId fid ); void onRelationsLoaded(); From 8efa9fa815e1437432a44ed00ea51f063da27014 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 5 Sep 2017 11:30:13 +0200 Subject: [PATCH 333/364] [FEATURE] Add "starts with" and "ends with" to multi attribute search --- .../editorwidgets/core/qgssearchwidgetwrapper.sip | 2 ++ .../editorwidgets/core/qgssearchwidgetwrapper.cpp | 9 +++++++-- .../editorwidgets/core/qgssearchwidgetwrapper.h | 2 ++ .../qgsdefaultsearchwidgetwrapper.cpp | 14 ++++++++++---- tests/src/python/test_qgssearchwidgetwrapper.py | 4 ++++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/python/gui/editorwidgets/core/qgssearchwidgetwrapper.sip b/python/gui/editorwidgets/core/qgssearchwidgetwrapper.sip index 1351c58b5a1..648629b6094 100644 --- a/python/gui/editorwidgets/core/qgssearchwidgetwrapper.sip +++ b/python/gui/editorwidgets/core/qgssearchwidgetwrapper.sip @@ -90,6 +90,8 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper IsNull, IsNotBetween, IsNotNull, + StartsWith, + EndsWith, }; typedef QFlags FilterFlags; diff --git a/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp b/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp index 8dc5361ec25..450e9ff6d99 100644 --- a/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp +++ b/src/gui/editorwidgets/core/qgssearchwidgetwrapper.cpp @@ -34,7 +34,9 @@ QList QgsSearchWidgetWrapper::exclusiveFilte << Contains << DoesNotContain << IsNull - << IsNotNull; + << IsNotNull + << StartsWith + << EndsWith; } QList QgsSearchWidgetWrapper::nonExclusiveFilterFlags() @@ -73,7 +75,10 @@ QString QgsSearchWidgetWrapper::toString( QgsSearchWidgetWrapper::FilterFlag fla return QObject::tr( "Is missing (null)" ); case IsNotNull: return QObject::tr( "Is not missing (not null)" ); - + case StartsWith: + return QObject::tr( "Starts with" ); + case EndsWith: + return QObject::tr( "Ends with" ); } return QString(); } diff --git a/src/gui/editorwidgets/core/qgssearchwidgetwrapper.h b/src/gui/editorwidgets/core/qgssearchwidgetwrapper.h index 4b19d2e6bc6..ce304c71cdc 100644 --- a/src/gui/editorwidgets/core/qgssearchwidgetwrapper.h +++ b/src/gui/editorwidgets/core/qgssearchwidgetwrapper.h @@ -109,6 +109,8 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper IsNull = 1 << 11, //!< Supports searching for null values IsNotBetween = 1 << 12, //!< Supports searching for values outside of a set range IsNotNull = 1 << 13, //!< Supports searching for non-null values + StartsWith = 1 << 14, //!< Supports searching for strings that start with + EndsWith = 1 << 15, //!< Supports searching for strings that end with }; Q_DECLARE_FLAGS( FilterFlags, FilterFlag ) diff --git a/src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.cpp b/src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.cpp index 569fa8eea1b..abcd6d2c1d8 100644 --- a/src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.cpp +++ b/src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.cpp @@ -109,7 +109,7 @@ QgsSearchWidgetWrapper::FilterFlags QgsDefaultSearchWidgetWrapper::supportedFlag break; case QVariant::String: - flags |= Contains | DoesNotContain; + flags |= Contains | DoesNotContain | StartsWith | EndsWith; break; default: @@ -213,15 +213,21 @@ QString QgsDefaultSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper: + ( flags & EqualTo ? "=" : "<>" ) + QStringLiteral( "lower(%1)" ).arg( QgsExpression::quotedString( mLineEdit->text() ) ); } - else if ( flags & Contains || flags & DoesNotContain ) + else if ( flags & Contains || flags & DoesNotContain || flags & StartsWith || flags & EndsWith ) { QString exp = fieldName + ( mCheckbox && mCheckbox->isChecked() ? " LIKE " : " ILIKE " ); QString value = QgsExpression::quotedString( mLineEdit->text() ); value.chop( 1 ); value = value.remove( 0, 1 ); - exp += "'%" + value + "%'"; + exp += '\''; + if ( !flags.testFlag( StartsWith ) ) + exp += '%'; + exp += value; + if ( !flags.testFlag( EndsWith ) ) + exp += '%'; + exp += '\''; if ( flags & DoesNotContain ) - exp.prepend( "NOT (" ).append( ")" ); + exp.prepend( "NOT (" ).append( ')' ); return exp; } diff --git a/tests/src/python/test_qgssearchwidgetwrapper.py b/tests/src/python/test_qgssearchwidgetwrapper.py index ccbd555b01e..c5c5e54cd6f 100644 --- a/tests/src/python/test_qgssearchwidgetwrapper.py +++ b/tests/src/python/test_qgssearchwidgetwrapper.py @@ -87,9 +87,13 @@ class PyQgsDefaultSearchWidgetWrapper(unittest.TestCase): case_sensitive.setChecked(False) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.Contains), '"fldtxt" ILIKE \'%test%\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.DoesNotContain), 'NOT ("fldtxt" ILIKE \'%test%\')') + self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.StartsWith), '"fldtxt" ILIKE \'test%\'') + self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EndsWith), '"fldtxt" ILIKE \'%test\'') case_sensitive.setChecked(True) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.Contains), '"fldtxt" LIKE \'%test%\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.DoesNotContain), 'NOT ("fldtxt" LIKE \'%test%\')') + self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.StartsWith), '"fldtxt" LIKE \'test%\'') + self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EndsWith), '"fldtxt" LIKE \'%test\'') case_sensitive.setChecked(False) # numeric field From 117261bfb772ad93b76451a9edf621a9d9cb5e49 Mon Sep 17 00:00:00 2001 From: Richard Duivenvoorde Date: Mon, 4 Sep 2017 19:44:43 +0200 Subject: [PATCH 334/364] Fix for #17104 --- python/plugins/processing/gui/wrappers_postgis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/plugins/processing/gui/wrappers_postgis.py b/python/plugins/processing/gui/wrappers_postgis.py index 402663cc54b..3514cc4d681 100644 --- a/python/plugins/processing/gui/wrappers_postgis.py +++ b/python/plugins/processing/gui/wrappers_postgis.py @@ -104,7 +104,8 @@ class SchemaWidgetWrapper(WidgetWrapper, ExpressionWidgetWrapperMixin): def setConnection(self, connection): self._connection = connection - if isinstance(connection, str): + # when there is NO connection (yet), this get's called with a ''-connection + if isinstance(connection, str) and connection != '': self._database = GeoDB.from_name(connection) else: self._database = None From b3448ed4a810b64c762ddac69095a642b4298fa7 Mon Sep 17 00:00:00 2001 From: DiGro Date: Tue, 5 Sep 2017 14:28:49 +0200 Subject: [PATCH 335/364] Typo corrected appoximation --> should be: approximation --- resources/function_help/json/hausdorff_distance | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/function_help/json/hausdorff_distance b/resources/function_help/json/hausdorff_distance index 88b836958c7..8f97b628eee 100644 --- a/resources/function_help/json/hausdorff_distance +++ b/resources/function_help/json/hausdorff_distance @@ -1,7 +1,7 @@ { "name": "hausdorff_distance", "type": "function", - "description": "Returns the Hausdorff distance between two geometries. This is basically a measure of how similar or dissimilar 2 geometries are, with a lower distance indicating more similar geometries.
      The function can be executed with an optional densify fraction argument. If not specified, an appoximation to the standard Hausdorff distance is used. This approximation is exact or close enough for a large subset of useful cases. Examples of these are:

    7. computing distance between Linestrings that are roughly parallel to each other, and roughly equal in length. This occurs in matching linear networks.
    8. Testing similarity of geometries.


    9. If the default approximate provided by this method is insufficient, specify the optional densify fraction argument. Specifying this argument performs a segment densification before computing the discrete Hausdorff distance. The parameter sets the fraction by which to densify each segment. Each segment will be split into a number of equal-length subsegments, whose fraction of the total length is closest to the given fraction. Decreasing the densify fraction parameter will make the distance returned approach the true Hausdorff distance for the geometries.", + "description": "Returns the Hausdorff distance between two geometries. This is basically a measure of how similar or dissimilar 2 geometries are, with a lower distance indicating more similar geometries.
      The function can be executed with an optional densify fraction argument. If not specified, an approximation to the standard Hausdorff distance is used. This approximation is exact or close enough for a large subset of useful cases. Examples of these are:

    10. computing distance between Linestrings that are roughly parallel to each other, and roughly equal in length. This occurs in matching linear networks.
    11. Testing similarity of geometries.


    12. If the default approximate provided by this method is insufficient, specify the optional densify fraction argument. Specifying this argument performs a segment densification before computing the discrete Hausdorff distance. The parameter sets the fraction by which to densify each segment. Each segment will be split into a number of equal-length subsegments, whose fraction of the total length is closest to the given fraction. Decreasing the densify fraction parameter will make the distance returned approach the true Hausdorff distance for the geometries.", "arguments": [ {"arg":"geometry a","description":"a geometry"}, {"arg":"geometry b","description":"a geometry"}, {"arg":"densify_fraction","description":"densify fraction amount", "optional":true}], From fb125056e19a374eee6f5ae206a584d9d57c90ba Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 5 Sep 2017 16:55:10 +0200 Subject: [PATCH 336/364] Removed SIP_FACTORY and fixed docstrings --- src/gui/qgssourceselectprovider.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gui/qgssourceselectprovider.h b/src/gui/qgssourceselectprovider.h index bca6549340b..c7fdf181dea 100644 --- a/src/gui/qgssourceselectprovider.h +++ b/src/gui/qgssourceselectprovider.h @@ -27,7 +27,7 @@ class QString; class QWidget; /** \ingroup gui - * This is the interface for those who want to add entries to the \see QgsDataSourceManagerDialog + * This is the interface for those who want to add entries to the QgsDataSourceManagerDialog * * \since QGIS 3.0 */ @@ -50,10 +50,14 @@ class GUI_EXPORT QgsSourceSelectProvider //! Text for the menu item entry, it will be visible to the user so make sure it's translatable virtual QString text() const = 0; - /** Creates a new instance of an QIcon for the menu item entry - * Caller takes responsibility of deleting created. + /** Text for the tooltip menu item entry, it will be visible to the user so make sure it's translatable + * + * The default implementation returns an empty string. */ - virtual QIcon icon() const = 0 SIP_FACTORY; + virtual QString toolTip() const { return QString(); } + + //! Creates a new instance of an QIcon for the menu item entry + virtual QIcon icon() const = 0; /** Ordering: the source select provider registry will be able to sort * the source selects (ascending) using this integer value From 41eccd1bf9324ce26425fbc28cf37f64088a42ea Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 5 Sep 2017 16:56:09 +0200 Subject: [PATCH 337/364] Sip updates for qgssourceselectprovider --- python/gui/qgssourceselectprovider.sip | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/python/gui/qgssourceselectprovider.sip b/python/gui/qgssourceselectprovider.sip index 231afb286ce..d12580f4190 100644 --- a/python/gui/qgssourceselectprovider.sip +++ b/python/gui/qgssourceselectprovider.sip @@ -12,7 +12,7 @@ class QgsSourceSelectProvider { %Docstring -.. seealso:: QgsDataSourceManagerDialog + This is the interface for those who want to add entries to the QgsDataSourceManagerDialog .. versionadded:: 3.0 %End @@ -45,10 +45,17 @@ Text for the menu item entry, it will be visible to the user so make sure it's t :rtype: str %End - virtual QIcon icon() const = 0 /Factory/; + virtual QString toolTip() const; %Docstring - Creates a new instance of an QIcon for the menu item entry - Caller takes responsibility of deleting created. + Text for the tooltip menu item entry, it will be visible to the user so make sure it's translatable + + The default implementation returns an empty string. + :rtype: str +%End + + virtual QIcon icon() const = 0; +%Docstring +Creates a new instance of an QIcon for the menu item entry :rtype: QIcon %End From e43cb79630b702b4ad76b99711255f3ef7b83bff Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 5 Sep 2017 16:58:17 +0200 Subject: [PATCH 338/364] Q_FOREACH removal --- src/gui/qgssourceselectproviderregistry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/qgssourceselectproviderregistry.cpp b/src/gui/qgssourceselectproviderregistry.cpp index 1fd0ceb9b4d..7d576f41cbc 100644 --- a/src/gui/qgssourceselectproviderregistry.cpp +++ b/src/gui/qgssourceselectproviderregistry.cpp @@ -91,8 +91,8 @@ void QgsSourceSelectProviderRegistry::init() { return; } - QStringList providersList = QgsProviderRegistry::instance()->providerList(); - Q_FOREACH ( const QString &key, providersList ) + const QStringList providersList = QgsProviderRegistry::instance()->providerList(); + for ( const QString &key : providersList ) { std::unique_ptr< QLibrary > library( QgsProviderRegistry::instance()->createProviderLibrary( key ) ); if ( !library ) From 08588a54da21914aea2b34a7989a871ddaf94b48 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 5 Sep 2017 17:03:20 +0200 Subject: [PATCH 339/364] Add tooltips and tests for the provider class --- src/gui/qgsdatasourcemanagerdialog.cpp | 18 ++++++++++++------ .../src/python/test_qgssourceselectprovider.py | 8 ++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/gui/qgsdatasourcemanagerdialog.cpp b/src/gui/qgsdatasourcemanagerdialog.cpp index 164b81bad11..5cc61b9b3dd 100644 --- a/src/gui/qgsdatasourcemanagerdialog.cpp +++ b/src/gui/qgsdatasourcemanagerdialog.cpp @@ -14,7 +14,6 @@ * * ***************************************************************************/ -#include #include #include "qgsdatasourcemanagerdialog.h" @@ -26,6 +25,7 @@ #include "qgssourceselectproviderregistry.h" #include "qgsabstractdatasourcewidget.h" #include "qgsmapcanvas.h" +#include "qgsmessagelog.h" #include "qgsgui.h" QgsDataSourceManagerDialog::QgsDataSourceManagerDialog( QWidget *parent, QgsMapCanvas *canvas, Qt::WindowFlags fl ) : @@ -65,10 +65,10 @@ QgsDataSourceManagerDialog::QgsDataSourceManagerDialog( QWidget *parent, QgsMapC QgsAbstractDataSourceWidget *dlg = provider->createDataSourceWidget( this ); if ( !dlg ) { - QMessageBox::warning( this, provider->name(), tr( "Cannot get %1 select dialog from source select provider %2." ).arg( provider->name(), provider->providerKey() ) ); + QgsMessageLog::logMessage( tr( "Cannot get %1 select dialog from source select provider %2." ).arg( provider->name(), provider->providerKey() ), QStringLiteral( "DataSourceManager" ), QgsMessageLog::CRITICAL ); continue; } - addProviderDialog( dlg, provider->providerKey(), provider->text(), provider->icon( ) ); + addProviderDialog( dlg, provider->providerKey(), provider->text(), provider->icon( ), provider->toolTip( ) ); } } @@ -150,14 +150,20 @@ void QgsDataSourceManagerDialog::makeConnections( QgsAbstractDataSourceWidget *d this, SIGNAL( showStatusMessage( QString ) ) ); // Vector connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayer, this, [ = ]( const QString & vectorLayerPath, const QString & baseName ) - { this->vectorLayerAdded( vectorLayerPath, baseName, providerKey ); } ); - connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayers, this, &QgsDataSourceManagerDialog::vectorLayersAdded ); connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); + { + this->vectorLayerAdded( vectorLayerPath, baseName, providerKey ); + } + ); + connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayers, + this, &QgsDataSourceManagerDialog::vectorLayersAdded ); + connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); // Raster connect( dlg, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ), this, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ) ); // Virtual - connect( dlg, SIGNAL( replaceVectorLayer( QString, QString, QString, QString ) ), this, SIGNAL( replaceSelectedVectorLayer( QString, QString, QString, QString ) ) ); + connect( dlg, SIGNAL( replaceVectorLayer( QString, QString, QString, QString ) ), + this, SIGNAL( replaceSelectedVectorLayer( QString, QString, QString, QString ) ) ); // Common connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); connect( this, SIGNAL( providerDialogsRefreshRequested() ), dlg, SLOT( refresh() ) ); diff --git a/tests/src/python/test_qgssourceselectprovider.py b/tests/src/python/test_qgssourceselectprovider.py index 61ee975b74e..beecbb07774 100644 --- a/tests/src/python/test_qgssourceselectprovider.py +++ b/tests/src/python/test_qgssourceselectprovider.py @@ -61,6 +61,9 @@ class ConcreteSourceSelectProvider2(QgsSourceSelectProvider): def name(self): return "MyName" + def toolTip(self): + return "MyToolTip" + def icon(self): return QIcon() @@ -88,9 +91,14 @@ class TestQgsSourceSelectProvider(unittest.TestCase): self.assertEqual(provider.providerKey(), "MyTestProviderKey") self.assertEqual(provider.name(), "MyTestProviderKey") self.assertEqual(provider.text(), "MyTestProviderText") + self.assertEqual(provider.toolTip(), "") self.assertEqual(provider.ordering(), 1) self.assertTrue(isinstance(provider.icon(), QIcon)) + # test toolTip + provider = ConcreteSourceSelectProvider2() + self.assertEqual(provider.toolTip(), "MyToolTip") + def _testRegistry(self, registry): registry.addProvider(ConcreteSourceSelectProvider()) From 59aaa2780e8326f0cf425f70805a9b06ec9b3c5c Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 5 Sep 2017 17:08:04 +0200 Subject: [PATCH 340/364] Use the tooltip: virtual layer needs it ... because the automatically generated one was: "Add Virtual Layer layer" --- src/providers/virtual/qgsvirtuallayerprovider.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/providers/virtual/qgsvirtuallayerprovider.cpp b/src/providers/virtual/qgsvirtuallayerprovider.cpp index 0bd6a675b69..7933e02a143 100644 --- a/src/providers/virtual/qgsvirtuallayerprovider.cpp +++ b/src/providers/virtual/qgsvirtuallayerprovider.cpp @@ -664,6 +664,7 @@ class QgsVirtualSourceSelectProvider : public QgsSourceSelectProvider virtual QString providerKey() const override { return QStringLiteral( "virtual" ); } virtual QString text() const override { return QObject::tr( "Virtual Layer" ); } virtual int ordering() const override { return 90; } + virtual QString toolTip() const override { return QObject::tr( "Add Virtual Layer" ); } virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddVirtualLayer.svg" ) ); } virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override { From 63d0a91566fe2593d8980ce6df5c6d4cbfb3ed8d Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 5 Sep 2017 18:15:11 +0200 Subject: [PATCH 341/364] [FEATURE] History for attribute table form view display expressions It's possible to re-use the last 10 display expressions in the form view of the attribute table. The expressions will also be persisted in the project file. Using fields will now show field aliases instead of column names where available. --- python/gui/attributetable/qgsdualview.sip | 10 +- src/gui/attributetable/qgsdualview.cpp | 157 +++++++++++++++++----- src/gui/attributetable/qgsdualview.h | 24 ++-- 3 files changed, 142 insertions(+), 49 deletions(-) diff --git a/python/gui/attributetable/qgsdualview.sip b/python/gui/attributetable/qgsdualview.sip index 5699eed4a08..0ae4c8e9051 100644 --- a/python/gui/attributetable/qgsdualview.sip +++ b/python/gui/attributetable/qgsdualview.sip @@ -171,13 +171,6 @@ class QgsDualView : QStackedWidget :rtype: str %End - protected: - - void columnBoxInit(); -%Docstring - Initializes widgets which depend on the attributes of this layer -%End - public slots: void setCurrentEditSelection( const QgsFeatureIds &fids ); @@ -244,6 +237,9 @@ class QgsDualView : QStackedWidget \param mode new mode %End + protected: + virtual void hideEvent( QHideEvent *event ); + }; class QgsAttributeTableAction : QAction diff --git a/src/gui/attributetable/qgsdualview.cpp b/src/gui/attributetable/qgsdualview.cpp index 20ee31d2095..a729aa75e27 100644 --- a/src/gui/attributetable/qgsdualview.cpp +++ b/src/gui/attributetable/qgsdualview.cpp @@ -56,7 +56,6 @@ QgsDualView::QgsDualView( QWidget *parent ) mConditionalFormatWidget->hide(); - mPreviewActionMapper = new QSignalMapper( this ); mPreviewColumnsMenu = new QMenu( this ); mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu ); @@ -66,7 +65,6 @@ QgsDualView::QgsDualView( QWidget *parent ) // Connect layer list preview signals connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder ); - connect( mPreviewActionMapper, static_cast < void ( QSignalMapper::* )( QObject * ) > ( &QSignalMapper::mapped ), this, &QgsDualView::previewColumnChanged ); connect( mFeatureList, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged ); } @@ -149,16 +147,15 @@ void QgsDualView::columnBoxInit() if ( fieldIndex == -1 ) continue; - if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() ).type() != QLatin1String( "Hidden" ) ) + QString fieldName = field.name(); + if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName ).type() != QLatin1String( "Hidden" ) ) { QIcon icon = mLayer->fields().iconForField( fieldIndex ); - QString text = field.name(); + QString text = mLayer->attributeDisplayName( fieldIndex ); // Generate action for the preview popup button of the feature list QAction *previewAction = new QAction( icon, text, mFeatureListPreviewButton ); - mPreviewActionMapper->setMapping( previewAction, previewAction ); - connect( previewAction, &QAction::triggered, this, [previewAction, this]( bool ) { this->mPreviewActionMapper->map( previewAction ); } - ); + connect( previewAction, &QAction::triggered, this, [ = ] { previewColumnChanged( previewAction, fieldName ); } ); mPreviewColumnsMenu->addAction( previewAction ); if ( text == defaultField ) @@ -168,21 +165,26 @@ void QgsDualView::columnBoxInit() } } + QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this ); + connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression ); + mFeatureListPreviewButton->addAction( sortByPreviewExpression ); + + QAction *separator = new QAction(); + separator->setSeparator( true ); + mFeatureListPreviewButton->addAction( separator ); + restoreRecentDisplayExpressions(); + // If there is no single field found as preview if ( !mFeatureListPreviewButton->defaultAction() ) { mFeatureList->setDisplayExpression( displayExpression ); mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview ); - mDisplayExpression = mFeatureList->displayExpression(); + setDisplayExpression( mFeatureList->displayExpression() ); } else { mFeatureListPreviewButton->defaultAction()->trigger(); } - - QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this ); - connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression ); - mFeatureListPreviewButton->addAction( sortByPreviewExpression ); } void QgsDualView::setView( QgsDualView::ViewMode view ) @@ -314,6 +316,95 @@ void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest & mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel ); } +void QgsDualView::restoreRecentDisplayExpressions() +{ + const QVariantList previewExpressions = mLayer->customProperty( QStringLiteral( "dualview/previewExpressions" ) ).toList(); + + for ( const QVariant &previewExpression : previewExpressions ) + insertRecentlyUsedDisplayExpression( previewExpression.toString() ); +} + +void QgsDualView::saveRecentDisplayExpressions() const +{ + QList actions = mFeatureListPreviewButton->actions(); + + // Remove existing same action + int index = actions.indexOf( mLastDisplayExpressionAction ); + if ( index != -1 ) + { + QVariantList previewExpressions; + for ( ; index < actions.length(); ++index ) + { + QAction *action = actions.at( index ); + previewExpressions << action->property( "previewExpression" ); + } + + mLayer->setCustomProperty( QStringLiteral( "dualview/previewExpressions" ), previewExpressions ); + } +} + +void QgsDualView::setDisplayExpression( const QString &expression ) +{ + mDisplayExpression = expression; + insertRecentlyUsedDisplayExpression( expression ); +} + +void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression ) +{ + QList actions = mFeatureListPreviewButton->actions(); + + // Remove existing same action + int index = actions.indexOf( mLastDisplayExpressionAction ); + if ( index != -1 ) + { + for ( int i = 0; index + i < actions.length(); ++i ) + { + QAction *action = actions.at( index ); + if ( action->text() == expression || i >= 9 ) + { + if ( action == mLastDisplayExpressionAction ) + mLastDisplayExpressionAction = nullptr; + mFeatureListPreviewButton->removeAction( action ); + } + else + { + if ( !mLastDisplayExpressionAction ) + mLastDisplayExpressionAction = action; + } + } + } + + QString name = expression; + QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) ); + if ( expression.startsWith( QLatin1String( "COALESCE( \"" ) ) && expression.endsWith( QLatin1String( ", '' )" ) ) ) + { + name = expression.mid( 11, expression.length() - 24 ); // Numbers calculated from the COALESCE / parts + + int fieldIndex = mLayer->fields().indexOf( name ); + if ( fieldIndex != -1 ) + { + name = mLayer->attributeDisplayName( fieldIndex ); + icon = mLayer->fields().iconForField( fieldIndex ); + } + else + { + name = expression; + } + } + + QAction *previewAction = new QAction( icon, name, mFeatureListPreviewButton ); + previewAction->setProperty( "previewExpression", expression ); + connect( previewAction, &QAction::triggered, this, [expression, this]( bool ) + { + setDisplayExpression( expression ); + mFeatureListPreviewButton->setText( expression ); + } + ); + + mFeatureListPreviewButton->insertAction( mLastDisplayExpressionAction, previewAction ); + mLastDisplayExpressionAction = previewAction; +} + void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool &ok ) { if ( mLayer->isEditable() && !mAttributeForm->save() ) @@ -387,33 +478,27 @@ void QgsDualView::previewExpressionBuilder() mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup ); } - mDisplayExpression = mFeatureList->displayExpression(); + setDisplayExpression( mFeatureList->displayExpression() ); } -void QgsDualView::previewColumnChanged( QObject *action ) +void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &expression ) { - QAction *previewAction = qobject_cast< QAction * >( action ); - - if ( previewAction ) + if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '' )" ).arg( expression ) ) ) { - if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '' )" ).arg( previewAction->text() ) ) ) - { - QMessageBox::warning( this, - tr( "Could not set preview column" ), - tr( "Could not set column '%1' as preview column.\nParser error:\n%2" ) - .arg( previewAction->text(), mFeatureList->parserErrorString() ) - ); - } - else - { - mFeatureListPreviewButton->setDefaultAction( previewAction ); - mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup ); - } + QMessageBox::warning( this, + tr( "Could not set preview column" ), + tr( "Could not set column '%1' as preview column.\nParser error:\n%2" ) + .arg( previewAction->text(), mFeatureList->parserErrorString() ) + ); + } + else + { + mFeatureListPreviewButton->setText( previewAction->text() ); + mFeatureListPreviewButton->setIcon( previewAction->icon() ); + mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup ); } - mDisplayExpression = mFeatureList->displayExpression(); - - Q_ASSERT( previewAction ); + setDisplayExpression( mFeatureList->displayExpression() ); } int QgsDualView::featureCount() @@ -438,6 +523,12 @@ void QgsDualView::copyCellContent() const } } +void QgsDualView::hideEvent( QHideEvent *event ) +{ + Q_UNUSED( event ) + saveRecentDisplayExpressions(); +} + void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex ) { if ( !menu ) diff --git a/src/gui/attributetable/qgsdualview.h b/src/gui/attributetable/qgsdualview.h index 9b8b16d8c82..a64d9d70bf0 100644 --- a/src/gui/attributetable/qgsdualview.h +++ b/src/gui/attributetable/qgsdualview.h @@ -197,13 +197,6 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas */ QString sortExpression() const; - protected: - - /** - * Initializes widgets which depend on the attributes of this layer - */ - void columnBoxInit(); - public slots: /** @@ -265,6 +258,9 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas */ void formModeChanged( QgsAttributeForm::Mode mode ); + protected: + virtual void hideEvent( QHideEvent *event ) override; + private slots: void on_mFeatureList_aboutToChangeEditSelection( bool &ok ); @@ -278,7 +274,7 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas void previewExpressionBuilder(); - void previewColumnChanged( QObject *previewAction ); + void previewColumnChanged( QAction *previewAction, const QString &expression ); void viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex ); @@ -335,16 +331,26 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas void rebuildFullLayerCache(); private: + + /** + * Initializes widgets which depend on the attributes of this layer + */ + void columnBoxInit(); void initLayerCache( bool cacheGeometry ); void initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures ); + void restoreRecentDisplayExpressions(); + void saveRecentDisplayExpressions() const; + void setDisplayExpression( const QString &expression ); + void insertRecentlyUsedDisplayExpression( const QString &expression ); QgsAttributeEditorContext mEditorContext; QgsAttributeTableModel *mMasterModel = nullptr; QgsAttributeTableFilterModel *mFilterModel = nullptr; QgsFeatureListModel *mFeatureListModel = nullptr; QgsAttributeForm *mAttributeForm = nullptr; - QSignalMapper *mPreviewActionMapper = nullptr; QMenu *mPreviewColumnsMenu = nullptr; + QMenu *mPreviewActionMenu = nullptr; + QAction *mLastDisplayExpressionAction = nullptr; QMenu *mHorizontalHeaderMenu = nullptr; QgsVectorLayerCache *mLayerCache = nullptr; QgsVectorLayer *mLayer = nullptr; From 93ec7616e530970451bfcb0eaa17deb65807200c Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 5 Sep 2017 10:46:51 +0200 Subject: [PATCH 342/364] Make raster rendering a bit less verbose --- src/providers/gdal/qgsgdalprovider.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 99770ecf33c..f0a9b9fbe83 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -436,13 +436,13 @@ void QgsGdalProvider::readBlock( int bandNo, int xBlock, int yBlock, void *block void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pixelWidth, int pixelHeight, void *block, QgsRasterBlockFeedback *feedback ) { - QgsDebugMsg( "thePixelWidth = " + QString::number( pixelWidth ) ); - QgsDebugMsg( "thePixelHeight = " + QString::number( pixelHeight ) ); - QgsDebugMsg( "theExtent: " + extent.toString() ); + QgsDebugMsgLevel( "thePixelWidth = " + QString::number( pixelWidth ), 5 ); + QgsDebugMsgLevel( "thePixelHeight = " + QString::number( pixelHeight ), 5 ); + QgsDebugMsgLevel( "theExtent: " + extent.toString(), 5 ); for ( int i = 0 ; i < 6; i++ ) { - QgsDebugMsg( QString( "transform : %1" ).arg( mGeoTransform[i] ) ); + QgsDebugMsgLevel( QString( "transform : %1" ).arg( mGeoTransform[i] ), 5 ); } int dataSize = dataTypeSize( bandNo ); @@ -469,8 +469,8 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi QgsDebugMsg( "draw request outside view extent." ); return; } - QgsDebugMsg( "mExtent: " + mExtent.toString() ); - QgsDebugMsg( "myRasterExtent: " + myRasterExtent.toString() ); + QgsDebugMsgLevel( "mExtent: " + mExtent.toString(), 5 ); + QgsDebugMsgLevel( "myRasterExtent: " + myRasterExtent.toString(), 5 ); double xRes = extent.width() / pixelWidth; double yRes = extent.height() / pixelHeight; @@ -506,7 +506,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi int bottom = subRect.bottom(); int left = subRect.left(); int right = subRect.right(); - QgsDebugMsg( QString( "top = %1 bottom = %2 left = %3 right = %4" ).arg( top ).arg( bottom ).arg( left ).arg( right ) ); + QgsDebugMsgLevel( QString( "top = %1 bottom = %2 left = %3 right = %4" ).arg( top ).arg( bottom ).arg( left ).arg( right ), 5 ); // We want to avoid another resampling, so we read data approximately with @@ -517,7 +517,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi // Set readable names double srcXRes = mGeoTransform[1]; double srcYRes = mGeoTransform[5]; // may be negative? - QgsDebugMsg( QString( "xRes = %1 yRes = %2 srcXRes = %3 srcYRes = %4" ).arg( xRes ).arg( yRes ).arg( srcXRes ).arg( srcYRes ) ); + QgsDebugMsgLevel( QString( "xRes = %1 yRes = %2 srcXRes = %3 srcYRes = %4" ).arg( xRes ).arg( yRes ).arg( srcXRes ).arg( srcYRes ), 5 ); // target size in pizels int width = right - left + 1; @@ -573,12 +573,12 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi srcBottom = static_cast( std::floor( -1. * ( mExtent.yMaximum() - myRasterExtent.yMinimum() ) / srcYRes ) ); } - QgsDebugMsg( QString( "srcTop = %1 srcBottom = %2 srcLeft = %3 srcRight = %4" ).arg( srcTop ).arg( srcBottom ).arg( srcLeft ).arg( srcRight ) ); + QgsDebugMsgLevel( QString( "srcTop = %1 srcBottom = %2 srcLeft = %3 srcRight = %4" ).arg( srcTop ).arg( srcBottom ).arg( srcLeft ).arg( srcRight ), 5 ); int srcWidth = srcRight - srcLeft + 1; int srcHeight = srcBottom - srcTop + 1; - QgsDebugMsg( QString( "width = %1 height = %2 srcWidth = %3 srcHeight = %4" ).arg( width ).arg( height ).arg( srcWidth ).arg( srcHeight ) ); + QgsDebugMsgLevel( QString( "width = %1 height = %2 srcWidth = %3 srcHeight = %4" ).arg( width ).arg( height ).arg( srcWidth ).arg( srcHeight ), 5 ); int tmpWidth = srcWidth; int tmpHeight = srcHeight; @@ -594,13 +594,13 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &extent, int pi double tmpXMin = mExtent.xMinimum() + srcLeft * srcXRes; double tmpYMax = mExtent.yMaximum() + srcTop * srcYRes; - QgsDebugMsg( QString( "tmpXMin = %1 tmpYMax = %2 tmpWidth = %3 tmpHeight = %4" ).arg( tmpXMin ).arg( tmpYMax ).arg( tmpWidth ).arg( tmpHeight ) ); + QgsDebugMsgLevel( QString( "tmpXMin = %1 tmpYMax = %2 tmpWidth = %3 tmpHeight = %4" ).arg( tmpXMin ).arg( tmpYMax ).arg( tmpWidth ).arg( tmpHeight ), 5 ); // Allocate temporary block char *tmpBlock = ( char * )qgsMalloc( dataSize * tmpWidth * tmpHeight ); if ( ! tmpBlock ) { - QgsDebugMsg( QString( "Couldn't allocate temporary buffer of %1 bytes" ).arg( dataSize * tmpWidth * tmpHeight ) ); + QgsDebugMsgLevel( QString( "Couldn't allocate temporary buffer of %1 bytes" ).arg( dataSize * tmpWidth * tmpHeight ), 5 ); return; } GDALRasterBandH gdalBand = getBand( bandNo ); From 11240a3d72415665c7bc986424c4746f58ad4bdf Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 5 Sep 2017 11:03:23 +0200 Subject: [PATCH 343/364] Remove dead code --- src/core/qgsvectorlayer.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 9dfc060b0e7..b895a41532c 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1922,9 +1922,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte */ bool setDataProvider( QString const &provider ); - //! Goes through all features and finds a free id (e.g. to give it temporarily to a not-committed feature) - QgsFeatureId findFreeId(); - //! Read labeling from SLD void readSldLabeling( const QDomNode &node ); From ff2c109d3d658a5e17d09c65cac9fd72e410d0b5 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 5 Sep 2017 11:08:25 +0200 Subject: [PATCH 344/364] Make ogr a bit less verbose --- src/providers/ogr/qgsogrprovider.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index bbbdf7ed6f3..d2eea655d94 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -2913,8 +2913,6 @@ QGISEXTERN QList< QgsDataItemProvider * > *dataItemProviders() QgsCoordinateReferenceSystem QgsOgrProvider::crs() const { - QgsDebugMsg( "Entering." ); - QgsCoordinateReferenceSystem srs; if ( !mValid ) return srs; From 1d0de6028e09aa164e44bcd023832f6f6529e26f Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 5 Sep 2017 11:09:47 +0200 Subject: [PATCH 345/364] Remove generic debug noise --- src/core/qgsmaplayer.cpp | 2 -- src/core/qgsnetworkreplyparser.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index d950c5a2693..265970f322a 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -1516,8 +1516,6 @@ QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const QString QgsMapLayer::loadSldStyle( const QString &uri, bool &resultFlag ) { - QgsDebugMsg( "Entered." ); - resultFlag = false; QDomDocument myDocument; diff --git a/src/core/qgsnetworkreplyparser.cpp b/src/core/qgsnetworkreplyparser.cpp index 50ad568face..9db4d8da91c 100644 --- a/src/core/qgsnetworkreplyparser.cpp +++ b/src/core/qgsnetworkreplyparser.cpp @@ -29,7 +29,6 @@ QgsNetworkReplyParser::QgsNetworkReplyParser( QNetworkReply *reply ) : mReply( reply ) , mValid( false ) { - QgsDebugMsg( "Entered." ); if ( !mReply ) return; // Content type examples: From 2830f31e8b606288f6cb2359a62b4f71d11175c1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 6 Sep 2017 09:10:49 +1000 Subject: [PATCH 346/364] Disable Travis OSX builds They are broken and ignored at the moment, but still causing a massive backlog and delay in obtaining the valid test results --- .travis.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8bc77e4817b..de44156e280 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,18 +66,18 @@ matrix: - coreutils - libyaml-tiny-perl # OSX based build with QT4 and Python 2 - - os: osx - osx_image: xcode8.3 # MacOS 10.12: Sierra - cache: - pip: true - directories: - - $HOME/.ccache - env: - - TRAVIS_CONFIG=macos - - IGNORE_BUILD_FAILURES=YES - - allow_failures: - - os: osx +# - os: osx +# osx_image: xcode8.3 # MacOS 10.12: Sierra +# cache: +# pip: true +# directories: +# - $HOME/.ccache +# env: +# - TRAVIS_CONFIG=macos +# - IGNORE_BUILD_FAILURES=YES +# +# allow_failures: +# - os: osx git: depth: 30 From 1a961e804586aa4b3c45038a2e03b557f07d0298 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 13 Jun 2017 10:26:01 +1000 Subject: [PATCH 347/364] Template based referenced geometry class --- .../core/geometry/qgsreferencedgeometry.sip | 108 ++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/geometry/qgsreferencedgeometry.h | 107 +++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 python/core/geometry/qgsreferencedgeometry.sip create mode 100644 src/core/geometry/qgsreferencedgeometry.h diff --git a/python/core/geometry/qgsreferencedgeometry.sip b/python/core/geometry/qgsreferencedgeometry.sip new file mode 100644 index 00000000000..2839cee8af0 --- /dev/null +++ b/python/core/geometry/qgsreferencedgeometry.sip @@ -0,0 +1,108 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/geometry/qgsreferencedgeometry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +template +class QgsReferencedGeometryPrimitive +{ +%Docstring + A template based class for storing geometry primitives with an associated reference system. + + QgsReferencedGeometryPrimitive classes represent some form of geometry primitive + (such as rectangles) which have an optional coordinate reference system + associated with them. + +.. versionadded:: 3.0 +.. seealso:: QgsReferencedRectangle +.. note:: + + Not available in Python bindings (although SIP file is present for specific implementations). +%End + +%TypeHeaderCode +#include "qgsreferencedgeometry.h" +%End + public: + + QgsReferencedGeometryPrimitive( T primitive, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ); +%Docstring + Constructor for QgsReferencedGeometryPrimitive, for the specified ``primitive`` and ``crs``. +%End + + T primitive() const; +%Docstring + Returns the geometry primitive. + :rtype: T +%End + + T &primitive(); +%Docstring + Returns the geometry primitive. + :rtype: T +%End + + QgsCoordinateReferenceSystem crs() const; +%Docstring + Returns the associated coordinate reference system, or an invalid CRS if + no reference system is set. +.. seealso:: setCrs() + :rtype: QgsCoordinateReferenceSystem +%End + + void setCrs( const QgsCoordinateReferenceSystem &crs ); +%Docstring + Sets the associated ``crs``. Set to an invalid CRS if + no reference system is required. +.. seealso:: crs() +%End + +}; + + +typedef QgsReferencedGeometryPrimitive QgsReferencedGeometryPrimitiveQgsRectangleBase; + +class QgsReferencedRectangle : QgsReferencedGeometryPrimitiveQgsRectangleBase +{ +%Docstring + A QgsRectangle with associated coordinate reference system. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsreferencedgeometry.h" +typedef QgsReferencedGeometryPrimitive QgsReferencedGeometryPrimitiveQgsRectangleBase; +%End + public: + + QgsReferencedRectangle(); +%Docstring + Construct a default optional expression. + It will be disabled and with an empty expression. +%End + + QgsRectangle &rect(); +%Docstring + :rtype: QgsRectangle +%End + + +}; + + + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/geometry/qgsreferencedgeometry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8f424071d36..3826966c618 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1059,6 +1059,7 @@ SET(QGIS_CORE_HDRS geometry/qgsmultisurface.h geometry/qgspolygon.h geometry/qgsrectangle.h + geometry/qgsreferencedgeometry.h geometry/qgsregularpolygon.h geometry/qgstriangle.h geometry/qgssurface.h diff --git a/src/core/geometry/qgsreferencedgeometry.h b/src/core/geometry/qgsreferencedgeometry.h new file mode 100644 index 00000000000..b849ff7fb0e --- /dev/null +++ b/src/core/geometry/qgsreferencedgeometry.h @@ -0,0 +1,107 @@ +/*************************************************************************** + qgsreferencedgeometry.h + ---------------------- + begin : June 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSREFERENCEDGEOMETRY_H +#define QGSREFERENCEDGEOMETRY_H + +#include "qgis.h" +#include "qgis_sip.h" +#include "qgis_core.h" +#include "qgscoordinatereferencesystem.h" +#include "qgsrectangle.h" + +/** + * \class QgsReferencedGeometryPrimitive + * \ingroup core + * A template based class for storing geometry primitives with an associated reference system. + * + * QgsReferencedGeometryPrimitive classes represent some form of geometry primitive + * (such as rectangles) which have an optional coordinate reference system + * associated with them. + * + * \since QGIS 3.0 + * \see QgsReferencedRectangle + * \note Not available in Python bindings (although SIP file is present for specific implementations). + */ +template +class CORE_EXPORT QgsReferencedGeometryPrimitive +{ + public: + + /** + * Constructor for QgsReferencedGeometryPrimitive, for the specified \a primitive and \a crs. + */ + QgsReferencedGeometryPrimitive( T primitive, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ) + : mPrimitive( primitive ) + , mCrs( crs ) + {} + + /** + * Returns the geometry primitive. + */ + T primitive() const { return mPrimitive; } + + /** + * Returns the geometry primitive. + */ + T &primitive() { return mPrimitive; } + + /** + * Returns the associated coordinate reference system, or an invalid CRS if + * no reference system is set. + * \see setCrs() + */ + QgsCoordinateReferenceSystem crs() const { return mCrs; } + + /** + * Sets the associated \a crs. Set to an invalid CRS if + * no reference system is required. + * \see crs() + */ + void setCrs( const QgsCoordinateReferenceSystem &crs ) { mCrs = crs; } + + private: + + T mPrimitive; + QgsCoordinateReferenceSystem mCrs; + +}; + +/** + * A QgsRectangle with associated coordinate reference system. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsReferencedRectangle : public QgsReferencedGeometryPrimitive< QgsRectangle > +{ + public: + + /** + * Construct a default optional expression. + * It will be disabled and with an empty expression. + */ + QgsReferencedRectangle(); + + QgsRectangle &rect() { return primitive(); } + + +}; + +#endif // QGSREFERENCEDGEOMETRY_H + + + + From 8053b96ec07260f82bf49d833813648b4e8f1a84 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 13 Jun 2017 08:26:52 +0200 Subject: [PATCH 348/364] fix constructor and skip method with same python signature --- python/core/geometry/qgsreferencedgeometry.sip | 14 +++----------- src/core/geometry/qgsreferencedgeometry.h | 15 ++++++++------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/python/core/geometry/qgsreferencedgeometry.sip b/python/core/geometry/qgsreferencedgeometry.sip index 2839cee8af0..dd4c2ce91a6 100644 --- a/python/core/geometry/qgsreferencedgeometry.sip +++ b/python/core/geometry/qgsreferencedgeometry.sip @@ -36,11 +36,6 @@ class QgsReferencedGeometryPrimitive Constructor for QgsReferencedGeometryPrimitive, for the specified ``primitive`` and ``crs``. %End - T primitive() const; -%Docstring - Returns the geometry primitive. - :rtype: T -%End T &primitive(); %Docstring @@ -66,6 +61,7 @@ class QgsReferencedGeometryPrimitive }; + typedef QgsReferencedGeometryPrimitive QgsReferencedGeometryPrimitiveQgsRectangleBase; class QgsReferencedRectangle : QgsReferencedGeometryPrimitiveQgsRectangleBase @@ -81,7 +77,7 @@ typedef QgsReferencedGeometryPrimitive QgsReferencedGeometryPrimit %End public: - QgsReferencedRectangle(); + QgsReferencedRectangle( const QgsRectangle &rect ); %Docstring Construct a default optional expression. It will be disabled and with an empty expression. @@ -89,16 +85,12 @@ typedef QgsReferencedGeometryPrimitive QgsReferencedGeometryPrimit QgsRectangle &rect(); %Docstring + Returns the rectangles :rtype: QgsRectangle %End - }; - - - - /************************************************************************ * This file has been generated automatically from * * * diff --git a/src/core/geometry/qgsreferencedgeometry.h b/src/core/geometry/qgsreferencedgeometry.h index b849ff7fb0e..f7eeed57057 100644 --- a/src/core/geometry/qgsreferencedgeometry.h +++ b/src/core/geometry/qgsreferencedgeometry.h @@ -53,7 +53,7 @@ class CORE_EXPORT QgsReferencedGeometryPrimitive /** * Returns the geometry primitive. */ - T primitive() const { return mPrimitive; } + T primitive() const { return mPrimitive; } SIP_SKIP /** * Returns the geometry primitive. @@ -81,7 +81,10 @@ class CORE_EXPORT QgsReferencedGeometryPrimitive }; +//template class QgsReferencedGeometryPrimitive< QgsRectangle > QgsReferencedRectangle; + /** + * \ingroup core * A QgsRectangle with associated coordinate reference system. * \since QGIS 3.0 */ @@ -93,15 +96,13 @@ class CORE_EXPORT QgsReferencedRectangle : public QgsReferencedGeometryPrimitive * Construct a default optional expression. * It will be disabled and with an empty expression. */ - QgsReferencedRectangle(); + QgsReferencedRectangle( const QgsRectangle &rect ) : QgsReferencedGeometryPrimitive( rect ) {} + /** + * Returns the rectangles + */ QgsRectangle &rect() { return primitive(); } - }; #endif // QGSREFERENCEDGEOMETRY_H - - - - From e926f345e82260993126541c230ccc8e68e44dd3 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 31 Aug 2017 11:41:48 +1000 Subject: [PATCH 349/364] Update sip --- python/core/core_auto.sip | 1 + 1 file changed, 1 insertion(+) diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 37cfc8ee399..fb4d918a1d6 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -268,6 +268,7 @@ %Include geometry/qgsmultisurface.sip %Include geometry/qgspolygon.sip %Include geometry/qgsrectangle.sip +%Include geometry/qgsreferencedgeometry.sip %Include geometry/qgsregularpolygon.sip %Include geometry/qgstriangle.sip %Include geometry/qgssurface.sip From 1194b5abea74e419d068420666bb47a2568da8e6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 31 Aug 2017 13:53:00 +1000 Subject: [PATCH 350/364] Use inheritance rather than composition for QgsReferencedGeometries See https://github.com/qgis/QGIS/pull/4720#issuecomment-308652392 for discussion of the rationale --- python/core/geometry/qgsrectangle.sip | 2 + .../core/geometry/qgsreferencedgeometry.sip | 54 +++++++++---------- src/core/CMakeLists.txt | 1 + src/core/geometry/qgsrectangle.h | 5 ++ src/core/geometry/qgsreferencedgeometry.cpp | 32 +++++++++++ src/core/geometry/qgsreferencedgeometry.h | 54 +++++++++---------- src/core/qgspointxy.h | 6 ++- 7 files changed, 94 insertions(+), 60 deletions(-) create mode 100644 src/core/geometry/qgsreferencedgeometry.cpp diff --git a/python/core/geometry/qgsrectangle.sip b/python/core/geometry/qgsrectangle.sip index 94c133c54b9..936a0fb67f1 100644 --- a/python/core/geometry/qgsrectangle.sip +++ b/python/core/geometry/qgsrectangle.sip @@ -42,6 +42,8 @@ Construct a rectangle from a QRectF. The rectangle is normalized after construct Copy constructor %End + ~QgsRectangle(); + void set( const QgsPointXY &p1, const QgsPointXY &p2 ); %Docstring Sets the rectangle from two QgsPoints. The rectangle is diff --git a/python/core/geometry/qgsreferencedgeometry.sip b/python/core/geometry/qgsreferencedgeometry.sip index dd4c2ce91a6..68349918a40 100644 --- a/python/core/geometry/qgsreferencedgeometry.sip +++ b/python/core/geometry/qgsreferencedgeometry.sip @@ -9,21 +9,17 @@ -template -class QgsReferencedGeometryPrimitive +class QgsReferencedGeometryBase { %Docstring - A template based class for storing geometry primitives with an associated reference system. + A base class for geometry primitives which are stored with an associated reference system. - QgsReferencedGeometryPrimitive classes represent some form of geometry primitive + QgsReferencedGeometryBase classes represent some form of geometry primitive (such as rectangles) which have an optional coordinate reference system associated with them. .. versionadded:: 3.0 .. seealso:: QgsReferencedRectangle -.. note:: - - Not available in Python bindings (although SIP file is present for specific implementations). %End %TypeHeaderCode @@ -31,16 +27,9 @@ class QgsReferencedGeometryPrimitive %End public: - QgsReferencedGeometryPrimitive( T primitive, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ); + QgsReferencedGeometryBase( const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ); %Docstring - Constructor for QgsReferencedGeometryPrimitive, for the specified ``primitive`` and ``crs``. -%End - - - T &primitive(); -%Docstring - Returns the geometry primitive. - :rtype: T + Constructor for QgsReferencedGeometryBase, with the specified ``crs``. %End QgsCoordinateReferenceSystem crs() const; @@ -60,11 +49,7 @@ class QgsReferencedGeometryPrimitive }; - - -typedef QgsReferencedGeometryPrimitive QgsReferencedGeometryPrimitiveQgsRectangleBase; - -class QgsReferencedRectangle : QgsReferencedGeometryPrimitiveQgsRectangleBase +class QgsReferencedRectangle : QgsRectangle, QgsReferencedGeometryBase { %Docstring A QgsRectangle with associated coordinate reference system. @@ -73,20 +58,33 @@ class QgsReferencedRectangle : QgsReferencedGeometryPrimitiveQgsRectangleBase %TypeHeaderCode #include "qgsreferencedgeometry.h" -typedef QgsReferencedGeometryPrimitive QgsReferencedGeometryPrimitiveQgsRectangleBase; %End public: - QgsReferencedRectangle( const QgsRectangle &rect ); + QgsReferencedRectangle( const QgsRectangle &rectangle, const QgsCoordinateReferenceSystem &crs ); %Docstring - Construct a default optional expression. - It will be disabled and with an empty expression. + Constructor for QgsReferencedRectangle, with the specified initial ``rectangle`` + and ``crs``. %End - QgsRectangle &rect(); +}; + +class QgsReferencedPointXY : QgsPointXY, QgsReferencedGeometryBase +{ %Docstring - Returns the rectangles - :rtype: QgsRectangle + A QgsPointXY with associated coordinate reference system. +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsreferencedgeometry.h" +%End + public: + + QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs ); +%Docstring + Constructor for QgsReferencedPointXY, with the specified initial ``point`` + and ``crs``. %End }; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3826966c618..0f1b48f7b06 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -447,6 +447,7 @@ SET(QGIS_CORE_SRCS geometry/qgspoint.cpp geometry/qgspolygon.cpp geometry/qgsrectangle.cpp + geometry/qgsreferencedgeometry.cpp geometry/qgsregularpolygon.cpp geometry/qgstriangle.cpp geometry/qgswkbptr.cpp diff --git a/src/core/geometry/qgsrectangle.h b/src/core/geometry/qgsrectangle.h index 5a1c15a0fd6..c8da1353019 100644 --- a/src/core/geometry/qgsrectangle.h +++ b/src/core/geometry/qgsrectangle.h @@ -47,6 +47,11 @@ class CORE_EXPORT QgsRectangle //! Copy constructor QgsRectangle( const QgsRectangle &other ); + // IMPORTANT - while QgsRectangle is inherited by QgsReferencedRectangle, we do NOT want a virtual destructor here + // because this class MUST be lightweight and we don't want the cost of the vtable here. + // see https://github.com/qgis/QGIS/pull/4720#issuecomment-308652392 + ~QgsRectangle() = default; + /** * Sets the rectangle from two QgsPoints. The rectangle is * normalised after construction. diff --git a/src/core/geometry/qgsreferencedgeometry.cpp b/src/core/geometry/qgsreferencedgeometry.cpp new file mode 100644 index 00000000000..5e0ed48476e --- /dev/null +++ b/src/core/geometry/qgsreferencedgeometry.cpp @@ -0,0 +1,32 @@ +/*************************************************************************** + qgsreferencedgeometry.cpp + ------------------------ + begin : June 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsreferencedgeometry.h" + +QgsReferencedGeometryBase::QgsReferencedGeometryBase( const QgsCoordinateReferenceSystem &crs ) + : mCrs( crs ) +{} + +QgsReferencedRectangle::QgsReferencedRectangle( const QgsRectangle &rect, const QgsCoordinateReferenceSystem &crs ) + : QgsRectangle( rect ) + , QgsReferencedGeometryBase( crs ) +{} + +QgsReferencedPointXY::QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs ) + : QgsPointXY( point ) + , QgsReferencedGeometryBase( crs ) +{} diff --git a/src/core/geometry/qgsreferencedgeometry.h b/src/core/geometry/qgsreferencedgeometry.h index f7eeed57057..6d5c801b6b2 100644 --- a/src/core/geometry/qgsreferencedgeometry.h +++ b/src/core/geometry/qgsreferencedgeometry.h @@ -25,40 +25,25 @@ #include "qgsrectangle.h" /** - * \class QgsReferencedGeometryPrimitive + * \class QgsReferencedGeometryBase * \ingroup core - * A template based class for storing geometry primitives with an associated reference system. + * A base class for geometry primitives which are stored with an associated reference system. * - * QgsReferencedGeometryPrimitive classes represent some form of geometry primitive + * QgsReferencedGeometryBase classes represent some form of geometry primitive * (such as rectangles) which have an optional coordinate reference system * associated with them. * * \since QGIS 3.0 * \see QgsReferencedRectangle - * \note Not available in Python bindings (although SIP file is present for specific implementations). */ -template -class CORE_EXPORT QgsReferencedGeometryPrimitive +class CORE_EXPORT QgsReferencedGeometryBase { public: /** - * Constructor for QgsReferencedGeometryPrimitive, for the specified \a primitive and \a crs. + * Constructor for QgsReferencedGeometryBase, with the specified \a crs. */ - QgsReferencedGeometryPrimitive( T primitive, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ) - : mPrimitive( primitive ) - , mCrs( crs ) - {} - - /** - * Returns the geometry primitive. - */ - T primitive() const { return mPrimitive; } SIP_SKIP - - /** - * Returns the geometry primitive. - */ - T &primitive() { return mPrimitive; } + QgsReferencedGeometryBase( const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ); /** * Returns the associated coordinate reference system, or an invalid CRS if @@ -76,32 +61,41 @@ class CORE_EXPORT QgsReferencedGeometryPrimitive private: - T mPrimitive; QgsCoordinateReferenceSystem mCrs; }; -//template class QgsReferencedGeometryPrimitive< QgsRectangle > QgsReferencedRectangle; - /** * \ingroup core * A QgsRectangle with associated coordinate reference system. * \since QGIS 3.0 */ -class CORE_EXPORT QgsReferencedRectangle : public QgsReferencedGeometryPrimitive< QgsRectangle > +class CORE_EXPORT QgsReferencedRectangle : public QgsRectangle, public QgsReferencedGeometryBase { public: /** - * Construct a default optional expression. - * It will be disabled and with an empty expression. + * Constructor for QgsReferencedRectangle, with the specified initial \a rectangle + * and \a crs. */ - QgsReferencedRectangle( const QgsRectangle &rect ) : QgsReferencedGeometryPrimitive( rect ) {} + QgsReferencedRectangle( const QgsRectangle &rectangle, const QgsCoordinateReferenceSystem &crs ); + +}; + +/** + * \ingroup core + * A QgsPointXY with associated coordinate reference system. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsReferencedPointXY : public QgsPointXY, public QgsReferencedGeometryBase +{ + public: /** - * Returns the rectangles + * Constructor for QgsReferencedPointXY, with the specified initial \a point + * and \a crs. */ - QgsRectangle &rect() { return primitive(); } + QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs ); }; diff --git a/src/core/qgspointxy.h b/src/core/qgspointxy.h index c3651d5d10c..deb94a796e9 100644 --- a/src/core/qgspointxy.h +++ b/src/core/qgspointxy.h @@ -91,8 +91,10 @@ class CORE_EXPORT QgsPointXY */ QgsPointXY( const QgsPoint &point ); - ~QgsPointXY() - {} + // IMPORTANT - while QgsPointXY is inherited by QgsReferencedPointXY, we do NOT want a virtual destructor here + // because this class MUST be lightweight and we don't want the cost of the vtable here. + // see https://github.com/qgis/QGIS/pull/4720#issuecomment-308652392 + ~QgsPointXY() = default; /** Sets the x value of the point * \param x x coordinate From 6ab7ebadec558f10edb23acfe90c90080a306ad1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 31 Aug 2017 15:19:28 +1000 Subject: [PATCH 351/364] Metatype Qgs(Referenced)Rectangle/PointXY --- python/core/geometry/qgsrectangle.sip | 1 + python/core/geometry/qgsreferencedgeometry.sip | 6 ++++++ src/core/geometry/qgsrectangle.h | 2 ++ src/core/geometry/qgsreferencedgeometry.h | 8 ++++++++ src/core/qgspointxy.h | 1 + 5 files changed, 18 insertions(+) diff --git a/python/core/geometry/qgsrectangle.sip b/python/core/geometry/qgsrectangle.sip index 936a0fb67f1..5753fd1cd24 100644 --- a/python/core/geometry/qgsrectangle.sip +++ b/python/core/geometry/qgsrectangle.sip @@ -320,6 +320,7 @@ Copy constructor }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/core/geometry/qgsreferencedgeometry.sip b/python/core/geometry/qgsreferencedgeometry.sip index 68349918a40..17180a8ba6c 100644 --- a/python/core/geometry/qgsreferencedgeometry.sip +++ b/python/core/geometry/qgsreferencedgeometry.sip @@ -67,8 +67,11 @@ class QgsReferencedRectangle : QgsRectangle, QgsReferencedGeometryBase and ``crs``. %End + QgsReferencedRectangle(); + }; + class QgsReferencedPointXY : QgsPointXY, QgsReferencedGeometryBase { %Docstring @@ -87,8 +90,11 @@ class QgsReferencedPointXY : QgsPointXY, QgsReferencedGeometryBase and ``crs``. %End + QgsReferencedPointXY(); + }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/src/core/geometry/qgsrectangle.h b/src/core/geometry/qgsrectangle.h index c8da1353019..334ffcc0ee4 100644 --- a/src/core/geometry/qgsrectangle.h +++ b/src/core/geometry/qgsrectangle.h @@ -315,6 +315,8 @@ class CORE_EXPORT QgsRectangle }; +Q_DECLARE_METATYPE( QgsRectangle ) + #ifndef SIP_RUN /** diff --git a/src/core/geometry/qgsreferencedgeometry.h b/src/core/geometry/qgsreferencedgeometry.h index 6d5c801b6b2..d6d9274f87c 100644 --- a/src/core/geometry/qgsreferencedgeometry.h +++ b/src/core/geometry/qgsreferencedgeometry.h @@ -80,8 +80,12 @@ class CORE_EXPORT QgsReferencedRectangle : public QgsRectangle, public QgsRefere */ QgsReferencedRectangle( const QgsRectangle &rectangle, const QgsCoordinateReferenceSystem &crs ); + QgsReferencedRectangle(); + }; +Q_DECLARE_METATYPE( QgsReferencedRectangle ) + /** * \ingroup core * A QgsPointXY with associated coordinate reference system. @@ -97,6 +101,10 @@ class CORE_EXPORT QgsReferencedPointXY : public QgsPointXY, public QgsReferenced */ QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs ); + QgsReferencedPointXY(); + }; +Q_DECLARE_METATYPE( QgsReferencedPointXY ) + #endif // QGSREFERENCEDGEOMETRY_H diff --git a/src/core/qgspointxy.h b/src/core/qgspointxy.h index deb94a796e9..d9b919f84cd 100644 --- a/src/core/qgspointxy.h +++ b/src/core/qgspointxy.h @@ -311,6 +311,7 @@ class CORE_EXPORT QgsPointXY }; // class QgsPoint +Q_DECLARE_METATYPE( QgsPointXY ) inline bool operator==( const QgsPointXY &p1, const QgsPointXY &p2 ) SIP_SKIP { From 1e1ed8a4621115229218a6810fe36607bdcd4c85 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 31 Aug 2017 16:33:57 +1000 Subject: [PATCH 352/364] Add unit tests for referenced geometries --- src/core/geometry/qgsreferencedgeometry.h | 4 +- tests/src/python/CMakeLists.txt | 1 + .../src/python/test_qgsreferencedgeometry.py | 87 +++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 tests/src/python/test_qgsreferencedgeometry.py diff --git a/src/core/geometry/qgsreferencedgeometry.h b/src/core/geometry/qgsreferencedgeometry.h index d6d9274f87c..8ae882a4ecc 100644 --- a/src/core/geometry/qgsreferencedgeometry.h +++ b/src/core/geometry/qgsreferencedgeometry.h @@ -80,7 +80,7 @@ class CORE_EXPORT QgsReferencedRectangle : public QgsRectangle, public QgsRefere */ QgsReferencedRectangle( const QgsRectangle &rectangle, const QgsCoordinateReferenceSystem &crs ); - QgsReferencedRectangle(); + QgsReferencedRectangle() = default; }; @@ -101,7 +101,7 @@ class CORE_EXPORT QgsReferencedPointXY : public QgsPointXY, public QgsReferenced */ QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs ); - QgsReferencedPointXY(); + QgsReferencedPointXY() = default; }; diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index bbcee6788db..b91da6b66f6 100755 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -124,6 +124,7 @@ ADD_PYTHON_TEST(PyQgsRasterLayer test_qgsrasterlayer.py) ADD_PYTHON_TEST(PyQgsRasterColorRampShader test_qgsrastercolorrampshader.py) ADD_PYTHON_TEST(PyQgsRatioLockButton test_qgsratiolockbutton.py) ADD_PYTHON_TEST(PyQgsRectangle test_qgsrectangle.py) +ADD_PYTHON_TEST(PyQgsReferencedGeometry test_qgsreferencedgeometry.py) ADD_PYTHON_TEST(PyQgsRelation test_qgsrelation.py) ADD_PYTHON_TEST(PyQgsRelationManager test_qgsrelationmanager.py) ADD_PYTHON_TEST(PyQgsRenderContext test_qgsrendercontext.py) diff --git a/tests/src/python/test_qgsreferencedgeometry.py b/tests/src/python/test_qgsreferencedgeometry.py new file mode 100644 index 00000000000..164b6003587 --- /dev/null +++ b/tests/src/python/test_qgsreferencedgeometry.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsReferencedGeometry. + +.. note:: 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. +""" +__author__ = 'Nyall Dawson' +__date__ = '31/08/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import (QgsRectangle, + QgsPointXY, + QgsReferencedRectangle, + QgsReferencedPointXY, + QgsCoordinateReferenceSystem) +from qgis.PyQt.QtCore import QVariant +from qgis.testing import start_app, unittest +from utilities import compareWkt + +start_app() + + +class TestQgsReferencedGeometry(unittest.TestCase): + + def testRectangle(self): + rect = QgsReferencedRectangle(QgsRectangle(0.0, 1.0, 20.0, 10.0), QgsCoordinateReferenceSystem('epsg:3111')) + self.assertEqual(rect.xMinimum(), 0.0) + self.assertEqual(rect.yMinimum(), 1.0) + self.assertEqual(rect.xMaximum(), 20.0) + self.assertEqual(rect.yMaximum(), 10.0) + self.assertEqual(rect.crs().authid(), 'EPSG:3111') + + rect.setCrs(QgsCoordinateReferenceSystem('epsg:28356')) + self.assertEqual(rect.crs().authid(), 'EPSG:28356') + + # in variant + v = QVariant(QgsReferencedRectangle(QgsRectangle(1.0, 2.0, 3.0, 4.0), QgsCoordinateReferenceSystem('epsg:3111'))) + self.assertEqual(v.value().xMinimum(), 1.0) + self.assertEqual(v.value().yMinimum(), 2.0) + self.assertEqual(v.value().xMaximum(), 3.0) + self.assertEqual(v.value().yMaximum(), 4.0) + self.assertEqual(v.value().crs().authid(), 'EPSG:3111') + + # to rectangle + r = QgsRectangle(rect) + self.assertEqual(r.xMinimum(), 0.0) + self.assertEqual(r.yMinimum(), 1.0) + self.assertEqual(r.xMaximum(), 20.0) + self.assertEqual(r.yMaximum(), 10.0) + + # test that QgsReferencedRectangle IS a QgsRectangle + r2 = QgsRectangle(5, 6, 30, 40) + r2.combineExtentWith(rect) + self.assertEqual(r2.xMinimum(), 0.0) + self.assertEqual(r2.yMinimum(), 1.0) + self.assertEqual(r2.xMaximum(), 30.0) + self.assertEqual(r2.yMaximum(), 40.0) + + def testPoint(self): + point = QgsReferencedPointXY(QgsPointXY(1.0, 2.0), QgsCoordinateReferenceSystem('epsg:3111')) + self.assertEqual(point.x(), 1.0) + self.assertEqual(point.y(), 2.0) + self.assertEqual(point.crs().authid(), 'EPSG:3111') + + point.setCrs(QgsCoordinateReferenceSystem('epsg:28356')) + self.assertEqual(point.crs().authid(), 'EPSG:28356') + + # in variant + v = QVariant(QgsReferencedPointXY(QgsPointXY(3.0, 4.0), QgsCoordinateReferenceSystem('epsg:3111'))) + self.assertEqual(v.value().x(), 3.0) + self.assertEqual(v.value().y(), 4.0) + self.assertEqual(v.value().crs().authid(), 'EPSG:3111') + + # to QgsPointXY + p = QgsPointXY(point) + self.assertEqual(p.x(), 1.0) + self.assertEqual(p.y(), 2.0) + + +if __name__ == '__main__': + unittest.main() From f12bb74b584b7a36a0029a752e4889e6985945f5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 31 Aug 2017 19:55:41 +1000 Subject: [PATCH 353/364] Add missing docs --- python/core/geometry/qgsreferencedgeometry.sip | 6 ++++++ src/core/geometry/qgsreferencedgeometry.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/python/core/geometry/qgsreferencedgeometry.sip b/python/core/geometry/qgsreferencedgeometry.sip index 17180a8ba6c..e1505105689 100644 --- a/python/core/geometry/qgsreferencedgeometry.sip +++ b/python/core/geometry/qgsreferencedgeometry.sip @@ -68,6 +68,9 @@ class QgsReferencedRectangle : QgsRectangle, QgsReferencedGeometryBase %End QgsReferencedRectangle(); +%Docstring + Constructor for QgsReferencedRectangle. +%End }; @@ -91,6 +94,9 @@ class QgsReferencedPointXY : QgsPointXY, QgsReferencedGeometryBase %End QgsReferencedPointXY(); +%Docstring + Constructor for QgsReferencedPointXY. +%End }; diff --git a/src/core/geometry/qgsreferencedgeometry.h b/src/core/geometry/qgsreferencedgeometry.h index 8ae882a4ecc..2fce33771ba 100644 --- a/src/core/geometry/qgsreferencedgeometry.h +++ b/src/core/geometry/qgsreferencedgeometry.h @@ -80,6 +80,9 @@ class CORE_EXPORT QgsReferencedRectangle : public QgsRectangle, public QgsRefere */ QgsReferencedRectangle( const QgsRectangle &rectangle, const QgsCoordinateReferenceSystem &crs ); + /** + * Constructor for QgsReferencedRectangle. + */ QgsReferencedRectangle() = default; }; @@ -101,6 +104,9 @@ class CORE_EXPORT QgsReferencedPointXY : public QgsPointXY, public QgsReferenced */ QgsReferencedPointXY( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs ); + /** + * Constructor for QgsReferencedPointXY. + */ QgsReferencedPointXY() = default; }; From f1313af9142069d63fd3f74a6daca6179e560dc2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 31 Aug 2017 20:54:43 +1000 Subject: [PATCH 354/364] Add some more unit tests --- python/core/geometry/qgsrectangle.sip | 5 +++ .../core/geometry/qgsreferencedgeometry.sip | 10 +++++ python/core/qgspointxy.sip | 5 +++ src/core/geometry/qgsrectangle.h | 6 +++ src/core/geometry/qgsreferencedgeometry.h | 12 +++++ src/core/qgspointxy.h | 6 +++ tests/src/core/testqgspoint.cpp | 40 +++++++++++++++++ tests/src/core/testqgsrectangle.cpp | 44 +++++++++++++++++++ 8 files changed, 128 insertions(+) diff --git a/python/core/geometry/qgsrectangle.sip b/python/core/geometry/qgsrectangle.sip index 5753fd1cd24..90ad8022e61 100644 --- a/python/core/geometry/qgsrectangle.sip +++ b/python/core/geometry/qgsrectangle.sip @@ -317,6 +317,11 @@ Copy constructor :rtype: QgsBox3d %End + operator QVariant() const; +%Docstring +Allows direct construction of QVariants from rectangles. +%End + }; diff --git a/python/core/geometry/qgsreferencedgeometry.sip b/python/core/geometry/qgsreferencedgeometry.sip index e1505105689..9bc96028ba9 100644 --- a/python/core/geometry/qgsreferencedgeometry.sip +++ b/python/core/geometry/qgsreferencedgeometry.sip @@ -72,6 +72,11 @@ class QgsReferencedRectangle : QgsRectangle, QgsReferencedGeometryBase Constructor for QgsReferencedRectangle. %End + operator QVariant() const; +%Docstring +Allows direct construction of QVariants from rectangle. +%End + }; @@ -98,6 +103,11 @@ class QgsReferencedPointXY : QgsPointXY, QgsReferencedGeometryBase Constructor for QgsReferencedPointXY. %End + operator QVariant() const; +%Docstring +Allows direct construction of QVariants from point. +%End + }; diff --git a/python/core/qgspointxy.sip b/python/core/qgspointxy.sip index 4d14b3b3fd4..f2fe6c629c3 100644 --- a/python/core/qgspointxy.sip +++ b/python/core/qgspointxy.sip @@ -285,6 +285,11 @@ Divides the coordinates in this point by a scalar quantity in place :rtype: QgsPointXY %End + operator QVariant() const; +%Docstring +Allows direct construction of QVariants from points. +%End + SIP_PYOBJECT __repr__(); %MethodCode QString str = "(" + QString::number( sipCpp->x() ) + "," + QString::number( sipCpp->y() ) + ")"; diff --git a/src/core/geometry/qgsrectangle.h b/src/core/geometry/qgsrectangle.h index 334ffcc0ee4..57a41937469 100644 --- a/src/core/geometry/qgsrectangle.h +++ b/src/core/geometry/qgsrectangle.h @@ -306,6 +306,12 @@ class CORE_EXPORT QgsRectangle */ QgsBox3d toBox3d( double zMin, double zMax ) const; + //! Allows direct construction of QVariants from rectangles. + operator QVariant() const + { + return QVariant::fromValue( *this ); + } + private: double mXmin; diff --git a/src/core/geometry/qgsreferencedgeometry.h b/src/core/geometry/qgsreferencedgeometry.h index 2fce33771ba..71fbe73c5b0 100644 --- a/src/core/geometry/qgsreferencedgeometry.h +++ b/src/core/geometry/qgsreferencedgeometry.h @@ -85,6 +85,12 @@ class CORE_EXPORT QgsReferencedRectangle : public QgsRectangle, public QgsRefere */ QgsReferencedRectangle() = default; + //! Allows direct construction of QVariants from rectangle. + operator QVariant() const + { + return QVariant::fromValue( *this ); + } + }; Q_DECLARE_METATYPE( QgsReferencedRectangle ) @@ -109,6 +115,12 @@ class CORE_EXPORT QgsReferencedPointXY : public QgsPointXY, public QgsReferenced */ QgsReferencedPointXY() = default; + //! Allows direct construction of QVariants from point. + operator QVariant() const + { + return QVariant::fromValue( *this ); + } + }; Q_DECLARE_METATYPE( QgsReferencedPointXY ) diff --git a/src/core/qgspointxy.h b/src/core/qgspointxy.h index d9b919f84cd..2c4903dcbe2 100644 --- a/src/core/qgspointxy.h +++ b/src/core/qgspointxy.h @@ -262,6 +262,12 @@ class CORE_EXPORT QgsPointXY //! Divides the coordinates in this point by a scalar quantity in place QgsPointXY &operator/=( double scalar ) { mX /= scalar; mY /= scalar; return *this; } + //! Allows direct construction of QVariants from points. + operator QVariant() const + { + return QVariant::fromValue( *this ); + } + #ifdef SIP_RUN SIP_PYOBJECT __repr__(); % MethodCode diff --git a/tests/src/core/testqgspoint.cpp b/tests/src/core/testqgspoint.cpp index d3561451af7..726cfea7922 100644 --- a/tests/src/core/testqgspoint.cpp +++ b/tests/src/core/testqgspoint.cpp @@ -25,6 +25,7 @@ #include //header for class being tested #include +#include "qgsreferencedgeometry.h" class TestQgsPointXY: public QObject { @@ -51,6 +52,8 @@ class TestQgsPointXY: public QObject void compare(); void project(); void vector(); //tests for QgsVector + void asVariant(); + void referenced(); private: QgsPointXY mPoint1; @@ -760,5 +763,42 @@ void TestQgsPointXY::vector() QCOMPARE( v1.y(), 3.0 ); } +void TestQgsPointXY::asVariant() +{ + QgsPointXY p1 = QgsPointXY( 10.0, 20.0 ); + + //convert to and from a QVariant + QVariant var = QVariant::fromValue( p1 ); + QVERIFY( var.isValid() ); + QVERIFY( var.canConvert< QgsPointXY >() ); + QVERIFY( !var.canConvert< QgsReferencedPointXY >() ); + + QgsPointXY p2 = qvariant_cast( var ); + QCOMPARE( p2.x(), p1.x() ); + QCOMPARE( p2.y(), p1.y() ); +} + +void TestQgsPointXY::referenced() +{ + QgsReferencedPointXY p1 = QgsReferencedPointXY( QgsPointXY( 10.0, 20.0 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ); + QCOMPARE( p1.crs().authid(), QStringLiteral( "EPSG:3111" ) ); + p1.setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) ); + QCOMPARE( p1.crs().authid(), QStringLiteral( "EPSG:28356" ) ); + + //convert to and from a QVariant + QVariant var = QVariant::fromValue( p1 ); + QVERIFY( var.isValid() ); + + // not great - we'd ideally like this to pass, but it doesn't: + // QVERIFY( !var.canConvert< QgsPointXY >() ); + + QVERIFY( var.canConvert< QgsReferencedPointXY >() ); + + QgsReferencedPointXY p2 = qvariant_cast( var ); + QCOMPARE( p2.x(), p1.x() ); + QCOMPARE( p2.y(), p1.y() ); + QCOMPARE( p2.crs().authid(), QStringLiteral( "EPSG:28356" ) ); +} + QGSTEST_MAIN( TestQgsPointXY ) #include "testqgspoint.moc" diff --git a/tests/src/core/testqgsrectangle.cpp b/tests/src/core/testqgsrectangle.cpp index 6ac7a56c912..bc1bd62a4f5 100644 --- a/tests/src/core/testqgsrectangle.cpp +++ b/tests/src/core/testqgsrectangle.cpp @@ -19,6 +19,7 @@ #include #include #include "qgslogger.h" +#include "qgsreferencedgeometry.h" class TestQgsRectangle: public QObject { @@ -27,6 +28,8 @@ class TestQgsRectangle: public QObject void manipulate(); void regression6194(); void operators(); + void asVariant(); + void referenced(); }; void TestQgsRectangle::manipulate() @@ -111,5 +114,46 @@ void TestQgsRectangle::operators() QCOMPARE( rect2.width(), rect1.width() ); } +void TestQgsRectangle::asVariant() +{ + QgsRectangle rect1 = QgsRectangle( 10.0, 20.0, 110.0, 220.0 ); + + //convert to and from a QVariant + QVariant var = QVariant::fromValue( rect1 ); + QVERIFY( var.isValid() ); + QVERIFY( var.canConvert< QgsRectangle >() ); + QVERIFY( !var.canConvert< QgsReferencedRectangle >() ); + + QgsRectangle rect2 = qvariant_cast( var ); + QCOMPARE( rect2.xMinimum(), rect1.xMinimum() ); + QCOMPARE( rect2.yMinimum(), rect1.yMinimum() ); + QCOMPARE( rect2.height(), rect1.height() ); + QCOMPARE( rect2.width(), rect1.width() ); +} + +void TestQgsRectangle::referenced() +{ + QgsReferencedRectangle rect1 = QgsReferencedRectangle( QgsRectangle( 10.0, 20.0, 110.0, 220.0 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ); + QCOMPARE( rect1.crs().authid(), QStringLiteral( "EPSG:3111" ) ); + rect1.setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) ); + QCOMPARE( rect1.crs().authid(), QStringLiteral( "EPSG:28356" ) ); + + //convert to and from a QVariant + QVariant var = QVariant::fromValue( rect1 ); + QVERIFY( var.isValid() ); + + // not great - we'd ideally like this to pass, but it doesn't: + // QVERIFY( !var.canConvert< QgsRectangle >() ); + + QVERIFY( var.canConvert< QgsReferencedRectangle >() ); + + QgsReferencedRectangle rect2 = qvariant_cast( var ); + QCOMPARE( rect2.xMinimum(), rect1.xMinimum() ); + QCOMPARE( rect2.yMinimum(), rect1.yMinimum() ); + QCOMPARE( rect2.height(), rect1.height() ); + QCOMPARE( rect2.width(), rect1.width() ); + QCOMPARE( rect2.crs().authid(), QStringLiteral( "EPSG:28356" ) ); +} + QGSTEST_MAIN( TestQgsRectangle ) #include "testqgsrectangle.moc" From 25accbcbdb8e6d37c3cc9f6c45f1af47a4572315 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Tue, 29 Aug 2017 02:50:16 +0100 Subject: [PATCH 355/364] Fix OGC test getmap:each-format mode for 1bit/8bit/16bit --- src/server/services/wms/qgswmsutils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/services/wms/qgswmsutils.cpp b/src/server/services/wms/qgswmsutils.cpp index 17e6cce57ee..36573f74a3c 100644 --- a/src/server/services/wms/qgswmsutils.cpp +++ b/src/server/services/wms/qgswmsutils.cpp @@ -94,7 +94,7 @@ namespace QgsWms QRegularExpression::CaseInsensitiveOption ); QRegularExpressionMatch match = modeExpr.match( format ); - QString mode = match.captured(); + QString mode = match.captured( 1 ); if ( mode.compare( QLatin1String( "16bit" ), Qt::CaseInsensitive ) == 0 ) return PNG16; if ( mode.compare( QLatin1String( "8bit" ), Qt::CaseInsensitive ) == 0 ) From eb9e2ace7568c646d915ed3febb1d2da4e3e1424 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Mon, 4 Sep 2017 10:16:01 +0100 Subject: [PATCH 356/364] Add some tests --- tests/src/python/test_qgsserver.py | 2 +- tests/src/python/test_qgsserver_wms.py | 55 ++++++++++++++++++ .../WMS_GetMap_Mode_16bit.png | Bin 0 -> 1002198 bytes .../WMS_GetMap_Mode_1bit.png | Bin 0 -> 2503 bytes .../WMS_GetMap_Mode_8bit.png | Bin 0 -> 252024 bytes 5 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_16bit/WMS_GetMap_Mode_16bit.png create mode 100644 tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit.png create mode 100644 tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_8bit/WMS_GetMap_Mode_8bit.png diff --git a/tests/src/python/test_qgsserver.py b/tests/src/python/test_qgsserver.py index 5ee167951d6..de348820209 100644 --- a/tests/src/python/test_qgsserver.py +++ b/tests/src/python/test_qgsserver.py @@ -163,7 +163,7 @@ class QgsServerTestBase(unittest.TestCase): control.setRenderedImage(temp_image) if max_size_diff.isValid(): control.setSizeTolerance(max_size_diff.width(), max_size_diff.height()) - return control.compareImages(control_image), control.report() + return control.compareImages(control_image, max_diff), control.report() def _img_diff_error(self, response, headers, image, max_diff=10, max_size_diff=QSize()): self.assertEqual( diff --git a/tests/src/python/test_qgsserver_wms.py b/tests/src/python/test_qgsserver_wms.py index 34029fb65da..d6f9673dea9 100644 --- a/tests/src/python/test_qgsserver_wms.py +++ b/tests/src/python/test_qgsserver_wms.py @@ -176,6 +176,61 @@ class TestQgsServerWMS(QgsServerTestBase): for request in ('GetCapabilities',): self.wms_inspire_request_compare(request) + def test_wms_getmap_basic_mode(self): + # 1 bits + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "Country", + "STYLES": "", + "FORMAT": "image/png; mode=1bit", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857" + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetMap_Mode_1bit", 20000) + + # 8 bits + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "Country", + "STYLES": "", + "FORMAT": "image/png; mode=8bit", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857" + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetMap_Mode_8bit", 20000) + + # 16 bits + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "Country", + "STYLES": "", + "FORMAT": "image/png; mode=16bit", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857" + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetMap_Mode_16bit", 20000) + def test_wms_getmap_basic(self): qs = "?" + "&".join(["%s=%s" % i for i in list({ "MAP": urllib.parse.quote(self.projectPath), diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_16bit/WMS_GetMap_Mode_16bit.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_16bit/WMS_GetMap_Mode_16bit.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f166337d377349303dcf24c3644f8470351333 GIT binary patch literal 1002198 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 NjK1&R`y=1+{{za<9gqM3 literal 0 HcmV?d00001 diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit.png new file mode 100644 index 0000000000000000000000000000000000000000..4231097d84261ad3637062d9fd65345d2d0734db GIT binary patch literal 2503 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 Date: Wed, 6 Sep 2017 09:15:31 +0200 Subject: [PATCH 357/364] [travis] Workaround timeouts --- .ci/travis/linux/script.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.ci/travis/linux/script.sh b/.ci/travis/linux/script.sh index 7e79d8c5ae9..066d093f090 100755 --- a/.ci/travis/linux/script.sh +++ b/.ci/travis/linux/script.sh @@ -29,16 +29,13 @@ export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i -k" -# This works around an issue where travis would timeout on master because +# This works around an issue where travis would timeout because # when make is run inside ctest no output is generated. At the current time -# nobody know why, but at least this workaround gets travis results for master +# nobody know why, but at least this workaround gets travis results # back. Better approaches VERY welcome. -if [[ ${TRAVIS_PULL_REQUEST} == "false" ]]; -then - pushd build - $CTEST_BUILD_COMMAND - popd -fi +pushd build +$CTEST_BUILD_COMMAND +popd python ${TRAVIS_BUILD_DIR}/.ci/travis/scripts/ctest2travis.py \ xvfb-run ctest -V -E "$(cat ${DIR}/blacklist.txt | sed -r '/^(#.*?)?$/d' | paste -sd '|' -)" -S ${DIR}/../travis.ctest --output-on-failure From a09839081c0393e87e6baf7fe74e15a70a5eb521 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 6 Sep 2017 09:17:31 +0200 Subject: [PATCH 358/364] [travis] Fold build output --- .ci/travis/linux/script.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/travis/linux/script.sh b/.ci/travis/linux/script.sh index 066d093f090..63f8c42170b 100755 --- a/.ci/travis/linux/script.sh +++ b/.ci/travis/linux/script.sh @@ -34,7 +34,9 @@ export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i -k" # nobody know why, but at least this workaround gets travis results # back. Better approaches VERY welcome. pushd build +echo "travis_fold:start:qgis_build" $CTEST_BUILD_COMMAND +echo "travis_fold:end:qgis_build" popd python ${TRAVIS_BUILD_DIR}/.ci/travis/scripts/ctest2travis.py \ From b3c15900c1270bc1d49ef9b253bdb35b300ecc7b Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 6 Sep 2017 10:14:33 +0200 Subject: [PATCH 359/364] [travis] Stop building on errors --- .ci/travis/linux/script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/travis/linux/script.sh b/.ci/travis/linux/script.sh index 63f8c42170b..a78997d565a 100755 --- a/.ci/travis/linux/script.sh +++ b/.ci/travis/linux/script.sh @@ -27,7 +27,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export OTB_APPLICATION_PATH=${HOME}/OTB-5.6.0-Linux64/lib/otb/applications export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so -export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i -k" +export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i" # This works around an issue where travis would timeout because # when make is run inside ctest no output is generated. At the current time From ee75d8cd14ddbf4539bf07f9be297d583cc1f359 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 6 Sep 2017 10:46:22 +0200 Subject: [PATCH 360/364] Fix build with Qt 5.5 --- src/gui/attributetable/qgsdualview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/attributetable/qgsdualview.cpp b/src/gui/attributetable/qgsdualview.cpp index a729aa75e27..9c0c1b35e87 100644 --- a/src/gui/attributetable/qgsdualview.cpp +++ b/src/gui/attributetable/qgsdualview.cpp @@ -169,7 +169,7 @@ void QgsDualView::columnBoxInit() connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression ); mFeatureListPreviewButton->addAction( sortByPreviewExpression ); - QAction *separator = new QAction(); + QAction *separator = new QAction( mFeatureListPreviewButton ); separator->setSeparator( true ); mFeatureListPreviewButton->addAction( separator ); restoreRecentDisplayExpressions(); From 6f9be4de0f1e5eae1df9bdfa95c4afd6f6e4865f Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 6 Sep 2017 10:46:47 +0200 Subject: [PATCH 361/364] [travis] Fail fast on error --- .ci/travis/linux/script.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/travis/linux/script.sh b/.ci/travis/linux/script.sh index a78997d565a..ee2026243c9 100755 --- a/.ci/travis/linux/script.sh +++ b/.ci/travis/linux/script.sh @@ -13,6 +13,8 @@ # # ########################################################################### +set -e + export PYTHONPATH=${HOME}/osgeo4travis/lib/python3.3/site-packages/ export PATH=${HOME}/osgeo4travis/bin:${HOME}/osgeo4travis/sbin:${HOME}/OTB-5.6.0-Linux64/bin:${PATH} export LD_LIBRARY_PATH=${HOME}/osgeo4travis/lib From 02c43445ced3ef3fecb294f32d7d20840bcc7f54 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Wed, 6 Sep 2017 00:30:46 +0200 Subject: [PATCH 362/364] Set the most accurate User Manual page to Project Properties tabs Because some of the tabs in the dialog have a dedicated chapter where they are fully described, better use those links instead of the global one. --- src/app/qgsprojectproperties.cpp | 17 ++++++- src/ui/qgsprojectpropertiesbase.ui | 72 +++++++++++++++--------------- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index 1009c801401..c432ca807aa 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -1984,5 +1984,20 @@ void QgsProjectProperties::scaleItemChanged( QListWidgetItem *changedScaleItem ) void QgsProjectProperties::showHelp() { - QgsHelp::openHelp( QStringLiteral( "introduction/qgis_configuration.html#project-properties" ) ); + QWidget *activeTab = mOptionsStackedWidget->currentWidget(); + QString link = QStringLiteral( "introduction/qgis_configuration.html#project-properties" ); + + if ( activeTab == mTabRelations ) + { + link = QStringLiteral( "working_with_vector/attribute_table.html#creating-one-or-many-to-many-relations" ); + } + else if ( activeTab == mTab_Variables ) + { + link = QStringLiteral( "introduction/general_tools.html#variables" ); + } + else if ( activeTab == mProjOptsCRS ) + { + link = QStringLiteral( "working_with_projections/working_with_projections.html" ); + } + QgsHelp::openHelp( link ); } diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui index 2a42b4eb8ce..200433348b9 100755 --- a/src/ui/qgsprojectpropertiesbase.ui +++ b/src/ui/qgsprojectpropertiesbase.ui @@ -230,9 +230,9 @@ - 0 + 1 - + 0 @@ -259,8 +259,8 @@ 0 0 - 685 - 778 + 597 + 656 @@ -743,7 +743,7 @@ - + 0 @@ -770,8 +770,8 @@ 0 0 - 685 - 778 + 683 + 779 @@ -802,7 +802,7 @@ - + 0 @@ -829,8 +829,8 @@ 0 0 - 685 - 778 + 133 + 100 @@ -888,7 +888,7 @@ - + 0 @@ -915,8 +915,8 @@ 0 0 - 685 - 778 + 297 + 532 @@ -1311,7 +1311,7 @@ - + 0 @@ -1338,8 +1338,8 @@ 0 0 - 624 - 2451 + 667 + 2305 @@ -2391,7 +2391,7 @@ - + 0 @@ -2418,8 +2418,8 @@ 0 0 - 165 - 57 + 168 + 46 @@ -2563,9 +2563,9 @@ - QgsColorButton - QToolButton -
      qgscolorbutton.h
      + QgsScrollArea + QScrollArea +
      qgsscrollarea.h
      1
      @@ -2581,9 +2581,20 @@ 1 - QgsScrollArea - QScrollArea -
      qgsscrollarea.h
      + QgsFilterLineEdit + QLineEdit +
      qgsfilterlineedit.h
      +
      + + QgsColorButton + QToolButton +
      qgscolorbutton.h
      + 1 +
      + + QgsColorSchemeList + QWidget +
      qgscolorschemelist.h
      1
      @@ -2592,17 +2603,6 @@
      qgsopacitywidget.h
      1
      - - QgsFilterLineEdit - QLineEdit -
      qgsfilterlineedit.h
      -
      - - QgsColorSchemeList - QWidget -
      qgscolorschemelist.h
      - 1 -
      QgsCodeEditorPython QWidget From 59d758280e3b7ff2d638a7819aa79da0f28ce3a3 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Wed, 6 Sep 2017 10:59:54 +0200 Subject: [PATCH 363/364] Add help link for OWS Server tab --- src/app/qgsprojectproperties.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index c432ca807aa..dd09442188e 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -1999,5 +1999,9 @@ void QgsProjectProperties::showHelp() { link = QStringLiteral( "working_with_projections/working_with_projections.html" ); } + else if ( activeTab == mProjOptsOWS ) + { + link = QStringLiteral( "working_with_ogc/server/getting_started.html#prepare-a-project-to-serve" ); + } QgsHelp::openHelp( link ); } From a0e6b7fde03ed60907bba6fe1e41a82b9a1d5ce9 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Mon, 4 Sep 2017 14:26:29 +0200 Subject: [PATCH 364/364] Code cleanup Remove already declared groups in header --- src/gui/qgstextformatwidget.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/gui/qgstextformatwidget.cpp b/src/gui/qgstextformatwidget.cpp index 1efd8d30b4b..c61d64ac56c 100644 --- a/src/gui/qgstextformatwidget.cpp +++ b/src/gui/qgstextformatwidget.cpp @@ -31,12 +31,6 @@ QgsTextFormatWidget::QgsTextFormatWidget( const QgsTextFormat &format, QgsMapCanvas *mapCanvas, QWidget *parent ) : QWidget( parent ) - , mQuadrantBtnGrp( nullptr ) - , mDirectSymbBtnGrp( nullptr ) - , mUpsidedownBtnGrp( nullptr ) - , mPlacePointBtnGrp( nullptr ) - , mPlaceLineBtnGrp( nullptr ) - , mPlacePolygonBtnGrp( nullptr ) , mMinPixelLimit( 0 ) , mWidgetMode( Text ) , mMapCanvas( mapCanvas ) @@ -50,12 +44,6 @@ QgsTextFormatWidget::QgsTextFormatWidget( const QgsTextFormat &format, QgsMapCan QgsTextFormatWidget::QgsTextFormatWidget( QgsMapCanvas *mapCanvas, QWidget *parent, Mode mode ) : QWidget( parent ) - , mQuadrantBtnGrp( nullptr ) - , mDirectSymbBtnGrp( nullptr ) - , mUpsidedownBtnGrp( nullptr ) - , mPlacePointBtnGrp( nullptr ) - , mPlaceLineBtnGrp( nullptr ) - , mPlacePolygonBtnGrp( nullptr ) , mMinPixelLimit( 0 ) , mWidgetMode( mode ) , mMapCanvas( mapCanvas )
      " ) % tr( "Geometry" ) % QLatin1String( "" ) % typeString % QLatin1String( "