def testZeroArgFunctionsTakeNoArgs(self): QgsExpression.registerFunction(self.special) special = self.special self.assertEqual(special.name(), 'special') exp = QgsExpression('special()') result = exp.evaluate() self.assertEqual('test', result)
def testHandlesNull(self): context = QgsExpressionContext() QgsExpression.registerFunction(self.null_mean) exp = QgsExpression('null_mean(1, 2, NULL, 3)') result = exp.evaluate(context) self.assertFalse(exp.hasEvalError()) self.assertEqual(result, 2)
def getValue(self): fileName = unicode(self.leText.text()) context = self.expressionContext() exp = QgsExpression(fileName) if not exp.hasParserError(): result = exp.evaluate(context) if not exp.hasEvalError(): fileName = result if fileName.startswith("[") and fileName.endswith("]"): fileName = fileName[1:-1] if fileName.strip() in ["", self.SAVE_TO_TEMP_FILE, self.SAVE_TO_TEMP_LAYER]: if isinstance(self.output, OutputVector) and self.alg.provider.supportsNonFileBasedOutput(): # use memory layers for temporary files if supported value = "memory:" else: value = None elif fileName.startswith("memory:"): value = fileName elif fileName.startswith("postgis:"): value = fileName elif fileName.startswith("spatialite:"): value = fileName elif not os.path.isabs(fileName): value = ProcessingConfig.getSetting(ProcessingConfig.OUTPUT_FOLDER) + os.sep + fileName else: value = fileName return value
def _time_query_string(self, epoch, col, symbol="="): if self.timeLayer.getDateType() == time_util.DateTypes.IntegerTimestamps: return "{} {} {}".format(QgsExpression.quotedColumnRef(col), symbol, epoch) else: timeStr = time_util.epoch_to_str(epoch, self.timeLayer.getTimeFormat()) return "{} {} {}".format(QgsExpression.quotedColumnRef(col), symbol, QgsExpression.quotedString(timeStr))
def exp2func(expstr, name=None, mapLib=None): """ Convert a QgsExpression into a JS function. """ global whenfunctions whenfunctions = [] exp = QgsExpression(expstr) js = walkExpression(exp.rootNode(), mapLib=mapLib) if name is None: import random import string name = ''.join(random.choice(string.ascii_lowercase) for _ in range(4)) name += "_eval_expression" temp = """ function %s(context) { // %s var feature = context.feature; %s if (feature.properties) { return %s; } else { return %s; } }""" % (name, exp.dump(), "\n".join(whenfunctions), js, js.replace("feature.properties['", "feature['")) return temp, name, exp.dump()
def processAlgorithm(self, progress): layer = layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) expression = self.getParameterValue(self.EXPRESSION) qExp = QgsExpression(expression) if qExp.hasParserError(): raise GeoAlgorithmExecutionException(qExp.parserErrorString()) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( layer.fields(), layer.wkbType(), layer.crs()) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) count = layer.featureCount() step = 100.0 / count if count else 1 request = QgsFeatureRequest(qExp, context) for current, f in enumerate(layer.getFeatures(request)): writer.addFeature(f) progress.setPercentage(int(current * step)) del writer
def setSearchAndReplaceDictionaryFromLayer(self, layer, expression): ''' Set the search and replace dictionary from a given layer and an expression. The first found features is the data source ''' searchAndReplaceDictionary = {} # Get and validate expression qExp = QgsExpression( expression ) if not qExp.hasParserError(): qReq = QgsFeatureRequest( qExp ) features = layer.getFeatures( qReq ) else: QgsMessageLog.logMessage( 'An error occured while parsing the given expression: %s' % qExp.parserErrorString() ) features = layer.getFeatures() # Get layer fields name fields = layer.pendingFields() field_names = [ field.name() for field in fields ] # Take only first feature for feat in features: # Build dictionnary searchAndReplaceDictionary = dict(zip(field_names, feat.attributes())) break self.searchAndReplaceDictionary = searchAndReplaceDictionary
def getValue(self): fileName = unicode(self.leText.text()) context = self.expressionContext() exp = QgsExpression(fileName) if not exp.hasParserError(): result = exp.evaluate(context) if not exp.hasEvalError(): fileName = result if fileName.startswith("[") and fileName.endswith("]"): fileName = fileName[1:-1] if fileName.strip() in ['', self.SAVE_TO_TEMP_FILE]: value = None elif fileName.startswith('memory:'): value = fileName elif fileName.startswith('postgis:'): value = fileName elif fileName.startswith('spatialite:'): value = fileName elif not os.path.isabs(fileName): value = ProcessingConfig.getSetting( ProcessingConfig.OUTPUT_FOLDER) + os.sep + fileName else: value = fileName return value
def mg_flow_trace_select_features(self, layer, elem_type): if self.index_action == '56': sql = "SELECT * FROM "+self.schema_name+".anl_flow_trace_"+elem_type+" ORDER BY "+elem_type+"_id" else: sql = "SELECT * FROM "+self.schema_name+".anl_flow_exit_"+elem_type+" ORDER BY "+elem_type+"_id" rows = self.controller.get_rows(sql) if rows: # Build an expression to select them aux = "\""+elem_type+"_id\" IN (" for elem in rows: aux += elem[0] + ", " aux = aux[:-2] + ")" # Get a featureIterator from this expression: expr = QgsExpression(aux) if expr.hasParserError(): message = "Expression Error: " + str(expr.parserErrorString()) self.controller.show_warning(message, context_name='ui_message') return it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result id_list = [i.id() for i in it] # Select features with these id's layer.setSelectedFeatures(id_list)
def generic_zoom(self, fieldname, combo, field_index=0): ''' Get selected element from the combo, and returns a feature request expression ''' # Get selected element from combo element = combo.currentText() if element.strip() == '': print "Any record selected" return None elem = combo.itemData(combo.currentIndex()) if not elem: # that means that user has edited manually the combo but the element # does not correspond to any combo element message = 'Element {} does not exist'.format(element) self.controller.show_warning(message) return None # Select this feature in order to copy to memory layer aux = fieldname+" = '"+str(elem[field_index])+"'" expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return return expr
def resolveValue(self, alg): if self.hidden: return if not bool(self.value): self.value = self._resolveTemporary(alg) else: exp = QgsExpression(self.value) if not exp.hasParserError(): value = exp.evaluate(_expressionContext(alg)) if not exp.hasEvalError(): self.value = value if ":" not in self.value: if not os.path.isabs(self.value): self.value = os.path.join(ProcessingConfig.getSetting(ProcessingConfig.OUTPUT_FOLDER), self.value) supported = self._supportedExtensions() if supported: idx = self.value.rfind('.') if idx == -1: self.value = self.value + '.' + self.getDefaultFileExtension() else: ext = self.value[idx + 1:] if ext not in supported: self.value = self.value + '.' + self.getDefaultFileExtension()
def testZeroArgFunctionsTakeNoArgs(self): QgsExpression.registerFunction(self.special) special = self.special self.assertEqual(special.name(), "special") exp = QgsExpression("special()") result = exp.evaluate() self.assertEqual("test", result)
def layer_value(feature, layer, defaultconfig): layername = defaultconfig['layer'] expression = defaultconfig['expression'] field = defaultconfig['field'] searchlayer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0] if feature.geometry(): rect = feature.geometry().boundingBox() if layer.geometryType() == QGis.Point: point = feature.geometry().asPoint() rect = QgsRectangle(point.x(), point.y(), point.x() + 10, point.y() + 10) else: rect.scale(20) rect = canvas.mapRenderer().mapToLayerCoordinates(layer, rect) rq = QgsFeatureRequest().setFilterRect(rect)\ .setFlags(QgsFeatureRequest.ExactIntersect) features = searchlayer.getFeatures(rq) else: features = searchlayer.getFeatures() exp = QgsExpression(expression) exp.prepare(searchlayer.pendingFields()) for f in features: if exp.evaluate(f): return f[field] raise DefaultError('No features found')
def add_flooded_field(self, shapefile_path): """Create the layer from the local shp adding the flooded field. .. versionadded:: 3.3 Use this method to add a calculated field to a shapefile. The shapefile should have a field called 'count' containing the number of flood reports for the field. The field values will be set to 0 if the count field is < 1, otherwise it will be set to 1. :param shapefile_path: Path to the shapefile that will have the flooded field added. :type shapefile_path: basestring :return: A vector layer with the flooded field added. :rtype: QgsVectorLayer """ layer = QgsVectorLayer( shapefile_path, self.tr('Jakarta Floods'), 'ogr') # Add a calculated field indicating if a poly is flooded or not # from PyQt4.QtCore import QVariant layer.startEditing() field = QgsField('flooded', QVariant.Int) layer.dataProvider().addAttributes([field]) layer.commitChanges() layer.startEditing() idx = layer.fieldNameIndex('flooded') expression = QgsExpression('state > 0') expression.prepare(layer.pendingFields()) for feature in layer.getFeatures(): feature[idx] = expression.evaluate(feature) layer.updateFeature(feature) layer.commitChanges() return layer
def prepareAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, 'INPUT', context) mapping = self.parameterAsFieldsMapping(parameters, self.FIELDS_MAPPING, context) self.fields = QgsFields() self.expressions = [] da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs()) da.setEllipsoid(context.project().ellipsoid()) # create an expression context using thread safe processing context self.expr_context = self.createExpressionContext(parameters, context, source) for field_def in mapping: self.fields.append(QgsField(name=field_def['name'], type=field_def['type'], typeName="", len=field_def.get('length', 0), prec=field_def.get('precision', 0))) expression = QgsExpression(field_def['expression']) expression.setGeomCalculator(da) expression.setDistanceUnits(context.project().distanceUnits()) expression.setAreaUnits(context.project().areaUnits()) if expression.hasParserError(): raise QgsProcessingException( self.tr(u'Parser error in expression "{}": {}') .format(str(expression.expression()), str(expression.parserErrorString()))) self.expressions.append(expression) return True
def _expressionValues(self, text): strings = self.dialog.getAvailableValuesOfType( [QgsProcessingParameterString, QgsProcessingParameterNumber], []) model_params = [(self.dialog.resolveValueDescription(s), s) for s in strings] variables = QgsExpression.referencedVariables(text) # replace description by parameter's name (diverging after model save) descriptions = QgsExpression.referencedVariables(text) for k, v in model_params: if k in descriptions: text = text.replace('[% @{} %]'.format(k), '[% @{} %]'.format(v.parameterName())) src = QgsProcessingModelChildParameterSource.fromExpressionText(text) # add parameters currently used by the expression expression_values = [] expression_values.append(src) for k, v in model_params: if k in variables: expression_values.append(v) return expression_values
def mg_flow_trace_select_features(self, layer, elem_type): sql = "SELECT * FROM "+self.schema_name+".temp_mincut_"+elem_type+" ORDER BY "+elem_type+"_id" rows = self.dao.get_rows(sql) self.dao.commit() # Build an expression to select them aux = "\""+elem_type+"_id\" IN (" for elem in rows: aux+= elem[0]+", " aux = aux[:-2]+")" # Get a featureIterator from this expression: expr = QgsExpression(aux) if expr.hasParserError(): self.showWarning("Expression Error: "+str(expr.parserErrorString())) return it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result id_list = [i.id() for i in it] # Select features with these id's layer.setSelectedFeatures(id_list)
def expression_iterator(self, layer, expression, geometryStorage): featReq = QgsFeatureRequest() expression = QgsExpression(expression) context = QgsExpressionContext() self.stopLoop = False i = 0 for f in layer.getFeatures(featReq): QCoreApplication.processEvents() if self.stopLoop: break self.recordingSearchProgress.emit(i) i += 1 context.setFeature(f) evaluated = unicode(expression.evaluate(context)) if expression.hasEvalError(): continue if f.geometry() is None or f.geometry().centroid() is None: continue centroid = f.geometry().centroid().asPoint() if geometryStorage == 'wkb': geom = binascii.b2a_hex(f.geometry().asWkb()) elif geometryStorage == 'wkt': geom = f.geometry().exportToWkt() elif geometryStorage == 'extent': geom = f.geometry().boundingBox().asWktPolygon() yield ( evaluated, centroid.x(), centroid.y(), geom )
def __ok(self, withVertex, withPoint): """ To apply the interpolation :param withVertex: if we want a new interpolated vertex :param withPoint: if we want a new interpolated point """ line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface) vertex_v2 = QgsPointV2() vertex_id = QgsVertexId() line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2, vertex_id, 0) x0 = line_v2.xAt(vertex_id.vertex-1) y0 = line_v2.yAt(vertex_id.vertex-1) d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y()) x1 = line_v2.xAt(vertex_id.vertex) y1 = line_v2.yAt(vertex_id.vertex) d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y()) z0 = line_v2.zAt(vertex_id.vertex-1) z1 = line_v2.zAt(vertex_id.vertex) z = old_div((d0*z1 + d1*z0), (d0 + d1)) vertex_v2.addZValue(round(z, 2)) if withPoint: pt_feat = QgsFeature(self.__layer.pendingFields()) pt_feat.setGeometry(QgsGeometry(vertex_v2)) for i in range(len(self.__layer.pendingFields())): # default = self.__layer.defaultValue(i, pt_feat) # if default is not None: # print(pt_feat.fields().at(i).name(), pt_feat.fields().at(i).defaultValueExpression(), default) # print(self.__layer.defaultValueExpression(i), self.__layer.expressionField(i)) e = QgsExpression(self.__layer.defaultValueExpression(i)) default = e.evaluate(pt_feat) pt_feat.setAttribute(i, default) if self.__layer.editFormConfig().suppress() == QgsEditFormConfig.SuppressOn: self.__layer.addFeature(pt_feat) else: self.__iface.openFeatureForm(self.__layer, pt_feat) if withVertex: line_v2.insertVertex(vertex_id, vertex_v2) self.__lastLayer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2)) found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) else: self.__selectedFeature = found_features[0] else: self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "No more feature selected"), level=QgsMessageBar.INFO) self.__iface.mapCanvas().refresh() self.__done() self.__findVertex = True
def showExpressionsBuilder(self): context = self.param.expressionContext() dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, 'generic', context) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): self.setValue(dlg.expressionText())
def testCanUnregisterFunction(self): QgsExpression.registerFunction(self.testfun) index = QgsExpression.functionIndex("testfun") self.assertTrue(not index == -1) error = QgsExpression.unregisterFunction("testfun") self.assertTrue(error) index = QgsExpression.functionIndex("testfun") self.assertTrue(index == -1)
def testCanUnregisterFunction(self): QgsExpression.registerFunction(self.testfun) index = QgsExpression.functionIndex('testfun') self.assertNotEqual(index, -1) error = QgsExpression.unregisterFunction('testfun') self.assertTrue(error) index = QgsExpression.functionIndex('testfun') self.assertEqual(index, -1)
def showExpressionsBuilder(self): context = self.alg.createExpressionContext({}, createContext()) dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, 'generic', context) dlg.setWindowTitle(self.tr('Expression based output')) if dlg.exec_() == QDialog.Accepted: expression = QgsExpression(dlg.expressionText()) self.leText.setText(expression.evaluate(context))
def populateByExpression(self, adding=False): """ Populates the panel using an expression """ context = dataobjects.createContext() expression_context = context.expressionContext() # use the first row parameter values as a preview during expression creation params = self.panel.parametersForRow(0, warnOnInvalid=False) alg_scope = QgsExpressionContextUtils.processingAlgorithmScope(self.panel.alg, params, context) # create explicit variables corresponding to every parameter for k, v in params.items(): alg_scope.setVariable(k, v, True) expression_context.appendScope(alg_scope) # mark the parameter variables as highlighted for discoverability highlighted_vars = expression_context.highlightedVariables() highlighted_vars.extend(list(params.keys())) expression_context.setHighlightedVariables(highlighted_vars) dlg = QgsExpressionBuilderDialog(layer=None, context=context.expressionContext()) if adding: dlg.setExpectedOutputFormat(self.tr('An array of values corresponding to each new row to add')) if not dlg.exec_(): return if adding: exp = QgsExpression(dlg.expressionText()) res = exp.evaluate(expression_context) if type(res) is not list: res = [res] first_row = self.panel.batchRowCount() if self.panel.batchRowCount() > 1 else 0 for row, value in enumerate(res): self.setRowValue(row + first_row, value, context) else: for row in range(self.panel.batchRowCount()): params = self.panel.parametersForRow(row, warnOnInvalid=False) # remove previous algorithm scope -- we need to rebuild this completely, using the # other parameter values from the current row expression_context.popScope() alg_scope = QgsExpressionContextUtils.processingAlgorithmScope(self.panel.alg, params, context) for k, v in params.items(): alg_scope.setVariable(k, v, True) expression_context.appendScope(alg_scope) # rebuild a new expression every time -- we don't want the expression compiler to replace # variables with precompiled values exp = QgsExpression(dlg.expressionText()) value = exp.evaluate(expression_context) self.setRowValue(row, value, context)
def urban_zoom(self): ''' Zoom to layer 'urban' filtering values of all combos ''' # Build expresion search aux = "" layer = self.layers['urban_propierties_layer'] fieldname = self.params['urban_propierties_field_pzone'] combo = self.dlg.urban_properties_zone text = utils_giswater.getSelectedItem(combo) if text != "null": if aux != "": aux+= " AND " aux+= fieldname+" = '"+str(text)+"'" fieldname = self.params['urban_propierties_field_block'] combo = self.dlg.urban_properties_block text = utils_giswater.getSelectedItem(combo) if text != "null": if aux != "": aux+= " AND " aux+= fieldname+" = '"+str(text)+"'" fieldname = self.params['urban_propierties_field_number'] combo = self.dlg.urban_properties_number text = utils_giswater.getSelectedItem(combo) if text != "null": if aux != "": aux+= " AND " aux+= fieldname+" = '"+str(text)+"'" # Build a list of feature id's from the expression and select them if aux != '': expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return it = layer.getFeatures(QgsFeatureRequest(expr)) ids = [i.id() for i in it] layer.setSelectedFeatures(ids) # Select all features else: layer.selectAll() # Copy selected features to memory layer self.urbanMemLayer = self.copy_selected(layer, self.urbanMemLayer, "Polygon") # Zoom to generated memory layer self.zoom_to_scale() # Load style if self.QML_URBAN is not None: self.load_style(self.urbanMemLayer, self.QML_URBAN) # Toggles 'Show feature count' self.show_feature_count()
def address_get_numbers(self): ''' Populate civic numbers depending on selected street. Available civic numbers are linked with self.street_field_code column code in self.portal_layer and self.street_layer ''' # get selected street selected = self.dlg.adress_street.currentText() if selected == '': print "Any record selected" return # get street code elem = self.dlg.adress_street.itemData(self.dlg.adress_street.currentIndex()) code = elem[0] # to know the index see the query that populate the combo records = [[-1, '']] # Set filter expression layer = self.layers['portal_layer'] idx_field_code = layer.fieldNameIndex(self.params['portal_field_code']) idx_field_number = layer.fieldNameIndex(self.params['portal_field_number']) aux = self.params['portal_field_code']+" = '"+str(code)+"'" # Check filter and existence of fields expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return if idx_field_code == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['portal_field_code'], layer.name(), self.setting_file, 'portal_field_code') self.controller.show_warning(message) return if idx_field_number == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['portal_field_number'], layer.name(), self.setting_file, 'portal_field_number') self.controller.show_warning(message) return # Get a featureIterator from an expression: # Get features from the iterator and do something it = layer.getFeatures(QgsFeatureRequest(expr)) for feature in it: attrs = feature.attributes() field_number = attrs[idx_field_number] if not type(field_number) is QPyNullVariant: elem = [code, field_number] records.append(elem) # Fill numbers combo records_sorted = sorted(records, key = operator.itemgetter(1)) self.dlg.adress_number.blockSignals(True) self.dlg.adress_number.clear() for record in records_sorted: self.dlg.adress_number.addItem(str(record[1]), record) self.dlg.adress_number.blockSignals(False)
def testAutoArgsAreExpanded(self): function = self.expandargs args = function.params() self.assertEqual(args, 3) values = [1, 2, 3] exp = QgsExpression("") result = function.func(values, None, exp, None) # Make sure there is no eval error self.assertEqual(exp.evalErrorString(), "") self.assertEqual(result, (1, 2, 3))
def testHelp(self): QgsExpression.registerFunction(self.help_with_variable) html = ('<h3>help_with_variable function</h3><br>' 'The help comes from a variable.') self.assertEqual(self.help_with_variable.helpText(), html) QgsExpression.registerFunction(self.help_with_docstring) html = ('<h3>help_with_docstring function</h3><br>' 'The help comes from the python docstring.') self.assertEqual(self.help_with_docstring.helpText(), html)
def __call__(self, features): func = self.filterfunc if not hasattr(self.filterfunc, '__call__'): exp = QgsExpression(self.filterfunc) fields = self.layer.pendingFields() exp.prepare(fields) func = exp.evaluate # Return a generator of features that match the given where check. for f in features: if func(f): yield f
def getStreetNumbers(self): ''' Populate civic numbers depending on selected street. Available civic numbers are linked with self.STREET_FIELD_CODE column code in self.PORTAL_LAYER and self.STREET_LAYER ''' # get selected street selected = self.dlg.cboStreet.currentText() if selected == '': return # get street code sel_street = self.dlg.cboStreet.itemData(self.dlg.cboStreet.currentIndex()) code = sel_street[2] # to know the index see the query that populate the combo records = [[-1, '']] # Set filter expression layer = self.portalLayer idx_field_code = layer.fieldNameIndex(self.PORTAL_FIELD_CODE) idx_field_number = layer.fieldNameIndex(self.PORTAL_FIELD_NUMBER) aux = self.PORTAL_FIELD_CODE+"='"+str(code)+"'" # Check filter and existence of fields expr = QgsExpression(aux) if expr.hasParserError(): self.iface.messageBar().pushMessage(expr.parserErrorString() + ": " + aux, self.app_name, QgsMessageBar.WARNING, 10) return if idx_field_code == -1: message = self.tr("Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'". format(self.PORTAL_FIELD_CODE, layer.name(), self.setting_file, 'PORTAL_FIELD_CODE')) self.iface.messageBar().pushMessage(message, '', QgsMessageBar.WARNING) return if idx_field_number == -1: message = self.tr("Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'". format(self.PORTAL_FIELD_NUMBER, layer.name(), self.setting_file, 'PORTAL_FIELD_NUMBER')) self.iface.messageBar().pushMessage(message, '', QgsMessageBar.WARNING) return # Get a featureIterator from an expression: # Get features from the iterator and do something it = layer.getFeatures(QgsFeatureRequest(expr)) for feature in it: attrs = feature.attributes() field_number = attrs[idx_field_number] if not type(field_number) is QPyNullVariant: elem = [code, field_number] records.append(elem) # Fill numbers combo records_sorted = sorted(records, key = operator.itemgetter(1)) self.dlg.cboNumber.blockSignals(True) self.dlg.cboNumber.clear() for record in records_sorted: self.dlg.cboNumber.addItem(record[1], record) self.dlg.cboNumber.blockSignals(False)
def testCanRegisterGeometryFunction(self): success = QgsExpression.registerFunction(self.geomtest) self.assertTrue(success)
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :type context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :type feedback: QgsProcessingFeedback, optional :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback :type raise_exceptions: boo, default to False :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) # Only feature based algs have sourceFlags try: if alg.sourceFlags() & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks: context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) except AttributeError: pass active_layer = parameters['INPUT'] # Run some checks and prepare the layer for in-place execution by: # - getting the active layer and checking that it is a vector # - making the layer editable if it was not already # - selecting all features if none was selected # - checking in-place support for the active layer/alg/parameters # If one of the check fails and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: if active_layer is None: raise QgsProcessingException(tr("There is not active layer.")) if not isinstance(active_layer, QgsVectorLayer): raise QgsProcessingException(tr("Active layer is not a vector layer.")) if not active_layer.isEditable(): if not active_layer.startEditing(): raise QgsProcessingException(tr("Active layer is not editable (and editing could not be turned on).")) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) except QgsProcessingException as e: if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {} if not active_layer.selectedFeatureIds(): active_layer.selectAll() # Make sure we are working on selected features only parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(active_layer.id(), True) parameters['OUTPUT'] = 'memory:' req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Start the execution # If anything goes wrong and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException(tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) field_idxs = range(len(active_layer.fields())) iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds()) iterator_req.setInvalidGeometryCheck(context.invalidGeometryCheck()) feature_iterator = active_layer.getFeatures(iterator_req) step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1 for current, f in enumerate(feature_iterator): feedback.setProgress(current * step) if feedback.isCanceled(): break # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle # There is no way to know if some features have been skipped # due to invalid geometries if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid: selected_ids = active_layer.selectedFeatureIds() else: selected_ids = [] results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle new_features = [] # Check if there are any skipped features if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid: missing_ids = list(set(selected_ids) - set(result_layer.allFeatureIds())) if missing_ids: for f in active_layer.getFeatures(QgsFeatureRequest(missing_ids)): if not f.geometry().isGeosValid(): new_features.append(f) active_layer.deleteFeatures(active_layer.selectedFeatureIds()) for f in result_layer.getFeatures(): new_features.extend(QgsVectorLayerUtils. makeFeaturesCompatible([f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {}
def testReferencedColumnsNoSet(self): QgsExpression.registerFunction(self.no_referenced_columns_set) exp = QgsExpression('no_referenced_columns_set()') self.assertEqual(exp.referencedColumns(), {QgsFeatureRequest.ALL_ATTRIBUTES})
def _remove_selected_from_relation(agg_id: int, src_primary_key: str, src_layer_name: str, rel_primary_key: str, rel_layer_name: str): # get project instance project = QgsProject.instance() # get layers needed src_layer = project.mapLayersByName(src_layer_name) # Control if layers exists if not len(src_layer): iface.messageBar().pushCritical( 'Véloroutes', 'La couche {} n\'a pas été trouvée'.format(src_layer_name)) return src_layer = src_layer[0] rel_layer = project.mapLayersByName(rel_layer_name) if not len(rel_layer): iface.messageBar().pushCritical( 'Véloroutes', 'La table {} n\'a pas été trouvée'.format(rel_layer_name)) return rel_layer = rel_layer[0] # count number of features selected from src_layer count = src_layer.selectedFeatureCount() if count < 1: iface.messageBar().pushCritical( 'Véloroutes', 'Vous devez sélectionner au moins un objet de la couche {}'.format( src_layer_name)) return # get list rel_layer couple_rel = [] for feat in rel_layer.getFeatures( QgsExpression.createFieldEqualityExpression( src_primary_key, agg_id)): couple_rel.append((feat[src_primary_key], feat[rel_primary_key])) # create list of couples between src_primary_key and rel_primary_key features = src_layer.getSelectedFeatures() couple_id = [] for feat in features: couple_id.append((agg_id, feat[rel_primary_key])) # test between two lists match_list = [] for item in couple_rel: if item in couple_id: match_list.append(item) # test if match_list is empty if len(match_list) < 1: iface.messageBar().pushInfo( 'Véloroutes', 'Pas d\'objet de la couche {} à supprimer'.format(rel_layer_name)) return rel_layer.startEditing() for feat in rel_layer.getFeatures( QgsExpression.createFieldEqualityExpression( src_primary_key, agg_id)): if (feat[src_primary_key], feat[rel_primary_key]) in match_list: rel_layer.deleteFeature(feat.id()) rel_layer.commitChanges() msg = "{} objet(s) ont été supprimées de la couche {}".format( len(match_list), rel_layer_name) iface.messageBar().pushInfo('Véloroutes', msg)
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 = [{
def prepareAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, 'INPUT', context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) mapping = self.parameterAsFieldsMapping(parameters, self.FIELDS_MAPPING, context) self.fields = QgsFields() self.expressions = [] da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) # create an expression context using thread safe processing context self.expr_context = self.createExpressionContext( parameters, context, source) for field_def in mapping: self.fields.append( QgsField(name=field_def['name'], type=field_def['type'], typeName="", len=field_def.get('length', 0), prec=field_def.get('precision', 0))) if field_def['expression']: expression = QgsExpression(field_def['expression']) expression.setGeomCalculator(da) expression.setDistanceUnits(context.project().distanceUnits()) expression.setAreaUnits(context.project().areaUnits()) if expression.hasParserError(): feedback.reportError( self.tr(u'Parser error in expression "{}": {}').format( expression.expression(), expression.parserErrorString())) return False self.expressions.append(expression) else: self.expressions.append(None) return True
def showExpressionsBuilder(self): context = createExpressionContext() dlg = QgsExpressionBuilderDialog(None, str(self.leText.text()), self, 'generic', context) context.popScope() values = self.modelParametersDialog.getAvailableValuesOfType( QgsProcessingParameterNumber, QgsProcessingOutputNumber) variables = {} for value in values: if isinstance(value, QgsProcessingModelAlgorithm.ChildParameterSource): if value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ModelParameter: name = value.parameterName() element = self.modelParametersDialog.model.parameterDefinition( name) desc = element.description() elif value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ChildOutput: name = "%s_%s" % (value.outputChildId(), value.outputName()) alg = self.modelParametersDialog.model.childAlgorithm( value.outputChildId()) out = alg.algorithm().outputDefinition(value.outputName()) desc = self.tr("Output '{0}' from algorithm '{1}'").format( out.description(), alg.description()) variables[name] = desc values = self.modelParametersDialog.getAvailableValuesOfType([ QgsProcessingParameterFeatureSource, QgsProcessingParameterRasterLayer ], [QgsProcessingOutputVectorLayer, QgsProcessingOutputRasterLayer]) for value in values: if isinstance(value, QgsProcessingModelAlgorithm.ChildParameterSource): if value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ModelParameter: name = value.parameterName() element = self.modelParametersDialog.model.parameterDefinition( name) desc = element.description() elif value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ChildOutput: name = "%s_%s" % (value.outputChildId(), value.outputName()) alg = self.modelParametersDialog.model.childAlgorithm( value.outputChildId()) out = alg.algorithm().outputDefinition(value.outputName()) desc = self.tr("Output '{0}' from algorithm '{1}'").format( out.description(), alg.description()) variables['%s_minx' % name] = self.tr("Minimum X of {0}").format(desc) variables['%s_miny' % name] = self.tr("Minimum Y of {0}").format(desc) variables['%s_maxx' % name] = self.tr("Maximum X of {0}").format(desc) variables['%s_maxy' % name] = self.tr("Maximum Y of {0}").format(desc) if isinstance(element, (QgsProcessingParameterRasterLayer, QgsProcessingOutputRasterLayer)): variables['%s_min' % name] = self.tr("Minimum value of {0}").format(desc) variables['%s_max' % name] = self.tr("Maximum value of {0}").format(desc) variables['%s_avg' % name] = self.tr("Mean value of {0}").format(desc) variables['%s_stddev' % name] = self.tr( "Standard deviation of {0}").format(desc) for variable, desc in variables.items(): dlg.expressionBuilder().registerItem("Modeler", variable, "@" + variable, desc, highlightedItem=True) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): self.setValue(dlg.expressionText())
def testCanReregisterAfterUnregister(self): QgsExpression.registerFunction(self.testfun) QgsExpression.unregisterFunction("testfun") success = QgsExpression.registerFunction(self.testfun) self.assertTrue(success)
def testCanEvaluateFunction(self): QgsExpression.registerFunction(self.testfun) exp = QgsExpression('testfun(1)') result = exp.evaluate() self.assertEqual('Testing_1', result)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) 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(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) expression.setGeomCalculator(da) expression.setDistanceUnits(context.project().distanceUnits()) expression.setAreaUnits(context.project().areaUnits()) 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)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) exp_context = self.createExpressionContext(parameters, context) if layer is not None: exp_context.appendScope( QgsExpressionContextUtils.layerScope(layer)) expression.prepare(exp_context) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 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 = expression.evaluate(exp_context) if expression.hasEvalError(): feedback.reportError(expression.evalErrorString()) else: 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)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): # Get variables from dialog source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) field_name = self.parameterAsString(parameters, self.FIELD, context) kneighbors = self.parameterAsInt(parameters, self.KNEIGHBORS, context) use_field = bool(field_name) field_index = -1 fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 20)) current = 0 # Get properties of the field the grouping is based on if use_field: field_index = source.fields().lookupField(field_name) if field_index >= 0: fields.append( source.fields()[field_index] ) # Add a field with the name of the grouping field # Initialize writer (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) success = False fid = 0 # Get unique values of grouping field unique_values = source.uniqueValues(field_index) total = 100.0 / float( source.featureCount() * len(unique_values)) for unique in unique_values: points = [] filter = QgsExpression.createFieldEqualityExpression( field_name, unique) request = QgsFeatureRequest().setFilterExpression(filter) request.setSubsetOfAttributes([]) # Get features with the grouping attribute equal to the current grouping value features = source.getFeatures(request) for in_feature in features: if feedback.isCanceled(): break # Add points or vertices of more complex geometry points.extend(extract_points(in_feature.geometry())) current += 1 feedback.setProgress(int(current * total)) # A minimum of 3 points is necessary to proceed if len(points) >= 3: out_feature = QgsFeature() the_hull = concave_hull(points, kneighbors) if the_hull: vertex = [ QgsPointXY(point[0], point[1]) for point in the_hull ] poly = QgsGeometry().fromPolygonXY([vertex]) out_feature.setGeometry(poly) # Give the polygon the same attribute as the point grouping attribute out_feature.setAttributes([fid, unique]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) success = True # at least one polygon created fid += 1 if not success: raise QgsProcessingException( 'No hulls could be created. Most likely there were not at least three unique points in any of the groups.' ) else: # Field parameter provided but can't read from it raise QgsProcessingException('Unable to find grouping field') else: # Not grouped by field # Initialize writer (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) points = [] request = QgsFeatureRequest() request.setSubsetOfAttributes([]) features = source.getFeatures(request) # Get all features total = 100.0 / source.featureCount() if source.featureCount( ) else 0 for in_feature in features: if feedback.isCanceled(): break # Add points or vertices of more complex geometry points.extend(extract_points(in_feature.geometry())) current += 1 feedback.setProgress(int(current * total)) # A minimum of 3 points is necessary to proceed if len(points) >= 3: out_feature = QgsFeature() the_hull = concave_hull(points, kneighbors) if the_hull: vertex = [ QgsPointXY(point[0], point[1]) for point in the_hull ] poly = QgsGeometry().fromPolygonXY([vertex]) out_feature.setGeometry(poly) out_feature.setAttributes([0]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: # the_hull returns None only when there are less than three points after cleaning raise QgsProcessingException( 'At least three unique points are required to create a concave hull.' ) else: raise QgsProcessingException( 'At least three points are required to create a concave hull.' ) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_LAYER)) 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()) exp = QgsExpression(formula) da = QgsDistanceArea() da.setSourceCrs(layer.crs().srsid()) da.setEllipsoidalMode( iface.mapCanvas().mapSettings().hasCrsTransformEnabled()) da.setEllipsoid(QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE)[0]) exp.setGeomCalculator(da) exp.setDistanceUnits(QgsProject.instance().distanceUnits()) exp.setAreaUnits(QgsProject.instance().areaUnits()) exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) if not exp.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: %s' % exp.evalErrorString())) outFeature = QgsFeature() outFeature.initAttributes(len(fields)) outFeature.setFields(fields) error = '' calculationSuccess = True features = vector.features(layer) total = 100.0 / len(features) rownum = 1 for current, f in enumerate(features): 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 else: outFeature.setGeometry(f.geometry()) for fld in f.fields(): outFeature[fld.name()] = f[fld.name()] outFeature[fieldName] = value writer.addFeature(outFeature) progress.setPercentage(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation ' 'string:\n%s' % error))
class GeometryByExpression(QgisFeatureBasedAlgorithm): OUTPUT_GEOMETRY = 'OUTPUT_GEOMETRY' WITH_Z = 'WITH_Z' WITH_M = 'WITH_M' EXPRESSION = 'EXPRESSION' def group(self): return self.tr('Vector geometry') def groupId(self): return 'vectorgeometry' def __init__(self): super().__init__() self.geometry_types = [self.tr('Polygon'), 'Line', 'Point'] def initParameters(self, config=None): self.addParameter( QgsProcessingParameterEnum(self.OUTPUT_GEOMETRY, self.tr('Output geometry type'), options=self.geometry_types, defaultValue=0)) self.addParameter( QgsProcessingParameterBoolean( self.WITH_Z, self.tr('Output geometry has z dimension'), defaultValue=False)) self.addParameter( QgsProcessingParameterBoolean( self.WITH_M, self.tr('Output geometry has m values'), defaultValue=False)) self.addParameter( QgsProcessingParameterExpression(self.EXPRESSION, self.tr("Geometry expression"), defaultValue='$geometry', parentLayerParameterName='INPUT')) def name(self): return 'geometrybyexpression' def displayName(self): return self.tr('Geometry by expression') def outputName(self): return self.tr('Modified geometry') def prepareAlgorithm(self, parameters, context, feedback): self.geometry_type = self.parameterAsEnum(parameters, self.OUTPUT_GEOMETRY, context) self.wkb_type = None if self.geometry_type == 0: self.wkb_type = QgsWkbTypes.Polygon elif self.geometry_type == 1: self.wkb_type = QgsWkbTypes.LineString else: self.wkb_type = QgsWkbTypes.Point if self.parameterAsBool(parameters, self.WITH_Z, context): self.wkb_type = QgsWkbTypes.addZ(self.wkb_type) if self.parameterAsBool(parameters, self.WITH_M, context): self.wkb_type = QgsWkbTypes.addM(self.wkb_type) self.expression = QgsExpression( self.parameterAsString(parameters, self.EXPRESSION, context)) if self.expression.hasParserError(): feedback.reportError(self.expression.parserErrorString()) return False self.expression_context = self.createExpressionContext( parameters, context) self.expression.prepare(self.expression_context) return True def outputWkbType(self, input_wkb_type): return self.wkb_type def processFeature(self, feature, context, feedback): self.expression_context.setFeature(feature) value = self.expression.evaluate(self.expression_context) if self.expression.hasEvalError(): raise QgsProcessingException( self.tr('Evaluation error: {0}').format( self.expression.evalErrorString())) if not value: feature.setGeometry(QgsGeometry()) else: if not isinstance(value, QgsGeometry): raise QgsProcessingException( self.tr('{} is not a geometry').format(value)) feature.setGeometry(value) return [feature]
joinObject = QgsVectorJoinInfo() joinObject.joinLayerId = csvfile.id() joinObject.joinFieldName = csvField joinObject.targetFieldName = shpField joinObject.memoryCache = True shp.addJoin(joinObject) shp.startEditing() #step 1 myField = QgsField( 'Hill%', QVariant.Double) shp.addAttribute( myField ) idx = shp.fieldNameIndex( 'Hill%' ) #step 2 e = QgsExpression( 'if("csv_pres_clinton" + "csv_pres_trump">0,("csv_pres_clinton"-"csv_pres_trump")/("csv_pres_clinton"+"csv_pres_trump"),0)' ) e.prepare( shp.pendingFields() ) for f in shp.getFeatures(): f[idx] = e.evaluate( f ) shp.updateFeature( f ) shp.commitChanges() shp.startEditing() #step 1 myField = QgsField( 'X', QVariant.Double) myField2 = QgsField( 'Y', QVariant.Double) shp.addAttribute( myField ) shp.addAttribute( myField2 ) idx = shp.fieldNameIndex( 'X' )
def testCantOverrideBuiltinsWithRegister(self): success = QgsExpression.registerFunction(self.sqrt) self.assertFalse(success)
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ sourceL = self.parameterAsSource(parameters, self.INPUT_L, context) sourceF = self.parameterAsMatrix(parameters, self.INPUT_F, context) filtro = self.parameterAsString(parameters, self.GROUP_BY, context) titolo = self.parameterAsString(parameters, self.INPUT_T, context) html = self.parameterAsFileOutput(parameters, self.OUTPUT, context) source_path = self.parameterAsString(parameters, self.INPUT_L, context) f_base = self.parameterAsString(parameters, self.INPUT_D, context) icona = self.parameterAsString(parameters, self.INPUT_I, context) fogliocss = self.parameterAsString(parameters, self.INPUT_S, context) rel_path = self.parameterAsBool(parameters, self.INPUT_ABS, context) #FASE #01 - cerco la path del progetto if QgsProject.instance().homePath(): path_proj = QgsProject.instance().homePath() #windowizzo la path quale che sia path_proj = str(Path(path_proj)) #rimuovo geopakage: se presente path_proj = path_proj.replace('geopackage:', '') #print('path proj ', path_proj) else: feedback.reportError( 'WARNING NO PROJECT PATH: the html file may not work correctly\n' ) #print('***** no project path') path_proj = '' #tolgo %20 e metto spazio path_proj = path_proj.replace('%20', ' ') #FASE #02 - cerco la path del file di input path_file = (self.parameterDefinition('INPUT_L').valueAsPythonString( parameters['INPUT_L'], context)) path_file = path_file[1:path_file.rfind('/') + 1] if 'memory' in path_file: file_mem = True path_file = '' #print('temporary file') else: file_mem = False #windowizzo la path quale che sia path_file = str(Path(path_file)) #print('file path ', path_file) #tolgo %20 e metto spazio path_file = path_file.replace('%20', ' ') #FASE #03 - scelgo la path da usare tra le due: prioritaria quella di progetto if path_proj: path_dir = path_proj if path_proj not in path_file and path_file != '': feedback.reportError('WARNING PATH FILE ' + path_file) feedback.reportError('OUTSIDE PROJECT PATH ' + path_proj) feedback.reportError('MOST LIKELY IT WON' 'T WORK' + '\n') elif path_file == '': feedback.reportError('WARNING TEMPORARY LAYER WITHOUT PATH\n') else: path_dir = path_file if path_dir: feedback.reportError( 'WARNING use the path of the input file ' + path_dir + '\n') else: feedback.reportError('WARNING TEMPORARY LAYER WITHOUT PATH\n') #FASE #04 - controllo se si sta salvando file con percorsi relativi nella cartella di progetto if path_dir not in str(Path(html)) and 'processing' not in str( Path(html)): #print('html ', str(Path(html))) feedback.reportError( 'WARNING HTML WITH RELATIVE PATH SAVED OUTSIDE THE PROJECT PATH DOES NOT WORK PROPERLY\n' ) if 'processing' in str(Path(html)): #print('html ', str(Path(html))) feedback.reportError( 'WARNING TEMPORARY HTML WORK PROPERLY ONLY WITH ABSOLUTE PATH\n' ) #FASE #05 - controllo se icona e css sono entro la cartella progetto if fogliocss and (path_dir not in fogliocss): feedback.reportError( 'WARNING css PATH OUTSIDE PROJECT PATH: the html file may not work correctly\n' ) if icona and path_dir not in icona: feedback.reportError( 'WARNING icon PATH OUTSIDE PROJECT PATH: the html file may not work correctly\n' ) #FASE #06 - aggiungo terminatore di percorso se non è un file temporaneo if path_dir != '': path_dir = path_dir + '\\' #print('Final path ', path_dir) #FASE #07 - modifica se csv in input if source_path.find(".csv"): source_path = 'file:///' + source_path[0:source_path.rfind('/') + 1] #FASE #08 recupero dimensioni foto e icona, titolo e riordino a causa di un bug wi, hi, wf, hf = f_base.split(';') titolo = titolo.replace('\"', '') intestazione = titolo.replace('"', '') intestazione = titolo.replace('\'', '') #riordino campi come da selezione per bug cleanlist = [] [cleanlist.append(x) for x in sourceF if x not in cleanlist] sourceF = cleanlist #FASE #09 - inizializzo variabile per barra % esecuzione script # Compute the number of steps to display within the progress bar and # get features from source total = 100.0 / sourceL.featureCount() if sourceL.featureCount() else 0 #FASE #10 - filtra dati se richiesto if len(filtro) > 0: request = QgsFeatureRequest(QgsExpression(filtro)) features = sourceL.getFeatures(request) else: features = sourceL.getFeatures() #FASE #11 - produco il file in uscita with open(html, 'w') as output_file: # write header line = '<html>\r' output_file.write(line) #FASE #11.01 - se richiesto inserisco foglio css if fogliocss: if not rel_path or 'processing' in html: fogliocss = 'file:///' + fogliocss else: fogliocss = str(Path(fogliocss)) fogliocss = fogliocss.replace(path_dir, '') line = '<head>\r<link rel="stylesheet" href="' + fogliocss + '">\r</head>' output_file.write(line) #FASE #11.02 - se richiesto inserisco icona e titolo if icona or titolo: line = '<div>' if icona: if not rel_path or 'processing' in html: icona = 'file:///' + icona else: icona = str(Path(icona)) icona = icona.replace(path_dir, '') line = '<img src="' + icona + '" style="width:' + wi + ';height:' + hi + ';">' output_file.write(line) line = '' if titolo: if icona: line = line + '<b>' + '  ' + titolo + '</b>' else: line = line + '<b>' + titolo + '</b>' output_file.write(line) line = '</div>' output_file.write(line) line = None #FASE #11.03 - compongo tabella line = '<table class="Table">\r<thead>\r<tr>\r' output_file.write(line) #titoli colonne line = ''.join(('<th style="width:auto">' + str(name) + '</<th>\r') for name in sourceF) + '</tr>\r' output_file.write(line) line = '</thead>\r<tbody>\r' output_file.write(line) #righe tabella for current, f in enumerate(features): line = '<tr>\r' output_file.write(line) for name in sourceF: #controllo se si tratta di una immagine try: img_type = f[name].split(".") img_type = img_type[len(img_type) - 1] #print('img ok ', name, f[name], img_type) except: img_type = '' #print('img no ', name, f[name], img_type) #se è un'immagine e/o ha un percorso if img_type in [ "JPEG", "jpeg", "JPG", "jpg", "PNG", "png" ]: #se non è un file temporaneo o non voglio riferimenti relativi if not rel_path or 'processing' in html: if file_mem: img_name = 'file:///' else: img_name = '' if path_dir not in str(Path(f[name])): img_name = img_name + path_dir img_name = img_name + f[name] else: #se voglio riferimenti relativi img_name = str(Path(f[name])) img_name = img_name.replace(path_dir, '') line = ''.join('<td><center><img src =' + "'" + img_name + "'" + 'alt=' + "'" + img_name + "'" + 'width="' + wf + '" height="' + hf + '"></center></td>\r') else: try: line = ''.join('<td>' + f[name].toString("dd.MM.yyyy") + '</td>\r') except: line = ''.join('<td>' + str(f[name]) + '</td>\r') output_file.write(line) line = '</tr>\r' output_file.write(line) # Update the progress bar feedback.setProgress(int(current * total)) line = '</tbody>\r</table>\r</html>' output_file.write(line) return {self.OUTPUT: html}
def processAlgorithm(self, 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) da = QgsDistanceArea() da.setSourceCrs(layer.crs()) da.setEllipsoid(QgsProject.instance().ellipsoid()) exp.setGeomCalculator(da) exp.setDistanceUnits(QgsProject.instance().distanceUnits()) exp.setAreaUnits(QgsProject.instance().areaUnits()) exp_context = QgsExpressionContext( QgsExpressionContextUtils.globalProjectLayerScopes(layer)) if not exp.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: {0}').format(exp.evalErrorString())) outFeature = QgsFeature() outFeature.initAttributes(len(fields)) outFeature.setFields(fields) error = '' calculationSuccess = True features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) rownum = 1 for current, f in enumerate(features): 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 else: outFeature.setGeometry(f.geometry()) for fld in f.fields(): outFeature[fld.name()] = f[fld.name()] outFeature[fieldName] = value writer.addFeature(outFeature) 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))
def testQgsExpressionRepr(self): e = QgsExpression('my expression') self.assertEqual(e.__repr__(), "<QgsExpression: 'my expression'>")
def testCantReregister(self): QgsExpression.registerFunction(self.testfun) success = QgsExpression.registerFunction(self.testfun) self.assertFalse(success)
def processAlgorithm(self, progress): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = dataobjects.getObjectFromUri(layer) fields = [] expressions = [] da = QgsDistanceArea() da.setSourceCrs(layer.crs().srsid()) da.setEllipsoidalMode( iface.mapCanvas().mapSettings().hasCrsTransformEnabled()) da.setEllipsoid(QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE)[0]) exp_context = layer.createExpressionContext() for field_def in mapping: fields.append( QgsField(name=field_def['name'], type=field_def['type'], len=field_def['length'], prec=field_def['precision'])) expression = QgsExpression(field_def['expression']) expression.setGeomCalculator(da) expression.setDistanceUnits(QgsProject.instance().distanceUnits()) expression.setAreaUnits(QgsProject.instance().areaUnits()) expression.prepare(exp_context) if expression.hasParserError(): raise GeoAlgorithmExecutionException( self.tr(u'Parser error in expression "{}": {}').format( unicode(expression.expression()), unicode(expression.parserErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs()) # Create output vector layer with new attributes error_exp = None inFeat = QgsFeature() outFeat = QgsFeature() features = vector.features(layer) total = 100.0 / len(features) for current, inFeat in enumerate(features): rownum = current + 1 geometry = inFeat.geometry() outFeat.setGeometry(geometry) attrs = [] for i in range(0, len(mapping)): field_def = mapping[i] expression = expressions[i] exp_context.setFeature(inFeat) exp_context.lastScope().setVariable("row_number", rownum) value = expression.evaluate(exp_context) if expression.hasEvalError(): error_exp = expression break attrs.append(value) outFeat.setAttributes(attrs) writer.addFeature(outFeat) progress.setPercentage(int(current * total)) del writer if error_exp is not None: raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}').format( unicode(error_exp.expression()), unicode(error_exp.parserErrorString())))
def processAlgorithm(self, parameters, context, feedback): 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() for layerIdx, layer in enumerate(layers): df.addSource('input{}'.format(layerIdx + 1), layer.id()) if query == '': raise QgsProcessingException( self. tr('Empty SQL. Please enter valid SQL expression and try again.' )) else: localContext = self.createExpressionContext(parameters, context) expandedQuery = QgsExpression.replaceExpressionText( query, localContext) df.setQuery(expandedQuery) if uid_field: df.setUid(uid_field) if geometry_type == 1: # no geometry df.setGeometryWkbType(QgsWkbTypes.NoGeometry) else: if geometry_field: df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) if geometry_crs.isValid(): df.setGeometrySrid(geometry_crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): raise QgsProcessingException( vLayer.dataProvider().error().message()) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) features = vLayer.getFeatures() total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break sink.addFeature(inFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testCanBeRegistered(self): QgsExpression.registerFunction(self.testfun) index = QgsExpression.functionIndex('testfun') self.assertNotEqual(index, -1)
def processAlgorithm(self, progress): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = dataobjects.getObjectFromUri(layer) provider = layer.dataProvider() fields = [] expressions = [] for field_def in mapping: fields.append(QgsField(name=field_def['name'], type=field_def['type'], len=field_def['length'], prec=field_def['precision'])) expression = QgsExpression(field_def['expression']) if expression.hasParserError(): raise GeoAlgorithmExecutionException( self.tr(u'Parser error in expression "{}": {}') .format(unicode(field_def['expression']), unicode(expression.parserErrorString()))) expression.prepare(provider.fields()) if expression.hasEvalError(): raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}') .format(unicode(field_def['expression']), unicode(expression.evalErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, provider.geometryType(), layer.crs()) # Create output vector layer with new attributes error = '' calculationSuccess = True inFeat = QgsFeature() outFeat = QgsFeature() features = vector.features(layer) count = len(features) for current, inFeat in enumerate(features): rownum = current + 1 outFeat.setGeometry(inFeat.geometry()) attrs = [] for i in xrange(0, len(mapping)): field_def = mapping[i] expression = expressions[i] expression.setCurrentRowNumber(rownum) value = expression.evaluate(inFeat) if expression.hasEvalError(): calculationSuccess = False error = expression.evalErrorString() break attrs.append(value) outFeat.setAttributes(attrs) writer.addFeature(outFeat) current += 1 progress.setPercentage(100 * current / float(count)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation' ' string:\n') + error)
def tearDown(self): QgsExpression.unregisterFunction('testfun')
def __ok(self, withVertex, withPoint): """ To realize the interpolation :param withVertex: if we want a new interpolated vertex :param withPoint: if we want a new interpolated point """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry(), self.__iface) vertex_v2 = QgsPointV2() vertex_id = QgsVertexId() line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2, vertex_id, 0) x0 = line_v2.xAt(vertex_id.vertex - 1) y0 = line_v2.yAt(vertex_id.vertex - 1) d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y()) x1 = line_v2.xAt(vertex_id.vertex) y1 = line_v2.yAt(vertex_id.vertex) d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y()) z0 = line_v2.zAt(vertex_id.vertex - 1) z1 = line_v2.zAt(vertex_id.vertex) vertex_v2.addZValue(old_div((d0 * z1 + d1 * z0), (d0 + d1))) if withPoint: pt_feat = QgsFeature(self.__layer.pendingFields()) pt_feat.setGeometry(QgsGeometry(vertex_v2)) for i in range(len(self.__layer.pendingFields())): # default = self.__layer.defaultValue(i, pt_feat) # if default is not None: # print(pt_feat.fields().at(i).name(), pt_feat.fields().at(i).defaultValueExpression(), default) # print(self.__layer.defaultValueExpression(i), self.__layer.expressionField(i)) e = QgsExpression(self.__layer.defaultValueExpression(i)) default = e.evaluate(pt_feat) pt_feat.setAttribute(i, default) if self.__layer.editFormConfig().suppress( ) == QgsEditFormConfig.SuppressOn: self.__layer.addFeature(pt_feat) else: self.__iface.openFeatureForm(self.__layer, pt_feat) if withVertex: line_v2.insertVertex(vertex_id, vertex_v2) self.__lastLayer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2)) found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) else: self.__selectedFeature = found_features[0] else: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "No more feature selected"), level=QgsMessageBar.INFO) self.__iface.mapCanvas().refresh() self.__done() self.__findVertex = True
def testCantOverrideBuiltinsWithUnregister(self): success = QgsExpression.unregisterFunction("sqrt") self.assertFalse(success)
def testLegendScopeVariables(self): layout = QgsLayout(QgsProject.instance()) layout.initializeDefaults() legend = QgsLayoutItemLegend(layout) legend.setTitle("Legend") layout.addLayoutItem(legend) legend.setColumnCount(2) legend.setWrapString('d') legend.setLegendFilterOutAtlas(True) expc = legend.createExpressionContext() exp1 = QgsExpression("@legend_title") self.assertEqual(exp1.evaluate(expc), "Legend") exp2 = QgsExpression("@legend_column_count") self.assertEqual(exp2.evaluate(expc), 2) exp3 = QgsExpression("@legend_wrap_string") self.assertEqual(exp3.evaluate(expc), 'd') exp4 = QgsExpression("@legend_split_layers") self.assertEqual(exp4.evaluate(expc), False) exp5 = QgsExpression("@legend_filter_out_atlas") self.assertEqual(exp5.evaluate(expc), True) map = QgsLayoutItemMap(layout) map.attemptSetSceneRect(QRectF(20, 20, 80, 80)) map.setFrameEnabled(True) map.setExtent( QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125)) layout.addLayoutItem(map) map.setScale(15000) legend.setLinkedMap(map) expc2 = legend.createExpressionContext() exp6 = QgsExpression("@map_scale") self.assertAlmostEqual(exp6.evaluate(expc2), 15000, 2)
def testReferencedColumnsSet(self): QgsExpression.registerFunction(self.referenced_columns_set) exp = QgsExpression('referenced_columns_set()') self.assertEqual(set(exp.referencedColumns()), set(['a', 'b']))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) expression = QgsExpression( self.parameterAsString(parameters, self.EXPRESSION, context)) if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) expressionContext = self.createExpressionContext( parameters, context, source) expression.prepare(expressionContext) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) total = 100.0 / source.featureCount() if source.featureCount() else 0 current_progress = 0 for current, f in enumerate(source.getFeatures()): if feedback.isCanceled(): break if not f.hasGeometry(): continue current_progress = total * current feedback.setProgress(current_progress) expressionContext.setFeature(f) value = expression.evaluate(expressionContext) if expression.hasEvalError(): feedback.pushInfo( self.tr('Evaluation error for feature ID {}: {}').format( f.id(), expression.evalErrorString())) continue fGeom = f.geometry() engine = QgsGeometry.createGeometryEngine(fGeom.constGet()) engine.prepareGeometry() bbox = fGeom.boundingBox() if strategy == 0: pointCount = int(value) else: pointCount = int(round(value * da.measureArea(fGeom))) if pointCount == 0: feedback.pushInfo( "Skip feature {} as number of points for it is 0.".format( f.id())) continue index = QgsSpatialIndex() points = dict() nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 feature_total = total / pointCount if pointCount else 1 random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) if engine.contains(geom.constGet()) and \ vector.checkMinDistance(p, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.addFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(current_progress + int(nPoints * feature_total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr('Could not generate requested number of random ' 'points. Maximum number of attempts exceeded.')) feedback.setProgress(100) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_LAYER)) 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) if output.value == '': ext = output.getDefaultFileExtension(self) output.value = system.getTempFilenameInTempFolder(output.name + '.' + ext) fields = layer.fields() if newField: fields.append(QgsField(fieldName, fieldType, '', width, precision)) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs()) exp = QgsExpression(formula) da = QgsDistanceArea() da.setSourceCrs(layer.crs().srsid()) da.setEllipsoidalMode( iface.mapCanvas().mapSettings().hasCrsTransformEnabled()) da.setEllipsoid(QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE)[0]) exp.setGeomCalculator(da) exp.setDistanceUnits(QgsProject.instance().distanceUnits()) exp.setAreaUnits(QgsProject.instance().areaUnits()) exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) if not exp.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: %s' % exp.evalErrorString())) # add layer to registry to fix https://issues.qgis.org/issues/17300 # it is necessary only for aggregate expressions that verify that layer # is registered removeRegistryAfterEvaluation = False if not QgsMapLayerRegistry.instance().mapLayer(layer.id()): removeRegistryAfterEvaluation = True QgsMapLayerRegistry.instance().addMapLayer(layer, addToLegend=False) outFeature = QgsFeature() outFeature.initAttributes(len(fields)) outFeature.setFields(fields) error = '' calculationSuccess = True features = vector.features(layer) total = 100.0 / len(features) if len(features) > 0 else 1 rownum = 1 for current, f in enumerate(features): 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 else: outFeature.setGeometry(f.geometry()) for fld in f.fields(): outFeature[fld.name()] = f[fld.name()] outFeature[fieldName] = value writer.addFeature(outFeature) progress.setPercentage(int(current * total)) del writer # remove from registry if added for expression requirement # see above comment about fix #17300 if removeRegistryAfterEvaluation: QgsMapLayerRegistry.instance().removeMapLayer(layer) if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation ' 'string:\n%s' % error))