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 processAlgorithm(self, parameters, context, feedback): 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(source.sourceCrs()) 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()) exp_context = self.createExpressionContext(parameters, context) if layer is not None: exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) 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 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 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 calculate( self, layer, fieldName, expression ): if ( layer.featureCount() == 0 ): self.msg.show( "[Info] * No existing features on layer " + layer.name() + " to calculate expression.", 'info', True ) return expression = QgsExpression( expression ) if expression.hasParserError(): self.msg.show( QApplication.translate( "AutoFields-FieldCalculator", "[Error] (Parsing) " ) + \ expression.parserErrorString(), 'critical' ) return context = QgsExpressionContext() context.appendScope( QgsExpressionContextUtils.globalScope() ) context.appendScope( QgsExpressionContextUtils.projectScope() ) context.appendScope( QgsExpressionContextUtils.layerScope( layer ) ) context.setFields( layer.fields() ) if expression.needsGeometry(): if self.iface: # This block was borrowed from QGIS/python/plugins/processing/algs/qgis/FieldsCalculator.py da = QgsDistanceArea() da.setSourceCrs( layer.crs().srsid() ) da.setEllipsoidalMode( self.iface.mapCanvas().mapSettings().hasCrsTransformEnabled() ) da.setEllipsoid( QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE )[0] ) expression.setGeomCalculator( da ) if QGis.QGIS_VERSION_INT >= 21400: # Methods added in QGIS 2.14 expression.setDistanceUnits( QgsProject.instance().distanceUnits() ) expression.setAreaUnits( QgsProject.instance().areaUnits() ) expression.prepare( context ) fieldIndex = layer.fieldNameIndex( fieldName ) if fieldIndex == -1: return field = layer.fields()[fieldIndex] dictResults = {} for feature in layer.getFeatures(): context.setFeature( feature ) result = expression.evaluate( context ) if expression.hasEvalError(): self.msg.show( QApplication.translate( "AutoFields-FieldCalculator", "[Error] (Evaluating) " ) + \ expression.evalErrorString(), 'critical' ) return dictResults[feature.id()] = { fieldIndex: field.convertCompatible( result ) } layer.dataProvider().changeAttributeValues( dictResults ) self.msg.show( "[Info] * An expression was calculated on existing features of layer " + layer.name() + ", field " + fieldName + ".", 'info', True )
def prepareAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, 'INPUT', context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, '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 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 = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) 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()) if expression.hasParserError(): raise GeoAlgorithmExecutionException( self.tr(u'Parser error in expression "{}": {}').format( str(field_def['expression']), str(expression.parserErrorString()))) expression.prepare(exp_context) if expression.hasEvalError(): raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}').format( str(field_def['expression']), str(expression.evalErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs()) # Create output vector layer with new attributes error = '' calculationSuccess = True 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(): calculationSuccess = False error = expression.evalErrorString() break attrs.append(value) outFeat.setAttributes(attrs) writer.addFeature(outFeat) progress.setPercentage(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation' ' string:\n') + error)
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.parameterAsBoolean(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, 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))
def processAlgorithm(self, context, feedback): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = QgsProcessingUtils.mapLayerFromString(layer, context) fields = QgsFields() expressions = [] da = QgsDistanceArea() da.setSourceCrs(layer.crs()) da.setEllipsoid(QgsProject.instance().ellipsoid()) exp_context = layer.createExpressionContext() for field_def in mapping: fields.append( QgsField(field_def['name'], field_def['type'], field_def['length'], 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( str(expression.expression()), str(expression.parserErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context) # Create output vector layer with new attributes error_exp = None inFeat = QgsFeature() outFeat = QgsFeature() features = QgsProcessingUtils.getFeatures(layer, context) count = QgsProcessingUtils.featureCount(layer, context) if count > 0: total = 100.0 / count 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) feedback.setProgress(int(current * total)) else: feedback.setProgress(100) del writer if error_exp is not None: raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}').format( str(error_exp.expression()), str(error_exp.parserErrorString())))
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): 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()) field_index = layer.fields().lookupField(field_name) if new_field or field_index < 0: res = layer.dataProvider().addAttributes([QgsField(field_name, field_type, '', width, precision)]) if not res: raise QgsProcessingException("The new field couldn't be created! We suggest you to create it using QGIS tools and then update its values with this algorithm.") layer.updateFields() field_index = layer.fields().lookupField(field_name) field = layer.fields()[field_index] exp_context = self.createExpressionContext(parameters, context) if layer is not None: exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(exp_context) features = layer.getFeatures() total = 100.0 / layer.featureCount() if layer.featureCount() else 0 dict_results = dict() 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: dict_results[f.id()] = {field_index: field.convertCompatible(value)} feedback.setProgress(int(current * total)) layer.dataProvider().changeAttributeValues(dict_results) return {self.OUTPUT: layer}
def get_feature_with_form_scope(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Get filtered features with a form scope In parameters: LAYER=wms-layer-name FILTER=An expression to filter layer FORM_FEATURE={"type": "Feature", "geometry": {}, "properties": {}} // optionals FIELDS=list of requested field separated by comma WITH_GEOMETRY=False """ layer_name = params.get('LAYER', '') if not layer_name: raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = find_vector_layer(layer_name, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'VirtualField': {} provided".format(layer_name), 400) # get filter exp_filter = params.get('FILTER', '') if not exp_filter: raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FILTER parameter is mandatory", 400) # get form feature form_feature = params.get('FORM_FEATURE', '') if not form_feature: raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE parameter is mandatory", 400) # Check features try: geojson = json.loads(form_feature) except Exception: QgsMessageLog.logMessage( "JSON loads form feature '{}' exception:\n{}".format(form_feature, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed".format(form_feature), 400) if not geojson or not isinstance(geojson, dict): raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed".format(form_feature), 400) if ('type' not in geojson) or geojson['type'] != 'Feature': raise ExpressionServiceError( "Bad request error", ("Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed: type not defined " "or not Feature.").format(form_feature), 400) # try to load form feature # read fields form_feature_fields = QgsJsonUtils.stringToFields( form_feature, QTextCodec.codecForName("UTF-8")) # read features form_feature_list = QgsJsonUtils.stringToFeatureList( form_feature, form_feature_fields, QTextCodec.codecForName("UTF-8")) # features not well formed if not form_feature_list: raise ExpressionServiceError( "Bad request error", ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n" "{}").format(form_feature), 400) if len(form_feature_list) != 1: raise ExpressionServiceError( "Bad request error", ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n" "{}").format(form_feature), 400) # Get the form feature form_feat = form_feature_list[0] # create expression context exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope(project)) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) exp_context.appendScope(QgsExpressionContextUtils.formScope(form_feat)) # create distance area context da = QgsDistanceArea() da.setSourceCrs(layer.crs(), project.transformContext()) da.setEllipsoid(project.ellipsoid()) # Get filter expression exp_f = QgsExpression(exp_filter) exp_f.setGeomCalculator(da) exp_f.setDistanceUnits(project.distanceUnits()) exp_f.setAreaUnits(project.areaUnits()) if exp_f.hasParserError(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'GetFeatureWithFormScope': Error \"{}\": {}".format( exp_filter, exp_f.parserErrorString()), 400) if not exp_f.isValid(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'GetFeatureWithFormScope': Expression not valid \"{}\"".format(exp_filter), 400) exp_f.prepare(exp_context) req = QgsFeatureRequest(exp_f, exp_context) # With geometry with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't'] if not with_geom: req.setFlags(QgsFeatureRequest.NoGeometry) # Fields pk_attributes = layer.primaryKeyAttributes() attribute_list = [i for i in pk_attributes] fields = layer.fields() r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f] for f in r_fields: attribute_list.append(fields.indexOf(f)) # response response.setStatusCode(200) response.setHeader("Content-Type", "application/json") response.write('{ "type": "FeatureCollection","features":[') response.flush() json_exporter = QgsJsonExporter(layer) if attribute_list: json_exporter.setAttributes(attribute_list) separator = '' for feat in layer.getFeatures(req): fid = layer_name + '.' + get_server_fid(feat, pk_attributes) response.write(separator + json_exporter.exportFeature(feat, {}, fid)) response.flush() separator = ',\n' response.write(']}') return
def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Get virtual fields for features In parameters: LAYER=wms-layer-name VIRTUALS={"key1": "first expression", "key2": "second expression"} // optionals FILTER=An expression to filter layer FIELDS=list of requested field separated by comma WITH_GEOMETRY=False """ layer_name = params.get('LAYER', '') if not layer_name: raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = find_vector_layer(layer_name, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'VirtualFields': {} provided".format(layer_name), 400) # get virtuals virtuals = params.get('VIRTUALS', '') if not virtuals: raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: VIRTUALS parameter is mandatory", 400) # try to load virtuals dict try: vir_json = json.loads(virtuals) except Exception: QgsMessageLog.logMessage( "JSON loads virtuals '{}' exception:\n{}".format(virtuals, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals), 400) if not isinstance(vir_json, dict): raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals), 400) # create expression context exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope(project)) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) # create distance area context da = QgsDistanceArea() da.setSourceCrs(layer.crs(), project.transformContext()) da.setEllipsoid(project.ellipsoid()) # parse virtuals exp_map = {} exp_parser_errors = [] for k, e in vir_json.items(): exp = QgsExpression(e) exp.setGeomCalculator(da) exp.setDistanceUnits(project.distanceUnits()) exp.setAreaUnits(project.areaUnits()) if exp.hasParserError(): exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString())) continue if not exp.isValid(): exp_parser_errors.append('Expression not valid "{}"'.format(e)) continue exp.prepare(exp_context) exp_map[k] = exp # expression parser errors found if exp_parser_errors: raise ExpressionServiceError( "Bad request error", "Invalid VIRTUALS for 'VirtualFields':\n{}".format('\n'.join(exp_parser_errors)), 400) req = QgsFeatureRequest() # get filter req_filter = params.get('FILTER', '') if req_filter: req_exp = QgsExpression(req_filter) req_exp.setGeomCalculator(da) req_exp.setDistanceUnits(project.distanceUnits()) req_exp.setAreaUnits(project.areaUnits()) if req_exp.hasParserError(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'VirtualFields' Error \"{}\": {}".format( req_filter, req_exp.parserErrorString()), 400) if not req_exp.isValid(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'VirtualFields' Expression not valid \"{}\"".format(req_filter), 400) req_exp.prepare(exp_context) req = QgsFeatureRequest(req_exp, exp_context) # With geometry with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't'] if not with_geom: req.setFlags(QgsFeatureRequest.NoGeometry) # Fields pk_attributes = layer.primaryKeyAttributes() attribute_list = [i for i in pk_attributes] fields = layer.fields() r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f] for f in r_fields: attribute_list.append(fields.indexOf(f)) # response response.setStatusCode(200) response.setHeader("Content-Type", "application/json") response.write('{ "type": "FeatureCollection","features":[') response.flush() json_exporter = QgsJsonExporter(layer) if attribute_list: json_exporter.setAttributes(attribute_list) separator = '' for feat in layer.getFeatures(req): fid = layer_name + '.' + get_server_fid(feat, pk_attributes) extra = {} # Update context exp_context.setFeature(feat) exp_context.setFields(feat.fields()) # Evaluate expressions for virtual fields errors = {} for k, exp in exp_map.items(): value = exp.evaluate(exp_context) if exp.hasEvalError(): extra[k] = None errors[k] = exp.evalErrorString() else: extra[k] = json.loads(QgsJsonUtils.encodeValue(value)) errors[k] = exp.expression() response.write(separator + json_exporter.exportFeature(feat, extra, fid)) response.flush() separator = ',\n' response.write(']}') return
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 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))
def expressionBasedUpdate( self, layer, dictProperties, featureId, index=None, value=None ): """ Defines the logic of the expression-based update to be applied. This SLOT listens to featureAdded, geometryChanged, and attributeValueChanged SIGNALS. """ # Check if AutoField is there, otherwise return fieldIndex = layer.fieldNameIndex( dictProperties['field'] ) if fieldIndex == -1: self.msg.show( QApplication.translate( "EventManager", "[Error] Updating AutoField " ) + \ dictProperties['field'] + \ QApplication.translate( "EventManager", " in layer " ) + \ layer.name() + QApplication.translate( "EventManager", " was NOT possible." ) + \ QApplication.translate( "EventManager", " Perhaps you just removed it but haven't saved the changes yet?" ), 'warning' ) return event = "" result = None expression = QgsExpression( dictProperties['expression'] ) if expression.hasParserError(): self.msg.show( QApplication.translate( "EventManager", "[Error] (Parsing) " ) + \ expression.parserErrorString(), 'critical' ) result = NULL # Avoid infinite recursion (changing the same attribute value infinitely). if not index is None: # Filters out the featureAdded SIGNAL if type( index ) == int: # Filters out the geometryChanged SIGNAL if index == fieldIndex: # This call comes from the same AutoField, so return return if self.afm.isFieldAnAutoField( layer, layer.fields()[index].name() ): # Call from AutoField, don't listen # This is to prevent corrupting the layerEditBuffer and being bitten by: # Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585 return #if type(value)==QPyNullVariant: # Vector layers with numeric field whose value for 1st feature is NULL # trigger an attributeValueChanged SIGNAL when start editing from the # attribute table window. We use this conditional to avoid such SIGNAL. # The ideal case is that such NULL valued SIGNAL shouldn't be emitted by QGIS. # return # While the previous block reduces the number of times attributeValueChanged # is called from the attribute table, it leads to a QGIS bug: # Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585 # I prefer the attributeValueChanged to be called multiple # times (inefficient) than to open the possibility to a bug. # As soon as QGIS bug #15272 is solved, the number of calls will be reduced! event = "attributeValueChanged" else: event = "geometryChanged" else: event = "featureAdded" feature = layer.getFeatures( QgsFeatureRequest( featureId ) ).next() if result is None: context = QgsExpressionContext() context.appendScope( QgsExpressionContextUtils.globalScope() ) context.appendScope( QgsExpressionContextUtils.projectScope() ) context.appendScope( QgsExpressionContextUtils.layerScope( layer ) ) context.setFields( feature.fields() ) context.setFeature( feature ) if expression.needsGeometry(): if self.iface: # This block was borrowed from QGIS/python/plugins/processing/algs/qgis/FieldsCalculator.py da = QgsDistanceArea() da.setSourceCrs( layer.crs().srsid() ) da.setEllipsoidalMode( self.iface.mapCanvas().mapSettings().hasCrsTransformEnabled() ) da.setEllipsoid( QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE )[0] ) expression.setGeomCalculator( da ) if QGis.QGIS_VERSION_INT >= 21400: # Methods added in QGIS 2.14 expression.setDistanceUnits( QgsProject.instance().distanceUnits() ) expression.setAreaUnits( QgsProject.instance().areaUnits() ) expression.prepare( context ) result = expression.evaluate( context ) if expression.hasEvalError(): self.msg.show( QApplication.translate( "EventManager", "[Error] (Evaluating) " ) + \ expression.evalErrorString(), 'critical' ) result = NULL field = layer.fields()[fieldIndex] res = field.convertCompatible( result ) # If result is None, res will be None, but even in that case, QGIS knows # what to do with it while saving, it seems it's treated as NULL. # TODO when bug #15311 is fixed, this block should work better #if dictProperties['expression'] in self.listProviderExpressions: # # Save directly to provider # layer.dataProvider().changeAttributeValues( { featureId : { fieldIndex : res } } ) #else: # Save to layer # layer.changeAttributeValue( featureId, fieldIndex, res ) # Workaround if event == 'featureAdded': # Save directly to the provider layer.dataProvider().changeAttributeValues( { featureId : { fieldIndex : res } } ) else: # Save to layer layer.changeAttributeValue( featureId, fieldIndex, res ) self.msg.show( "[Info] * AutoField's value updated to " + unicode(res) + \ ", (" + layer.name() + "." + dictProperties['field'] + ") by " + event +".", 'info', True )
def expressionBasedUpdate(self, layer, dictProperties, featureId, index=None, value=None): """ Defines the logic of the expression-based update to be applied. This SLOT listens to featureAdded, geometryChanged, and attributeValueChanged SIGNALS. """ # Check if AutoField is there, otherwise return fieldIndex = layer.fieldNameIndex(dictProperties['field']) if fieldIndex == -1: self.msg.show( QApplication.translate( "EventManager", "[Error] Updating AutoField " ) + \ dictProperties['field'] + \ QApplication.translate( "EventManager", " in layer " ) + \ layer.name() + QApplication.translate( "EventManager", " was NOT possible." ) + \ QApplication.translate( "EventManager", " Perhaps you just removed it but haven't saved the changes yet?" ), 'warning' ) return event = "" result = None expression = QgsExpression(dictProperties['expression']) if expression.hasParserError(): self.msg.show( QApplication.translate( "EventManager", "[Error] (Parsing) " ) + \ expression.parserErrorString(), 'critical' ) result = NULL # Avoid infinite recursion (changing the same attribute value infinitely). if not index is None: # Filters out the featureAdded SIGNAL if type(index) == int: # Filters out the geometryChanged SIGNAL if index == fieldIndex: # This call comes from the same AutoField, so return return if self.afm.isFieldAnAutoField( layer, layer.fields() [index].name()): # Call from AutoField, don't listen # This is to prevent corrupting the layerEditBuffer and being bitten by: # Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585 return #if type(value)==QPyNullVariant: # Vector layers with numeric field whose value for 1st feature is NULL # trigger an attributeValueChanged SIGNAL when start editing from the # attribute table window. We use this conditional to avoid such SIGNAL. # The ideal case is that such NULL valued SIGNAL shouldn't be emitted by QGIS. # return # While the previous block reduces the number of times attributeValueChanged # is called from the attribute table, it leads to a QGIS bug: # Fatal: ASSERT: "mChangedAttributeValues.isEmpty()" in file /tmp/buildd/qgis-2.14.2+20trusty/src/core/qgsvectorlayereditbuffer.cpp, line 585 # I prefer the attributeValueChanged to be called multiple # times (inefficient) than to open the possibility to a bug. # As soon as QGIS bug #15272 is solved, the number of calls will be reduced! event = "attributeValueChanged" else: event = "geometryChanged" else: event = "featureAdded" feature = layer.getFeatures(QgsFeatureRequest(featureId)).next() if result is None: context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) context.setFields(feature.fields()) context.setFeature(feature) if expression.needsGeometry(): if self.iface: # This block was borrowed from QGIS/python/plugins/processing/algs/qgis/FieldsCalculator.py da = QgsDistanceArea() da.setSourceCrs(layer.crs().srsid()) da.setEllipsoidalMode(self.iface.mapCanvas().mapSettings(). hasCrsTransformEnabled()) da.setEllipsoid(QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE)[0]) expression.setGeomCalculator(da) if QGis.QGIS_VERSION_INT >= 21400: # Methods added in QGIS 2.14 expression.setDistanceUnits( QgsProject.instance().distanceUnits()) expression.setAreaUnits( QgsProject.instance().areaUnits()) expression.prepare(context) result = expression.evaluate(context) if expression.hasEvalError(): self.msg.show( QApplication.translate( "EventManager", "[Error] (Evaluating) " ) + \ expression.evalErrorString(), 'critical' ) result = NULL field = layer.fields()[fieldIndex] res = field.convertCompatible(result) # If result is None, res will be None, but even in that case, QGIS knows # what to do with it while saving, it seems it's treated as NULL. # TODO when bug #15311 is fixed, this block should work better #if dictProperties['expression'] in self.listProviderExpressions: # # Save directly to provider # layer.dataProvider().changeAttributeValues( { featureId : { fieldIndex : res } } ) #else: # Save to layer # layer.changeAttributeValue( featureId, fieldIndex, res ) # Workaround if event == 'featureAdded': # Save directly to the provider layer.dataProvider().changeAttributeValues( {featureId: { fieldIndex: res }}) else: # Save to layer layer.changeAttributeValue(featureId, fieldIndex, res) self.msg.show( "[Info] * AutoField's value updated to " + unicode(res) + \ ", (" + layer.name() + "." + dictProperties['field'] + ") by " + event +".", 'info', True )
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))
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) 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 / layer.featureCount() if layer.featureCount() else 0 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, 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))
def processAlgorithm(self, parameters, context, feedback): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = QgsProcessingUtils.mapLayerFromString(layer, context) fields = QgsFields() expressions = [] da = QgsDistanceArea() da.setSourceCrs(layer.crs()) da.setEllipsoid(context.project().ellipsoid()) exp_context = layer.createExpressionContext() for field_def in mapping: fields.append(QgsField(field_def['name'], field_def['type'], field_def['length'], field_def['precision'])) expression = QgsExpression(field_def['expression']) expression.setGeomCalculator(da) expression.setDistanceUnits(context.project().distanceUnits()) expression.setAreaUnits(context.project().areaUnits()) expression.prepare(exp_context) if expression.hasParserError(): raise GeoAlgorithmExecutionException( self.tr(u'Parser error in expression "{}": {}') .format(str(expression.expression()), str(expression.parserErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context) # Create output vector layer with new attributes error_exp = None inFeat = QgsFeature() outFeat = QgsFeature() features = QgsProcessingUtils.getFeatures(layer, context) count = QgsProcessingUtils.featureCount(layer, context) if count > 0: total = 100.0 / count 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, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) else: feedback.setProgress(100) del writer if error_exp is not None: raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}') .format(str(error_exp.expression()), str(error_exp.parserErrorString())))
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))
def evaluate(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Evaluate expressions against layer or features In parameters: LAYER=wms-layer-name EXPRESSION=An expression to evaluate or EXPRESSIONS=["first expression", "second expression"] or EXPRESSIONS={"key1": "first expression", "key2": "second expression"} // optionals FEATURE={"type": "Feature", "geometry": {}, "properties": {}} or FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {}, "properties": {}}] FORM_SCOPE=boolean to add formScope based on provided features """ layername = params.get('LAYER', '') if not layername: raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = find_vector_layer(layername, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'Evaluate': {} provided".format(layername), 400) # get expressions expressions = params.get('EXPRESSIONS', '') if not expressions: expression = params.get('EXPRESSION', '') if not expression: raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: EXPRESSION or EXPRESSIONS parameter is mandatory", 400) expressions = '["{}"]'.format(expression) # try to load expressions list or dict try: exp_json = json.loads(expressions) except Exception: QgsMessageLog.logMessage( "JSON loads expressions '{}' exception:\n{}".format(expressions, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: EXPRESSIONS '{}' are not well formed".format(expressions), 400) # create expression context exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope(project)) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) # create distance area context da = QgsDistanceArea() da.setSourceCrs(layer.crs(), project.transformContext()) da.setEllipsoid(project.ellipsoid()) # parse expressions exp_map = {} exp_parser_errors = [] exp_items = [] if isinstance(exp_json, list): exp_items = enumerate(exp_json) elif isinstance(exp_json, dict): exp_items = exp_json.items() for k, e in exp_items: exp = QgsExpression(e) exp.setGeomCalculator(da) exp.setDistanceUnits(project.distanceUnits()) exp.setAreaUnits(project.areaUnits()) if exp.hasParserError(): exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString())) continue if not exp.isValid(): exp_parser_errors.append('Expression not valid "{}"'.format(e)) continue exp.prepare(exp_context) exp_map[k] = exp # expression parser errors found if exp_parser_errors: raise ExpressionServiceError( "Bad request error", "Invalid EXPRESSIONS for 'Evaluate':\n{}".format('\n'.join(exp_parser_errors)), 400) # get features features = params.get('FEATURES', '') if not features: feature = params.get('FEATURE', '') if feature: features = '[' + feature + ']' # create the body body = { 'status': 'success', 'results': [], 'errors': [], 'features': 0 } # without features just evaluate expression with layer context if not features: result = {} error = {} for k, exp in exp_map.items(): value = exp.evaluate(exp_context) if exp.hasEvalError(): result[k] = None error[k] = exp.evalErrorString() else: result[k] = json.loads(QgsJsonUtils.encodeValue(value)) body['results'].append(result) body['errors'].append(error) write_json_response(body, response) return # Check features try: geojson = json.loads(features) except Exception: QgsMessageLog.logMessage( "JSON loads features '{}' exception:\n{}".format(features, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features), 400) if not geojson or not isinstance(geojson, list) or len(geojson) == 0: raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features), 400) if 'type' not in geojson[0] or geojson[0]['type'] != 'Feature': raise ExpressionServiceError( "Bad request error", ("Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not " "Feature.").format(features), 400) # try to load features # read fields feature_fields = QgsJsonUtils.stringToFields( '{ "type": "FeatureCollection","features":' + features + '}', QTextCodec.codecForName("UTF-8")) # read features feature_list = QgsJsonUtils.stringToFeatureList( '{ "type": "FeatureCollection","features":' + features + '}', feature_fields, QTextCodec.codecForName("UTF-8")) # features not well formed if not feature_list: raise ExpressionServiceError( "Bad request error", "Invalid FEATURES for 'Evaluate': not GeoJSON features array provided\n{}".format(features), 400) # Extend layer fields with this provided in GeoJSON Features feat_fields = QgsFields(layer.fields()) feat_fields.extend(feature_fields) # form scope add_form_scope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't'] # loop through provided features to evaluate expressions for f in feature_list: # clone the features with all attributes # those defined in layer + fields from GeoJSON Features feat = QgsFeature(feat_fields) feat.setGeometry(f.geometry()) for field in f.fields(): fname = field.name() if feat_fields.indexOf(fname) != -1: feat.setAttribute(fname, f[fname]) # Add form scope to expression context if add_form_scope: exp_context.appendScope(QgsExpressionContextUtils.formScope(feat)) exp_context.setFeature(feat) exp_context.setFields(feat.fields()) # Evaluate expressions with the new feature result = {} error = {} for k, exp in exp_map.items(): if add_form_scope: # need to prepare the expression because the context has been updated with a new scope exp.prepare(exp_context) value = exp.evaluate(exp_context) if exp.hasEvalError(): result[k] = None error[k] = exp.evalErrorString() else: result[k] = json.loads(QgsJsonUtils.encodeValue(value)) error[k] = exp.expression() body['results'].append(result) body['errors'].append(error) write_json_response(body, response) return