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 processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_LAYER)) geometry_type = self.getParameterValue(self.OUTPUT_GEOMETRY) wkb_type = None if geometry_type == 0: wkb_type = QgsWkbTypes.Polygon elif geometry_type == 1: wkb_type = QgsWkbTypes.LineString else: wkb_type = QgsWkbTypes.Point if self.getParameterValue(self.WITH_Z): wkb_type = QgsWkbTypes.addZ(wkb_type) if self.getParameterValue(self.WITH_M): wkb_type = QgsWkbTypes.addM(wkb_type) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter( layer.fields(), wkb_type, layer.crs()) expression = QgsExpression(self.getParameterValue(self.EXPRESSION)) if expression.hasParserError(): raise GeoAlgorithmExecutionException(expression.parserErrorString()) exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) if not expression.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: %s' % expression.evalErrorString())) features = vector.features(layer) total = 100.0 / len(features) for current, input_feature in enumerate(features): output_feature = input_feature exp_context.setFeature(input_feature) value = expression.evaluate(exp_context) if expression.hasEvalError(): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: %s' % expression.evalErrorString())) if not value: output_feature.setGeometry(QgsGeometry()) else: if not isinstance(value, QgsGeometry): raise GeoAlgorithmExecutionException( self.tr('{} is not a geometry').format(value)) output_feature.setGeometry(value) writer.addFeature(output_feature) progress.setPercentage(int(current * total)) del writer
def check_for_update_events(self, widget, value): if not self.feature: return from qgis.core import QgsExpression, QgsExpressionContext, QgsExpressionContextScope # If we don't have any events for this widgets just get out now if not widget.id in self.events: return events = self.events[widget.id] events = [event for event in events if event['event'].lower() == 'update'] if not events: return feature = self.to_feature(no_defaults=True) for event in events: action = event['action'].lower() targetid = event['target'] if targetid == widget.id: utils.log("Can't connect events to the same widget. ID {}".format(targetid)) continue widget = self.get_widget_from_id(targetid) if not widget: utils.log("Can't find widget for id {} in form".format(targetid)) continue condition = event['condition'] expression = event['value'] context = QgsExpressionContext() scope = QgsExpressionContextScope() scope.setVariable("value", value) scope.setVariable("field", widget.field) context.setFeature(feature) context.appendScope(scope) conditionexp = QgsExpression(condition) exp = QgsExpression(expression) if action.lower() == "show": widget.hidden = not conditionexp.evaluate(context) if action.lower() == "hide": widget.hidden = conditionexp.evaluate(context) if action == 'widget expression': if conditionexp.evaluate(context): newvalue = self.widget_default(field, feature=feature) widget.setvalue(newvalue) if action == 'set value': if conditionexp.evaluate(context): newvalue = exp.evaluate(context) widget.setvalue(newvalue)
def sum_fields(layer, output_field_key, input_fields): """Sum the value of input_fields and put it as output_field. :param layer: The vector layer. :type layer: QgsVectorLayer :param output_field_key: The output field definition key. :type output_field_key: basestring :param input_fields: List of input fields' name. :type input_fields: list """ field_definition = definition(output_field_key) output_field_name = field_definition['field_name'] # If the fields only has one element if len(input_fields) == 1: # Name is different, copy it if input_fields[0] != output_field_name: to_rename = {input_fields[0]: output_field_name} # We copy only, it will be deleted later. # We can't rename the field, we need to copy it as the same # field might be used many times in the FMT tool. copy_fields(layer, to_rename) else: # Name is same, do nothing return else: # Creating expression # Put field name in a double quote. See #4248 input_fields = ['"%s"' % f for f in input_fields] string_expression = ' + '.join(input_fields) sum_expression = QgsExpression(string_expression) context = QgsExpressionContext() context.setFields(layer.fields()) sum_expression.prepare(context) # Get the output field index output_idx = layer.fields().lookupField(output_field_name) # Output index is not found layer.startEditing() if output_idx == -1: output_field = create_field_from_definition(field_definition) layer.addAttribute(output_field) output_idx = layer.fields().lookupField(output_field_name) # Iterate to all features for feature in layer.getFeatures(): context.setFeature(feature) result = sum_expression.evaluate(context) feature[output_idx] = result layer.updateFeature(feature) layer.commitChanges()
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) geometry_type = self.getParameterValue(self.OUTPUT_GEOMETRY) wkb_type = None if geometry_type == 0: wkb_type = QgsWkbTypes.Polygon elif geometry_type == 1: wkb_type = QgsWkbTypes.LineString else: wkb_type = QgsWkbTypes.Point if self.getParameterValue(self.WITH_Z): wkb_type = QgsWkbTypes.addZ(wkb_type) if self.getParameterValue(self.WITH_M): wkb_type = QgsWkbTypes.addM(wkb_type) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter(layer.fields(), wkb_type, layer.crs(), context) expression = QgsExpression(self.getParameterValue(self.EXPRESSION)) if expression.hasParserError(): raise GeoAlgorithmExecutionException(expression.parserErrorString()) exp_context = QgsExpressionContext(QgsExpressionContextUtils.globalProjectLayerScopes(layer)) if not expression.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: {0}').format(expression.evalErrorString())) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, input_feature in enumerate(features): output_feature = input_feature exp_context.setFeature(input_feature) value = expression.evaluate(exp_context) if expression.hasEvalError(): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: {0}').format(expression.evalErrorString())) if not value: output_feature.setGeometry(QgsGeometry()) else: if not isinstance(value, QgsGeometry): raise GeoAlgorithmExecutionException( self.tr('{} is not a geometry').format(value)) output_feature.setGeometry(value) writer.addFeature(output_feature) feedback.setProgress(int(current * total)) del writer
def sum_fields(layer, output_field_key, input_fields): """Sum the value of input_fields and put it as output_field. :param layer: The vector layer. :type layer: QgsVectorLayer :param output_field_key: The output field definition key. :type output_field_key: basestring :param input_fields: List of input fields' name. :type input_fields: list """ field_definition = definition(output_field_key) output_field_name = field_definition['field_name'] # If the fields only has one element if len(input_fields) == 1: # Name is different, copy it if input_fields[0] != output_field_name: copy_fields(layer, { input_fields[0]: output_field_name}) # Name is same, do nothing else: return else: # Creating expression # Put field name in a double quote. See #4248 input_fields = ['"%s"' % f for f in input_fields] string_expression = ' + '.join(input_fields) sum_expression = QgsExpression(string_expression) context = QgsExpressionContext() context.setFields(layer.pendingFields()) sum_expression.prepare(context) # Get the output field index output_idx = layer.fieldNameIndex(output_field_name) # Output index is not found if output_idx == -1: output_field = create_field_from_definition(field_definition) layer.startEditing() layer.addAttribute(output_field) layer.commitChanges() output_idx = layer.fieldNameIndex(output_field_name) layer.startEditing() # Iterate to all features for feature in layer.getFeatures(): context.setFeature(feature) result = sum_expression.evaluate(context) feature[output_idx] = result layer.updateFeature(feature) layer.commitChanges()
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 qgis.PyQt.QtCore import QVariant layer.startEditing() # Add field with integer from 0 to 4 which represents the flood # class. Its the same as 'state' field except that is being treated # as a string. # This is used for cartography flood_class_field = QgsField('floodclass', QVariant.Int) layer.addAttribute(flood_class_field) layer.commitChanges() layer.startEditing() flood_class_idx = layer.fields().lookupField('floodclass') flood_class_expression = QgsExpression('to_int(state)') context = QgsExpressionContext() context.setFields(layer.fields()) flood_class_expression.prepare(context) # Add field with boolean flag to say if the area is flooded # This is used by the impact function flooded_field = QgsField('flooded', QVariant.Int) layer.dataProvider().addAttributes([flooded_field]) layer.commitChanges() layer.startEditing() flooded_idx = layer.fields().lookupField('flooded') flood_flag_expression = QgsExpression('state > 0') flood_flag_expression.prepare(context) for feature in layer.getFeatures(): context.setFeature(feature) feature[flood_class_idx] = flood_class_expression.evaluate(context) feature[flooded_idx] = flood_flag_expression.evaluate(context) layer.updateFeature(feature) layer.commitChanges() return layer
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 add25dAttributes(cleanLayer, layer, canvas): provider = cleanLayer.dataProvider() provider.addAttributes([QgsField("height", QVariant.Double), QgsField("wallColor", QVariant.String), QgsField("roofColor", QVariant.String)]) cleanLayer.updateFields() fields = cleanLayer.fields() renderer = layer.renderer() renderContext = QgsRenderContext.fromMapSettings(canvas.mapSettings()) feats = layer.getFeatures() context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression = QgsExpression('eval(@qgis_25d_height)') heightField = fields.indexFromName("height") wallField = fields.indexFromName("wallColor") roofField = fields.indexFromName("roofColor") renderer.startRender(renderContext, fields) cleanLayer.startEditing() for feat in feats: context.setFeature(feat) height = expression.evaluate(context) if isinstance(renderer, QgsCategorizedSymbolRenderer): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) catIndex = renderer.categoryIndexForValue(attrValue) categories = renderer.categories() symbol = categories[catIndex].symbol() elif isinstance(renderer, QgsGraduatedSymbolRenderer): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) ranges = renderer.ranges() for range in ranges: if (attrValue >= range.lowerValue() and attrValue <= range.upperValue()): symbol = range.symbol().clone() else: symbol = renderer.symbolForFeature(feat, renderContext) sl1 = symbol.symbolLayer(1) sl2 = symbol.symbolLayer(2) wallColor = sl1.subSymbol().color().name() roofColor = sl2.subSymbol().color().name() provider.changeAttributeValues({feat.id() + 1: {heightField: height, wallField: wallColor, roofField: roofColor}}) cleanLayer.commitChanges() renderer.stopRender(renderContext)
def _layer_tooltip(self, layer, feat): try: df = layer.displayField() if df: return str(feat.attribute(df)) else: context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance())) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) context.appendScope(QgsExpressionContextUtils.mapSettingsScope( self.canvas.mapSettings() ) ) context.setFeature( feat ) x = QgsExpression(layer.displayExpression()) x.prepare(context) return x.evaluate(context).replace('\n', "<br/>") except: return ""
def testConcurrency(self): """ The connection pool has a maximum of 4 connections defined (+2 spare connections) Make sure that if we exhaust those 4 connections and force another connection it is actually using the spare connections and does not freeze. This situation normally happens when (at least) 4 rendering threads are active in parallel and one requires an expression to be evaluated. """ # Acquire the maximum amount of concurrent connections iterators = list() for i in range(QgsApplication.instance().maxConcurrentConnectionsPerPool()): iterators.append(self.vl.getFeatures()) # Run an expression that will also do a request and should use a spare # connection. It just should not deadlock here. feat = next(iterators[0]) context = QgsExpressionContext() context.setFeature(feat) exp = QgsExpression('get_feature(\'{layer}\', \'pk\', 5)'.format(layer=self.vl.id())) exp.evaluate(context)
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 layer_value(feature, layer, defaultconfig): if not canvas: roam.utils.warning("No canvas set for using layer_values default function") return None layers = [] # layer name can also be a list of layers to search layername = defaultconfig['layer'] if isinstance(layername, basestring): layers.append(layername) else: layers = layername expression = defaultconfig['expression'] field = defaultconfig['field'] for searchlayer in layers: try: searchlayer = QgsMapLayerRegistry.instance().mapLayersByName(searchlayer)[0] except IndexError: RoamEvents.raisemessage("Missing layer", "Unable to find layer used in widget expression {}".format(searchlayer), level=1) roam.utils.warning("Unable to find expression layer {}".format(searchlayer)) return 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) rect.scale(20) rect = canvas.mapRenderer().mapToLayerCoordinates(layer, rect) rq = QgsFeatureRequest().setFilterRect(rect)\ .setFlags(QgsFeatureRequest.ExactIntersect) features = searchlayer.getFeatures(rq) else: features = searchlayer.getFeatures() expression = expression.replace("$roamgeometry", "@roamgeometry") exp = QgsExpression(expression) exp.prepare(searchlayer.pendingFields()) if exp.hasParserError(): error = exp.parserErrorString() roam.utils.warning(error) context = QgsExpressionContext() scope = QgsExpressionContextScope() context.appendScope(scope) scope.setVariable("roamgeometry", feature.geometry()) for f in features: context.setFeature(f) value = exp.evaluate(context) if exp.hasEvalError(): error = exp.evalErrorString() roam.utils.warning(error) if value: return f[field] raise DefaultError('No features found')
def fetch_values_from_layer(self): # pylint: disable=too-many-locals, too-many-branches, too-many-statements """ (Re)fetches plot values from the source layer. """ # Note: we keep things nice and efficient and only iterate a single time over the layer! if not self.context_generator: context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes( self.source_layer)) else: context = self.context_generator.createExpressionContext() # add a new scope corresponding to the source layer -- this will potentially overwrite any other # layer scopes which may be present in the context (e.g. from atlas layers), but we need to ensure # that source layer fields and attributes are present in the context context.appendScope( self.source_layer.createExpressionContextScope()) self.settings.data_defined_properties.prepare(context) self.fetch_layout_properties(context) def add_source_field_or_expression(field_or_expression): field_index = self.source_layer.fields().lookupField( field_or_expression) if field_index == -1: expression = QgsExpression(field_or_expression) if not expression.hasParserError(): expression.prepare(context) return expression, expression.needsGeometry( ), expression.referencedColumns() return None, False, {field_or_expression} x_expression, x_needs_geom, x_attrs = add_source_field_or_expression(self.settings.properties['x_name']) if \ self.settings.properties[ 'x_name'] else (None, False, set()) y_expression, y_needs_geom, y_attrs = add_source_field_or_expression(self.settings.properties['y_name']) if \ self.settings.properties[ 'y_name'] else (None, False, set()) z_expression, z_needs_geom, z_attrs = add_source_field_or_expression(self.settings.properties['z_name']) if \ self.settings.properties[ 'z_name'] else (None, False, set()) additional_info_expression, additional_needs_geom, additional_attrs = add_source_field_or_expression( self.settings.layout['additional_info_expression'] ) if self.settings.layout['additional_info_expression'] else (None, False, set()) attrs = set().union( self.settings.data_defined_properties.referencedFields(), x_attrs, y_attrs, z_attrs, additional_attrs) request = QgsFeatureRequest() if self.settings.data_defined_properties.property( PlotSettings.PROPERTY_FILTER).isActive(): expression = self.settings.data_defined_properties.property( PlotSettings.PROPERTY_FILTER).asExpression() request.setFilterExpression(expression) request.setExpressionContext(context) request.setSubsetOfAttributes(attrs, self.source_layer.fields()) if not x_needs_geom and not y_needs_geom and not z_needs_geom and not additional_needs_geom and not self.settings.data_defined_properties.hasActiveProperties( ): request.setFlags(QgsFeatureRequest.NoGeometry) visible_geom_engine = None if self.settings.properties.get( 'visible_features_only', False) and self.visible_region is not None: ct = QgsCoordinateTransform( self.visible_region.crs(), self.source_layer.crs(), QgsProject.instance().transformContext()) try: rect = ct.transformBoundingBox(self.visible_region) request.setFilterRect(rect) except QgsCsException: pass elif self.settings.properties.get( 'visible_features_only', False) and self.polygon_filter is not None: ct = QgsCoordinateTransform( self.polygon_filter.crs(), self.source_layer.crs(), QgsProject.instance().transformContext()) try: rect = ct.transformBoundingBox( self.polygon_filter.geometry.boundingBox()) request.setFilterRect(rect) g = self.polygon_filter.geometry g.transform(ct) visible_geom_engine = QgsGeometry.createGeometryEngine( g.constGet()) visible_geom_engine.prepareGeometry() except QgsCsException: pass if self.selected_features_only: it = self.source_layer.getSelectedFeatures(request) else: it = self.source_layer.getFeatures(request) # Some plot types don't draw individual glyphs for each feature, but aggregate them instead. # In that case it doesn't make sense to evaluate expressions for settings like marker size or color for each # feature. Instead, the evaluation should be executed only once for these settings. aggregating = self.settings.plot_type in ['box', 'histogram'] executed = False xx = [] yy = [] zz = [] additional_hover_text = [] marker_sizes = [] colors = [] stroke_colors = [] stroke_widths = [] for f in it: if visible_geom_engine and not visible_geom_engine.intersects( f.geometry().constGet()): continue self.settings.feature_ids.append(f.id()) context.setFeature(f) x = None if x_expression: x = x_expression.evaluate(context) if x == NULL or x is None: continue elif self.settings.properties['x_name']: x = f[self.settings.properties['x_name']] if x == NULL or x is None: continue y = None if y_expression: y = y_expression.evaluate(context) if y == NULL or y is None: continue elif self.settings.properties['y_name']: y = f[self.settings.properties['y_name']] if y == NULL or y is None: continue z = None if z_expression: z = z_expression.evaluate(context) if z == NULL or z is None: continue elif self.settings.properties['z_name']: z = f[self.settings.properties['z_name']] if z == NULL or z is None: continue if additional_info_expression: additional_hover_text.append( additional_info_expression.evaluate(context)) elif self.settings.layout['additional_info_expression']: additional_hover_text.append( f[self.settings.layout['additional_info_expression']]) if x is not None: xx.append(x) if y is not None: yy.append(y) if z is not None: zz.append(z) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_MARKER_SIZE): default_value = self.settings.properties['marker_size'] context.setOriginalValueVariable(default_value) value, _ = self.settings.data_defined_properties.valueAsDouble( PlotSettings.PROPERTY_MARKER_SIZE, context, default_value) marker_sizes.append(value) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_STROKE_WIDTH): default_value = self.settings.properties['marker_width'] context.setOriginalValueVariable(default_value) value, _ = self.settings.data_defined_properties.valueAsDouble( PlotSettings.PROPERTY_STROKE_WIDTH, context, default_value) stroke_widths.append(value) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_COLOR) and (not aggregating or not executed): default_value = QColor(self.settings.properties['in_color']) value, conversion_success = self.settings.data_defined_properties.valueAsColor( PlotSettings.PROPERTY_COLOR, context, default_value) if conversion_success: # We were given a valid color specification, use that color colors.append(value.name()) else: try: # Attempt to interpret the value as a list of color specifications value_list = self.settings.data_defined_properties.value( PlotSettings.PROPERTY_COLOR, context) color_list = [ QgsSymbolLayerUtils.decodeColor(item).name() for item in value_list ] colors.extend(color_list) except TypeError: # Not a list of color specifications, use the default color instead colors.append(default_value.name()) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_STROKE_COLOR) and (not aggregating or not executed): default_value = QColor(self.settings.properties['out_color']) value, conversion_success = self.settings.data_defined_properties.valueAsColor( PlotSettings.PROPERTY_STROKE_COLOR, context, default_value) if conversion_success: # We were given a valid color specification, use that color stroke_colors.append(value.name()) else: try: # Attempt to interpret the value as a list of color specifications value_list = self.settings.data_defined_properties.value( PlotSettings.PROPERTY_STROKE_COLOR, context) color_list = [ QgsSymbolLayerUtils.decodeColor(item).name() for item in value_list ] stroke_colors.extend(color_list) except TypeError: # Not a list of color specifications, use the default color instead stroke_colors.append(default_value.name()) executed = True self.settings.additional_hover_text = additional_hover_text self.settings.x = xx self.settings.y = yy self.settings.z = zz if marker_sizes: self.settings.data_defined_marker_sizes = marker_sizes if colors: self.settings.data_defined_colors = colors if stroke_colors: self.settings.data_defined_stroke_colors = stroke_colors if stroke_widths: self.settings.data_defined_stroke_widths = stroke_widths
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ vectorLayer = self.parameterAsVectorLayer(parameters, self.INPUT, context) expression = self.parameterAsString(parameters, self.EXPRESSION, context) expr = QgsExpression(expression) if expr.hasParserError(): raise QgsProcessingException(expr.parserErrorString()) context = QgsExpressionContext() count = vectorLayer.featureCount() if vectorLayer.featureCount() else 0 total = 100.0 / count # And now we can process attributes = [] duplicates = [] index = 0 protokoll = [] #Iterating over Vector Layer feedback.pushInfo("Processing ") iter = vectorLayer.getFeatures() for current, feature in enumerate(iter): try: #create value from Expression context.setFeature(feature) value = expr.evaluate(context) except Exception as err: msg = "Error while run Expression" + str( expr.expression()) + " on feature " + str( feature.attributes()) + ": " + str( err.args) + ";" + str(repr(err)) feedback.reportError(msg) raise QgsProcessingException(msg) try: #is the attribut in the list yet index = attributes.index(value) feedback.pushInfo("Index " + str(value) + ": " + str(index)) #index=attributes.index(feature[fidx]) except ValueError as err: # value is not in the list index = -1 #feedback.reportError( str(repr(err)) ) # "ValueError: "+str( value ) + ": " + str(err.args) + ";" + except KeyError as err: # value is not in the list index = -1 #feedback.reportError( "KeyError: "+str(value) + ": " + str(err.args) + ";" + str(repr(err)) ) if index == -1: # add new attribute in the list attributes.append(value) else: # it is a duplicate duplicates.append(feature.id()) # Update the progress bar proz = int((current + 1) * total) feedback.setProgress(proz) feedback.pushInfo(str(len(duplicates)) + " duplicates were selected!") if len(duplicates) > 0: vectorLayer.select(duplicates) return {self.OUTPUT: parameters[self.INPUT]}
def reloadData(self): self.beginResetModel() self._document_list = [] if self._relation.isValid() is False or self._feature.isValid( ) is False: self.endResetModel() return feature_list = [] layer = self._relation.referencingLayer() request = self._relation.getRelatedFeaturesRequest(self._feature) for feature in layer.getFeatures(request): feature_list.append(feature) if self._nmRelation.isValid(): filters = [] for joinTableFeature in feature_list: referencedFeatureRequest = self._nmRelation.getReferencedFeatureRequest( joinTableFeature) filterExpression = referencedFeatureRequest.filterExpression() filters.append("(" + filterExpression.expression() + ")") nmRequest = QgsFeatureRequest() nmRequest.setFilterExpression(" OR ".join(filters)) feature_list = [] layer = self._nmRelation.referencedLayer() for documentFeature in layer.getFeatures(nmRequest): feature_list.append(documentFeature) for documentFeature in feature_list: documents_path = str() if self._documents_path: exp = QgsExpression(self._documents_path) context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes(layer)) context.setFeature(documentFeature) documents_path = exp.evaluate(context) document_filename = str() if self._document_filename: exp = QgsExpression(self._document_filename) context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes(layer)) context.setFeature(documentFeature) document_filename = exp.evaluate(context) file_info = QFileInfo(QDir(str(documents_path)), str(document_filename)) # ToolTip toolTipList = [] toolTipList.append("<ul>") for field in documentFeature.fields(): index = documentFeature.fields().indexFromName(field.name()) toolTipList.append("<li><strong>{0}</strong>: {1}</li>".format( field.displayName(), documentFeature[index])) toolTipList.append("</ul>") self._document_list.append({ self.DocumentIdRole: documentFeature.id(), self.DocumentPathRole: file_info.filePath(), self.DocumentNameRole: file_info.fileName(), self.DocumentExistsRole: file_info.exists(), self.DocumentToolTipRole: "".join(toolTipList), self.DocumentIsImageRole: PreviewImageProvider.isMimeTypeSupported(file_info.filePath()) }) self.endResetModel()
def replaceExpressionText(self, params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Replace expression texts against layer or features In parameters: LAYER=wms-layer-name STRING=A string with expression between [% and %] or STRINGS=["first string with expression", "second string with expression"] or STRINGS={"key1": "first string with expression", "key2": "second string with 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 'ReplaceExpressionText' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = findVectorLayer(layername, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'ReplaceExpressionText': {} provided" .format(layername), 400) # get strings strings = params.get('STRINGS', '') if not strings: the_string = params.get('STRING', '') if not the_string: raise ExpressionServiceError( "Bad request error", "Invalid 'ReplaceExpressionText' REQUEST: STRING or STRINGS parameter is mandatory", 400) strings = '["{}"]'.format(the_string) # try to load expressions list or dict str_json = None try: str_json = json.loads(strings) except Exception: QgsMessageLog.logMessage( "JSON loads strings '{}' exception:\n{}".format( strings, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'ReplaceExpressionText' REQUEST: STRINGS '{}' are not well formed" .format(strings), 400) # get features features = params.get('FEATURES', '') if not features: feature = params.get('FEATURE', '') if feature: features = '[' + feature + ']' # 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()) # organized strings str_map = {} str_items = [] if isinstance(str_json, list): str_items = enumerate(str_json) elif isinstance(str_json, dict): str_items = str_json.items() for k, s in str_items: str_map[k] = s # create the body body = { 'status': 'success', 'results': [], 'errors': [], 'features': 0 } # without features just replace expression string with layer context if not features: result = {} for k, s in str_map.items(): value = QgsExpression.replaceExpressionText(s, exp_context, da) result[k] = json.loads(QgsJsonUtils.encodeValue(value)) body['results'].append(result) 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 'ReplaceExpressionText': 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 addFormScope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't'] # loop through provided features to replace expression strings 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 addFormScope: exp_context.appendScope( QgsExpressionContextUtils.formScope(feat)) exp_context.setFeature(feat) exp_context.setFields(feat.fields()) # replace expression strings with the new feature result = {} for k, s in str_map.items(): value = QgsExpression.replaceExpressionText(s, exp_context, da) result[k] = json.loads(QgsJsonUtils.encodeValue(value)) body['results'].append(result) write_json_response(body, response) return
def exportJSONLayer(layer, eachPopup, precision, tmpFileName, exp_crs, layerFileName, safeLayerName, minify, canvas, restrictToExtent, iface, extent): cleanedLayer = writeTmpLayer(layer, eachPopup, restrictToExtent, iface, extent) if is25d(layer, canvas, restrictToExtent, extent): provider = cleanedLayer.dataProvider() provider.addAttributes([ QgsField("height", QVariant.Double), QgsField("wallColor", QVariant.String), QgsField("roofColor", QVariant.String) ]) cleanedLayer.updateFields() fields = cleanedLayer.pendingFields() renderer = layer.rendererV2() renderContext = QgsRenderContext.fromMapSettings(canvas.mapSettings()) feats = layer.getFeatures() context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression = QgsExpression('eval(@qgis_25d_height)') heightField = fields.indexFromName("height") wallField = fields.indexFromName("wallColor") roofField = fields.indexFromName("roofColor") renderer.startRender(renderContext, fields) cleanedLayer.startEditing() for feat in feats: context.setFeature(feat) height = expression.evaluate(context) if isinstance(renderer, QgsCategorizedSymbolRendererV2): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) catIndex = renderer.categoryIndexForValue(attrValue) categories = renderer.categories() symbol = categories[catIndex].symbol() elif isinstance(renderer, QgsGraduatedSymbolRendererV2): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) ranges = renderer.ranges() for range in ranges: if (attrValue >= range.lowerValue() and attrValue <= range.upperValue()): symbol = range.symbol().clone() else: symbol = renderer.symbolForFeature2(feat, renderContext) wallColor = symbol.symbolLayer(1).subSymbol().color().name() roofColor = symbol.symbolLayer(2).subSymbol().color().name() cleanedLayer.changeAttributeValue(feat.id() + 1, heightField, height) cleanedLayer.changeAttributeValue(feat.id() + 1, wallField, wallColor) cleanedLayer.changeAttributeValue(feat.id() + 1, roofField, roofColor) cleanedLayer.commitChanges() renderer.stopRender(renderContext) writer = QgsVectorFileWriter options = [] if precision != "maintain": options.append("COORDINATE_PRECISION=" + unicode(precision)) writer.writeAsVectorFormat(cleanedLayer, tmpFileName, 'utf-8', exp_crs, 'GeoJson', 0, layerOptions=options) with open(layerFileName, "w") as f2: f2.write("var json_" + unicode(safeLayerName) + "=") with open(tmpFileName, "r") as tmpFile: for line in tmpFile: if minify: line = line.strip("\n\t ") line = removeSpaces(line) f2.write(line) os.remove(tmpFileName) fields = layer.pendingFields() for field in fields: exportImages(layer, field.name(), layerFileName)
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ ueberhoehung = self.parameterAsInt(parameters, self.INPUTZFACTOR, context) rasterLayer = self.parameterAsRasterLayer(parameters, self.INPUTRASTER, context) baseLineLayer = self.parameterAsVectorLayer(parameters, self.INPUTBASELINE, context) lineLayer = self.parameterAsVectorLayer(parameters, self.INPUTINTERSECTIONLAYER, context) exprBaselineID = self.parameterAsExpression(parameters, self.SOURCE_BASLINE_ID, context) outputGeomType = 1 #Output Geometry Type Point # Retrieve the feature source and sink. The 'dest_id' variable is used # to uniquely identify the feature sink, and must be included in the # dictionary returned by the processAlgorithm function. fields=lineLayer.fields() fields.append( QgsField( "station" , QVariant.Double) ) # will be added and filled by Subprocess (algorithm_TransformToProfil_LineIntersection.py) fields.append( QgsField( "z_factor" , QVariant.Int) ) fields.append( QgsField( "profil_id" , QVariant.Int) ) # If source was not found, throw an exception to indicate that the algorithm # encountered a fatal error. The exception text can be any string, but in this # case we use the pre-built invalidSourceError method to return a standard # helper text for when a source cannot be evaluated if lineLayer is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUTINTERSECTIONLAYER)) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, fields, outputGeomType, lineLayer.sourceCrs() ) try: # Send some information to the user #feedback.pushInfo('CRS is {}'.format(lineLayer.sourceCrs().authid())) # If sink was not created, throw an exception to indicate that the algorithm # encountered a fatal error. The exception text can be any string, but in this # case we use the pre-built invalidSinkError method to return a standard # helper text for when a sink cannot be evaluated if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) features=[] #QgsFeatureIterator if len( baseLineLayer.selectedFeatures() ) > 0: features = baseLineLayer.selectedFeatures() else: features = [feat for feat in baseLineLayer.getFeatures()] feedback.pushInfo( 'Features {} used'.format( len( features ) ) ) # Compute the number of steps to display within the progress bar and # get features from source total = 100.0 / len( features ) if len( features ) else 0 #names = [field.name()+"; " for field in fields] #feedback.pushInfo(''.join( names ) ) #Clear Selection baseLineLayer.removeSelection() counter=0 for current, feature in enumerate(features): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break expr = QgsExpression( exprBaselineID ) exprContext = QgsExpressionContext() exprContext.setFeature(feature) profilID = expr.evaluate ( exprContext ) #, baseLineLayer.fields() ) feedback.pushInfo(str(exprBaselineID) + ": Profil-ID: " + str(profilID) ) #select to current feature baseLineLayer.select( feature.id() ) #create profile feature for selected line #if False: #feedback.pushInfo( "Selection " + str( lineLayer.selectedFeatureCount() ) + " Objects" ) feedback.pushInfo("Counter: " + str(counter) ) profil_features = None try: profil_features = self.runLineIntersection( lineLayer, baseLineLayer, rasterLayer, ueberhoehung, context, feedback) count=0 for grFeature in profil_features: #feedback.pushInfo("Type: " + str(type(grFeature)) ) if not str(type(grFeature)) == "<class 'qgis._core.QgsFeature'>": feedback.pushInfo("Abbruch Type: " + str(type(grFeature)) ) break feedback.pushInfo("Abbruch Type: " + str( grFeature.attributes() ) ) newFeature = QgsFeature( fields ) attrs = grFeature.attributes() attrs.append( ueberhoehung ) attrs.append( profilID ) newFeature.setAttributes( attrs ) newFeature.setGeometry( grFeature.geometry() ) # Add a feature in the sink sink.addFeature( newFeature, QgsFeatureSink.FastInsert) count=count+1 print( "Count: " + str(count) + ' at profile ' + str( profilID ) ) feedback.pushInfo("Count: " + str(count) + ' at profile ' + str( profilID ) ) except Exception as err: print("ERROR at profile " + str( profilID ) + ': '+ str(err.args) + " " + str(repr( err )) + " Fehler: " ) feedback.pushInfo("ERROR at profile " + str( profilID ) + ': '+ str(err.args) + " " + str(repr( err )) + " Fehler: " ) #Clear Selection baseLineLayer.removeSelection() # Update the progress bar feedback.setProgress(int(current * total)) counter=counter+1 except Exception as err: feedback.pushInfo("ERROR: "+ str(err.args) + " " + str(repr( err )) + " Fehler: " ) # # print("ERROR:", err.args, repr( err ), "Fehler: " ) # To run another Processing algorithm as part of this algorithm, you can use # processing.run(...). Make sure you pass the current context and feedback # to processing.run to ensure that all temporary layer outputs are available # to the executed algorithm, and that the executed algorithm can send feedback # reports to the user (and correctly handle cancelation and progress reports!) # Return the results of the algorithm. In this case our only result is # the feature sink which contains the processed features, but some # algorithms may return multiple feature sinks, calculated numeric # statistics, etc. These should all be included in the returned # dictionary, with keys matching the feature corresponding parameter # or output names. return {self.OUTPUT: dest_id}
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(context.project().ellipsoid()) exp.setGeomCalculator(da) exp.setDistanceUnits(context.project().distanceUnits()) exp.setAreaUnits(context.project().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): """ Here is where the processing itself takes place. """ ueberhoehung = self.parameterAsDouble(parameters, self.INPUTZFACTOR, context) baseLineLayer = self.parameterAsVectorLayer(parameters, self.INPUTBASELINE, context) vectorLayer = self.parameterAsVectorLayer(parameters, self.INPUTVECTORLAYER, context) baselineIDFieldName = self.parameterAsExpression( parameters, self.PROFIL_BASELINE_ID, context) offsetFieldName = self.parameterAsString(parameters, self.OFFSETFIELD, context) vectorLayerBaselineIDFieldName = self.parameterAsExpression( parameters, self.INPUTVECTORLAYER_BASLINE_ID, context) outputGeomType = vectorLayer.wkbType() #Output Geometry Type Point offsetExpr = QgsExpression(offsetFieldName) if offsetExpr.hasParserError(): feedback.reportError("Offset Expression failed: " + offsetExpr.parserErrorString()) offsetExpr = "0" # Retrieve the feature source and sink. The 'dest_id' variable is used # to uniquely identify the feature sink, and must be included in the # dictionary returned by the processAlgorithm function. fields = vectorLayer.fields() #fields.append( QgsField( "station" , QVariant.Double ) ) # will be added and filled by Subprocess (algorithm_TransformToProfil_LineIntersection.py) #.append( QgsField( "abstand" , QVariant.Double ) ) # will be added and filled by Subprocess (algorithm_TransformToProfil_LineIntersection.py) #fields.append( QgsField( "z_factor" , QVariant.Int ) ) # will be added and filled by Subprocess (algorithm_TransformToProfil_LineIntersection.py) #fields.append( QgsField( "profil_id" , QVariant.Int ) ) if vectorLayer is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUTVECTORLAYER)) if vectorLayer is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.OUTPUT)) #check if vectorlayer has Features if vectorLayer.featureCount() == 0: msg = self.tr("Error: Layer ", vectorLayer.name(), "is emty! ") feedback.reportError(msg) raise QgsProcessingException(msg) #check if baselineLayer has Features if baseLineLayer.featureCount() == 0: msg = self.tr("Error: Layer ", baseLineLayer.name(), "is emty! ") feedback.reportError(msg) raise QgsProcessingException(msg) #take CRS from Project crsProject = QgsProject.instance().crs() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, outputGeomType, vectorLayer.sourceCrs()) # If sink was not created, throw an exception to indicate that the algorithm # encountered a fatal error. The exception text can be any string, but in this # case we use the pre-built invalidSinkError method to return a standard # helper text for when a sink cannot be evaluated if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) offsetExprContext = QgsExpressionContext() # Send some information to the user #feedback.pushInfo('CRS is {}'.format(vectorLayer.sourceCrs().authid())) features = [] #QgsFeatureIterator if len(vectorLayer.selectedFeatures()) > 0: features = vectorLayer.selectedFeatures() else: features = [feat for feat in vectorLayer.getFeatures()] feedback.pushInfo('Features {} used'.format(len(features))) # Compute the number of steps to display within the progress bar and # get features from source total = 100.0 / len(features) if len(features) else 0 #names = [field.name()+"; " for field in fields] #feedback.pushInfo(''.join( names ) ) #Clear Selection vectorLayer.removeSelection() i = 0 for current, feature in enumerate(features): try: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break # Offset abstand = 0 offsetExprContext.setFeature(feature) try: abstand = offsetExpr.evaluate(offsetExprContext) except: msg = self.tr( "Error while calculating Offset from Expression. Feature " + str(feat.attributes())) feedback.reportError(msg) raise QgsProcessingException(msg) try: #check for numeric Expression Data type a = int(abstand) b = float(abstand) except: msg = self.tr( "Feature(" + feature.id() + "): " + "Error Offset Experession result must be numeric, not " + str(type(abstand))) feedback.reportError(msg) raise QgsProcessingException(msg) # get Profile Baseline by ID #profil_id of the profile feature expr = QgsExpression(vectorLayerBaselineIDFieldName) exprContext = QgsExpressionContext() exprContext.setFeature(feature) profil_id = expr.evaluate(exprContext) # baseline ID exprBaseLineID = QgsExpression(baselineIDFieldName) # remove quotes "" if baselineIDFieldName.startswith('\"'): baselineIDFieldName = baselineIDFieldName.replace('\"', '') exprText = baselineIDFieldName + '=' + str(profil_id) #feedback.pushInfo('waehle Basislinie: ' + exprText ) exprBaseLine = QgsExpression(exprText) selection = baseLineLayer.getFeatures( QgsFeatureRequest(exprBaseLine)) baseLineFeature = next(selection) linRef = LinearReferencingMaschine(baseLineFeature.geometry(), crsProject, feedback) #feedback.pushInfo("Profil_id: " + str( profil_id ) + ' feature: ' + str( feature.attributes() ) ) geom = feature.geometry() feedback.pushInfo("srcgeom: " + str(geom.asWkt())) subFeatureList = [] layerUtils = LayerUtils(crsProject, feedback) subFeatureList = layerUtils.multiPartToSinglePartFeature( feature) feedback.pushInfo("subFeatureList: " + str(len(subFeatureList))) #preparation of profile geometrys prepSubFeatureList = [] for iP, f in enumerate(subFeatureList): if linRef.isSimpleLine or vectorLayer.geometryType( ) == 0 or vectorLayer.geometryType( ) == 1: #Point (Line nur temporär): prepSubFeatureList.append(f) #keep old feature else: # Basisline hat Knickpunkte, Profilgeometrien müssen ggf. mit zusätzlichen Stützpunkten gefüllt werden # Baseline has breakpoints, we have to fill the profile geometrys with additional vertices filledSingleGeom = None if vectorLayer.geometryType() == 2: #Polygon filledSingleGeomList = self.fillPolygonVertices( f.geometry(), linRef, crsProject, feedback) #elif vectorLayer.geometryType() == 1: #Line if len(filledSingleGeomList) > 0: for g in filledSingleGeomList: #create a feature for each filled sub geometry filledFeature = QgsFeature(f) filledFeature.setGeometry(g) filledFeature.setAttributes(f.attributes()) prepSubFeatureList.append(filledFeature) else: prepSubFeatureList.append(f) #keep old feature feedback.reportError( "Feature geometry can not be filled: " + str(f.attributes())) feedback.pushInfo("prepSubFeatureList: " + str(len(prepSubFeatureList))) #Back to Real World Transformation for each sub Feature realWorldSubFeatureList = [] for pFeat in prepSubFeatureList: #Create Real World geometry with LinearReferencingMaschine realWorldFeat = linRef.transformProfileFeatureToRealWorld( pFeat, vectorLayer.crs(), feedback, abstand, ueberhoehung) realWorldSubFeatureList.append(realWorldFeat) feedback.pushInfo(str(realWorldFeat.geometry().asWkt())) ##### ggf features könnten hier wieder gruppiert werden ###### #write real worl Features to output layer for rwFeat in realWorldSubFeatureList: sink.addFeature(rwFeat, QgsFeatureSink.FastInsert) i = i + 1 # Update the progress bar feedback.setProgress(int(i * total)) except: msg = self.tr("Error on Feature " + str(i) + " " + str(feature.attributes())) feedback.reportError(msg) raise QgsProcessingException(msg) msgInfo = self.tr( str(i) + " Features were transformed to real world coordinates") feedback.pushInfo(msgInfo) # Return the results of the algorithm. In this case our only result is return {self.OUTPUT: dest_id} # # print("ERROR:", err.args, repr( err ), "Fehler: " ) # To run another Processing algorithm as part of this algorithm, you can use # processing.run(...). Make sure you pass the current context and feedback # to processing.run to ensure that all temporary layer outputs are available # to the executed algorithm, and that the executed algorithm can send feedback # reports to the user (and correctly handle cancelation and progress reports!) # Return the results of the algorithm. In this case our only result is # the feature sink which contains the processed features, but some # algorithms may return multiple feature sinks, calculated numeric # statistics, etc. These should all be included in the returned # dictionary, with keys matching the feature corresponding parameter # or output names. feedback.pushInfo(str(counter) + " Profile Baslines processed") return {self.OUTPUT: dest_id}
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 virtualFields(self, 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 """ layername = params.get('LAYER', '') if not layername: raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = findVectorLayer(layername, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'VirtualFields': {} provided". format(layername), 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 vir_json = None 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 withGeom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't'] if not withGeom: req.setFlags(QgsFeatureRequest.NoGeometry) # Fields pkAttributes = layer.primaryKeyAttributes() attributeList = [i for i in pkAttributes] fields = layer.fields() r_fields = [ f.strip() for f in params.get('FIELDS', '').split(',') if f ] for f in r_fields: attributeList.append(fields.indexOf(f)) # response response.setStatusCode(200) response.setHeader("Content-Type", "application/json") response.write('{ "type": "FeatureCollection","features":[') response.flush() jsonExporter = QgsJsonExporter(layer) if attributeList: jsonExporter.setAttributes(attributeList) separator = '' for feat in layer.getFeatures(req): fid = layername + '.' + getServerFid(feat, pkAttributes) 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 + jsonExporter.exportFeature(feat, extra, fid)) response.flush() separator = ',\n' response.write(']}') return
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ ueberhoehung = self.parameterAsInt(parameters, self.INPUTZFACTOR, context) vectorLayer = self.parameterAsVectorLayer(parameters, self.INPUTVECTORLAYER, context) baseLineLayer = self.parameterAsVectorLayer(parameters, self.INPUTBASELINE, context) offsetFieldName = self.parameterAsString(parameters, self.OFFSETFIELD, context) #offsetFieldIndex=-1 #if not offsetFieldName=="": #fields = vectorLayer.fields() #offsetFieldIndex = vectorLayer.fields().lookupField(offsetFieldName) offsetExpr=QgsExpression(offsetFieldName) if offsetExpr.hasParserError(): feedback.reportError("Offset Expression failed: " + offsetExpr.parserErrorString()) offsetExpr="0" offsetExprContext = QgsExpressionContext() baseLineFeature=None baseLine=None #Basline Layer must have only 1 Feature if baseLineLayer.featureCount()==1: #baseLine must be the first feature baseLineFeature=next(baseLineLayer.getFeatures(QgsFeatureRequest().setLimit(1))) baseLine=baseLineFeature.geometry() elif len(baseLineLayer.selectedFeatures())==1: selection=baseLineLayer.selectedFeatures() #baseLine must be the first feature selFeats=[f for f in selection] baseLineFeature=selFeats[0] baseLine=baseLineFeature.geometry() else: msg = self.tr("Error: BaseLine layer needs exactly one line feature! "+ str(baseLineLayer.featureCount()) + " Just select one feature!") feedback.reportError(msg) raise QgsProcessingException(msg) #check if vectorlayer has Features if vectorLayer.featureCount()==0: msg = self.tr("Error: Layer " , vectorLayer.name() , "is emty! ") feedback.reportError(msg) raise QgsProcessingException(msg) #take CRS from Project crsProject=QgsProject.instance().crs() #check if layers have the same crs if not baseLineLayer.crs().authid()==crsProject.authid(): # if not, transform to raster crs() trafo=QgsCoordinateTransform(baseLineLayer.crs(),crsProject,QgsProject.instance()) #transform BaseLine opResult=baseLine.transform(trafo,QgsCoordinateTransform.ForwardTransform, False) linRef = LinearReferencingMaschine(baseLine, crsProject, feedback) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, vectorLayer.fields(), vectorLayer.wkbType(), crsProject) try: total = 100.0 / vectorLayer.featureCount() except: msg = self.tr("Keine Basislinie") feedback.reportError(msg) raise QgsProcessingException(msg) #check Selection of Inputlayer #if yes, use just the selection processfeatures=None if len(vectorLayer.selectedFeatures()) == 0: processfeatures = vectorLayer.getFeatures() else: processfeatures = vectorLayer.selectedFeatures() i=0 for feat in processfeatures: #vectorLayer.getFeatures(): abstand=0 offsetExprContext.setFeature( feat ) try: abstand = offsetExpr.evaluate( offsetExprContext ) except: msg = self.tr("Error while calculating Offset from Expression. Feature " + str(feat.attributes()) ) feedback.reportError(msg) raise QgsProcessingException(msg) try: #check for numeric Expression Data type a=int(abstand) b=float(abstand) except: msg = self.tr("Error Offset Experession result must be numeric, not " + str( type( abstand )) ) feedback.reportError(msg) raise QgsProcessingException(msg) subFeatureList=[] layerUtils=LayerUtils( crsProject, feedback) subFeatureList=layerUtils.multiPartToSinglePartFeature( feat ) #preparation of profile geometrys prepSubFeatureList=[] for iP, f in enumerate(subFeatureList): if linRef.isSimpleLine or vectorLayer.geometryType() == 0 or vectorLayer.geometryType() == 1: #Point (Line nur temporär): prepSubFeatureList.append( f ) #keep old feature else: # Basisline hat Knickpunkte, Profilgeometrien müssen ggf. mit zusätzlichen Stützpunkten gefüllt werden # Baseline has breakpoints, we have to fill the profile geometrys with additional vertices filledSingleGeom = None if vectorLayer.geometryType() == 2: #Polygon filledSingleGeomList = self.fillPolygonVertices( f.geometry() , linRef, crsProject, feedback) #elif vectorLayer.geometryType() == 1: #Line if len(filledSingleGeomList) > 0: for g in filledSingleGeomList: #create a feature for each filled sub geometry filledFeature=QgsFeature( f ) filledFeature.setGeometry( g ) filledFeature.setAttributes( f.attributes() ) prepSubFeatureList.append( filledFeature ) else: prepSubFeatureList.append( f ) #keep old feature feedback.reportError( "Feature geometry can not be filled: " + str( f.attributes() ) ) #Back to Real World Transformation for each sub Feature realWorldSubFeatureList=[] for pFeat in prepSubFeatureList: #Create Real World geometry with LinearReferencingMaschine realWorldFeat=linRef.transformProfileFeatureToRealWorld( pFeat, vectorLayer.crs(), feedback, abstand, ueberhoehung ) realWorldSubFeatureList.append( realWorldFeat ) ##### ggf features könnten hier wieder gruppiert werden ###### #write real worl Features to output layer for rwFeat in realWorldSubFeatureList: sink.addFeature(rwFeat, QgsFeatureSink.FastInsert) i=i+1 # Update the progress bar feedback.setProgress(int(i * total)) msgInfo=self.tr(str(i) + " Features were transformed to real world coordinates") feedback.pushInfo(msgInfo) # Return the results of the algorithm. In this case our only result is return {self.OUTPUT: dest_id}
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 evaluate(self, 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 = findVectorLayer(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 exp_json = None 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 geojson = [] 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 addFormScope = 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 addFormScope: 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 addFormScope: # need to prepare the expression because the context as 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
def data( self ): if self._dataLoaded: return self._x, self._y, self._z self._dataLoaded=True self._x = None self._y = None self._z = None self._gridShape=None self._gridTested=False self._dataLoaded=True source=self._source zField=self._zField if source is None or zField is None or zField == '': return self._x, self._y, self._z discardTolerance=self._discardTolerance feedback=self._feedback total = source.featureCount() percent = 100.0 / total if total > 0 else 0 count = 0 x = list() y = list() z = list() try: if source.fields().lookupField(zField) >= 0: zField='"'+zField.replace('"','""')+'"' expression=QgsExpression(zField) if expression.hasParserError(): raise ContourError(tr("Cannot parse")+" "+zField) fields=source.fields() context=QgsExpressionContext() context.setFields(fields) if not expression.prepare(context): raise ContourError(tr("Cannot evaluate value")+ " "+zField) request = QgsFeatureRequest() request.setSubsetOfAttributes( expression.referencedColumns(),fields) if self._sourceFids is not None: request.setFilterFids(self._sourceFids) for current,feat in enumerate(source.getFeatures( request )): try: if feedback.isCanceled(): raise ContourError('Cancelled by user') feedback.setProgress(int(current * percent)) context.setFeature(feat) zval=expression.evaluate(context) try: zval=float(zval) except ValueError: raise ContourError(tr("Z value {0} is not number") .format(zval)) if zval is not None: fgeom = feat.geometry() if QgsWkbTypes.flatType(fgeom.wkbType()) != QgsWkbTypes.Point: raise ContourError(tr("Invalid geometry type for contouring - must be point geometry")) geom=fgeom.asPoint() x.append(geom.x()) y.append(geom.y()) z.append(zval) except Exception as ex: raise count = count + 1 npt=len(x) if npt > 0: x=np.array(x) y=np.array(y) z=np.array(z) if discardTolerance > 0: index=ContourUtils.discardDuplicatePoints( x,y,discardTolerance,self.crs().isGeographic()) npt1=len(index) if npt1 < npt: x=x[index] y=y[index] z=z[index] feedback.pushInfo(tr("{0} near duplicate points discarded - tolerance {1}") .format(npt-npt1,discardTolerance)) except ContourError as ce: feedback.reportError(ce.message()) feedback.setProgress(0) return self._x,self._y,self._z finally: feedback.setProgress(0) if len(x) < 3: feedback.reportError(tr("Too few points to contour")) return self._x, self._y, self._z self._x=x self._y=y self._z=z return self._x, self._y, self._z
def create_mesure_layer(self): """ Create an temporary point layer in the Qgis canvas """ try: QApplication.setOverrideCursor( Qt.WaitCursor) ## Start the 'wait' cursor if self.list_mesures: vl = QgsVectorLayer("Linestring?crs=" + self.selected_epsg, self.layer_name, "memory") pr = vl.dataProvider() # add fields pr.addAttributes([ QgsField("station", QVariant.String), QgsField("st_num", QVariant.Int), QgsField("st_y", QVariant.Double, "double", 12, 4), QgsField("st_x", QVariant.Double, "double", 12, 4), QgsField("st_h", QVariant.Double, "double", 10, 4), QgsField("st_hi", QVariant.Double), QgsField("vise", QVariant.String), QgsField("vise_y", QVariant.Double, "double", 12, 4), QgsField("vise_x", QVariant.Double, "double", 12, 4), QgsField("vise_h", QVariant.Double, "double", 10, 4), QgsField("vise_hs", QVariant.Double), QgsField("vise_category", QVariant.Int), QgsField("dhz", QVariant.Double), QgsField("dh", QVariant.Double) ]) vl.updateFields( ) # tell the vector layer to fetch changes from the provider # add features for item in self.list_mesures: st_y = float(item[2]) st_x = float(item[3]) vise_y = float(item[7]) vise_x = float(item[8]) fet = QgsFeature() fet.setGeometry( QgsGeometry.fromPolyline( [QgsPoint(st_y, st_x), QgsPoint(vise_y, vise_x)])) fet.setAttributes(list(item)) pr.addFeatures([fet]) # Calculate the "dhz" and "dh" columns expression1 = QgsExpression( "round((sqrt((st_y - vise_y)^2 + (st_x - vise_x)^2)), 3)") expression2 = QgsExpression( "round((vise_h + vise_hs - st_h - st_hi), 3)") context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes(vl)) vl.startEditing() for f in vl.getFeatures(): context.setFeature(f) f["dhz"] = expression1.evaluate(context) f["dh"] = expression2.evaluate(context) vl.updateFeature(f) vl.commitChanges() # add preconfigured qgis.qml style file plugin_folder = os.path.dirname(os.path.dirname(__file__)) qml_file = Path(plugin_folder) / "qml" / self.qml_style_mesure if qml_file.is_file( ): # Test if file exist, avoid error if he is missing vl.loadNamedStyle(str(qml_file)) # update layer's extent when new features have been added vl.updateExtents() # zoom to the layer extent canvas = iface.mapCanvas() canvas.setExtent(vl.extent()) # Show in project self.rmv_old_qgs_mesure_layer() QgsProject.instance().addMapLayer(vl) except: print( "Mesures -> Failed to create a new measurements Qgis layer (def create_mesure_layer)" ) QApplication.restoreOverrideCursor() ## Stop the 'wait' cursor finally: QApplication.restoreOverrideCursor() ## Stop the 'wait' cursor
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, feedback): 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( QgsExpressionContextUtils.globalProjectLayerScopes(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) feedback.setProgress(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation ' 'string:\n%s' % error))
class VectorLayer: def __init__(self, settings, layer, materialManager, modelManager): """layer: Layer object""" self.settings = settings self.renderContext = QgsRenderContext.fromMapSettings( settings.mapSettings) self.mapLayer = layer.mapLayer self.name = self.mapLayer.name() if self.mapLayer else "no title" self.properties = layer.properties self.expressionContext = QgsExpressionContext() self.expressionContext.appendScope( QgsExpressionContextUtils.layerScope(self.mapLayer)) self.objectType = ObjectType.typeByName( self.properties.get("comboBox_ObjectType"), self.mapLayer.geometryType()) self.materialManager = materialManager self.modelManager = modelManager self.colorNames = [] # for random color self.transform = QgsCoordinateTransform(self.mapLayer.crs(), settings.crs, QgsProject.instance()) self.geomType = self.mapLayer.geometryType() # attributes self.writeAttrs = self.properties.get("checkBox_ExportAttrs", False) self.labelAttrIndex = self.properties.get("comboBox_Label", None) self.fieldIndices = [] self.fieldNames = [] if self.writeAttrs: for index, field in enumerate(self.mapLayer.fields()): if field.editorWidgetSetup().type() != "Hidden": self.fieldIndices.append(index) self.fieldNames.append(field.displayName()) # expressions self._exprs = {} self.exprAlt = QgsExpression( self.properties.get("fieldExpressionWidget_altitude") or "0") self.exprLabel = QgsExpression( self.properties.get("labelHeightWidget", {}).get("editText") or "0") def features(self, request=None): mapTo3d = self.settings.mapTo3d() baseExtent = self.settings.baseExtent baseExtentGeom = baseExtent.geometry() rotation = baseExtent.rotation() fields = self.mapLayer.fields() # initialize symbol rendering, and then get features (geometry, attributes, color, etc.) self.renderer = self.mapLayer.renderer().clone() self.renderer.startRender(self.renderContext, self.mapLayer.fields()) for f in self.mapLayer.getFeatures(request or QgsFeatureRequest()): geometry = f.geometry() if geometry is None: logMessage("null geometry skipped") continue # coordinate transformation - layer crs to project crs geom = QgsGeometry(geometry) if geom.transform(self.transform) != 0: logMessage("Failed to transform geometry") continue # check if geometry intersects with the base extent (rotated rect) if rotation and not baseExtentGeom.intersects(geom): continue # set feature to expression context self.expressionContext.setFeature(f) # evaluate expression altitude = float( self.exprAlt.evaluate(self.expressionContext) or 0) swVals = self.styleWidgetValues(f) attrs = labelHeight = None if self.writeAttrs: attrs = [ fields[i].displayString(f.attribute(i)) for i in self.fieldIndices ] if self.hasLabel(): labelHeight = float( self.exprLabel.evaluate(self.expressionContext) or 0) * mapTo3d.multiplierZ # create a feature object yield Feature(self, geom, altitude, swVals, attrs, labelHeight) self.renderer.stopRender(self.renderContext) def evaluateExpression(self, expr_str, f): if expr_str not in self._exprs: self._exprs[expr_str] = QgsExpression(expr_str) self.expressionContext.setFeature(f) return self._exprs[expr_str].evaluate(self.expressionContext) def readFillColor(self, vals, f): return self._readColor(vals, f) def readBorderColor(self, vals, f): return self._readColor(vals, f, isBorder=True) # read color from COLOR or OPTIONAL_COLOR widget def _readColor(self, widgetValues, f, isBorder=False): mode = widgetValues["comboData"] if mode == OptionalColorWidgetFunc.NONE: return None if mode == ColorWidgetFunc.EXPRESSION: val = self.evaluateExpression(widgetValues["editText"], f) try: if isinstance(val, str): a = val.split(",") if len(a) >= 3: a = [max(0, min(int(c), 255)) for c in a[:3]] return "0x{:02x}{:02x}{:02x}".format(a[0], a[1], a[2]) return val.replace("#", "0x") raise except: logMessage("Wrong color value: {}".format(val)) return "0" if mode == ColorWidgetFunc.RANDOM or f is None: self.colorNames = self.colorNames or QColor.colorNames() color = random.choice(self.colorNames) self.colorNames.remove(color) return QColor(color).name().replace("#", "0x") # feature color symbols = self.renderer.symbolsForFeature(f, self.renderContext) if not symbols: logMessage( 'Symbol for feature not found. Please use a simple renderer for {0}.' .format(self.mapLayer.name())) return "0" symbol = symbols[0] sl = symbol.symbolLayer(0) if sl: if isBorder: return sl.strokeColor().name().replace("#", "0x") if symbol.hasDataDefinedProperties(): expr = sl.dataDefinedProperty("color") if expr: # data defined color rgb = expr.evaluate(f, f.fields()) # "rrr,ggg,bbb" (dec) to "0xRRGGBB" (hex) r, g, b = [ max(0, min(int(c), 255)) for c in rgb.split(",")[:3] ] return "0x{:02x}{:02x}{:02x}".format(r, g, b) return symbol.color().name().replace("#", "0x") def readOpacity(self, widgetValues, f): vals = widgetValues if vals["comboData"] == OpacityWidgetFunc.EXPRESSION: try: val = self.evaluateExpression(widgetValues["editText"], f) return min(max(0, val), 100) / 100 except: logMessage("Wrong opacity value: {}".format(val)) return 1 symbols = self.renderer.symbolsForFeature(f, self.renderContext) if not symbols: logMessage( 'Symbol for feature not found. Please use a simple renderer for {0}.' .format(self.mapLayer.name())) return 1 # TODO [data defined property] symbol = symbols[0] return self.mapLayer.opacity() * symbol.opacity() @classmethod def toFloat(cls, val): try: return float(val) except Exception as e: logMessage('{0} (value: {1})'.format(e.message, str(val))) return 0 # functions to read values from height widget (z coordinate) def useZ(self): return self.properties.get("radioButton_zValue", False) def useM(self): return self.properties.get("radioButton_mValue", False) def isHeightRelativeToDEM(self): return self.properties.get("comboBox_altitudeMode") is not None def hasLabel(self): return bool(self.labelAttrIndex is not None) # read values from style widgets def styleWidgetValues(self, f): vals = [] for i in range(16): # big number for style count widgetValues = self.properties.get("styleWidget" + str(i)) if not widgetValues: break widgetType = widgetValues["type"] comboData = widgetValues.get("comboData") if widgetType == StyleWidget.COLOR: vals.append(self.readFillColor(widgetValues, f)) elif widgetType == StyleWidget.OPTIONAL_COLOR: vals.append(self.readBorderColor(widgetValues, f)) elif widgetType == StyleWidget.COLOR_TEXTURE: if comboData == ColorTextureWidgetFunc.MAP_CANVAS: vals.append(comboData) elif comboData == ColorTextureWidgetFunc.LAYER: vals.append(widgetValues.get("layerIds", [])) else: vals.append(self.readFillColor(widgetValues, f)) elif widgetType == StyleWidget.OPACITY: vals.append(self.readOpacity(widgetValues, f)) elif widgetType == StyleWidget.CHECKBOX: vals.append(widgetValues["checkBox"]) elif widgetType == StyleWidget.COMBOBOX: vals.append(widgetValues["comboData"]) else: expr = widgetValues["editText"] val = self.evaluateExpression(expr, f) if val is None: logMessage("Failed to evaluate expression: " + expr) if widgetType == StyleWidget.FILEPATH: val = "" else: val = 0 vals.append(val) return vals
def processAlgorithm(self, parameters, context, feedback): if DEBUG_MODE: logMessage("processAlgorithm(): {}".format(self.__class__.__name__), False) clayer = self.parameterAsLayer(parameters, self.INPUT, context) title_field = self.parameterAsString(parameters, self.TITLE_FIELD, context) cf_filter = self.parameterAsBool(parameters, self.CF_FILTER, context) fixed_scale = self.parameterAsEnum(parameters, self.SCALE, context) # == 1 buf = self.parameterAsDouble(parameters, self.BUFFER, context) tex_width = self.parameterAsInt(parameters, self.TEX_WIDTH, context) orig_tex_height = self.parameterAsInt(parameters, self.TEX_HEIGHT, context) header_exp = QgsExpression(self.parameterAsExpression(parameters, self.HEADER, context)) footer_exp = QgsExpression(self.parameterAsExpression(parameters, self.FOOTER, context)) exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.layerScope(clayer)) out_dir = self.parameterAsString(parameters, self.OUTPUT, context) if not QDir(out_dir).exists(): QDir().mkpath(out_dir) if DEBUG_MODE: openDirectory(out_dir) mapSettings = self.settings.mapSettings baseExtent = self.settings.baseExtent rotation = mapSettings.rotation() orig_size = mapSettings.outputSize() if cf_filter: cf_layer = QgsMemoryProviderUtils.createMemoryLayer("current feature", clayer.fields(), clayer.wkbType(), clayer.crs()) layers = [cf_layer if lyr == clayer else lyr for lyr in mapSettings.layers()] mapSettings.setLayers(layers) doc = QDomDocument("qgis") clayer.exportNamedStyle(doc) cf_layer.importNamedStyle(doc) total = clayer.featureCount() for current, feature in enumerate(clayer.getFeatures()): if feedback.isCanceled(): break if cf_filter: cf_layer.startEditing() cf_layer.deleteFeatures([f.id() for f in cf_layer.getFeatures()]) cf_layer.addFeature(feature) cf_layer.commitChanges() title = feature.attribute(title_field) feedback.setProgressText("({}/{}) Exporting {}...".format(current + 1, total, title)) logMessage("Exporting {}...".format(title), False) # extent geometry = QgsGeometry(feature.geometry()) geometry.transform(self.transform) center = geometry.centroid().asPoint() if fixed_scale or geometry.type() == QgsWkbTypes.PointGeometry: tex_height = orig_tex_height or int(tex_width * orig_size.height() / orig_size.width()) rect = RotatedRect(center, baseExtent.width(), baseExtent.width() * tex_height / tex_width, rotation).scale(1 + buf / 100) else: geometry.rotate(rotation, center) rect = geometry.boundingBox().scaled(1 + buf / 100) center = RotatedRect.rotatePoint(rect.center(), rotation, center) if orig_tex_height: tex_height = orig_tex_height tex_ratio = tex_width / tex_height rect_ratio = rect.width() / rect.height() if tex_ratio > rect_ratio: rect = RotatedRect(center, rect.height() * tex_ratio, rect.height(), rotation) else: rect = RotatedRect(center, rect.width(), rect.width() / tex_ratio, rotation) else: # fit to buffered geometry bounding box rect = RotatedRect(center, rect.width(), rect.height(), rotation) tex_height = tex_width * rect.height() / rect.width() rect.toMapSettings(mapSettings) mapSettings.setOutputSize(QSize(tex_width, tex_height)) self.settings.setMapSettings(mapSettings) # labels exp_context.setFeature(feature) self.settings.setHeaderLabel(header_exp.evaluate(exp_context)) self.settings.setFooterLabel(footer_exp.evaluate(exp_context)) self.export(title, out_dir, feedback) feedback.setProgress(int(current / total * 100)) if P_OPEN_DIRECTORY and not DEBUG_MODE: openDirectory(out_dir) return {}
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 exportLayers(iface, layers, folder, precision, optimize, popupField, json, restrictToExtent, extent, feedback): canvas = iface.mapCanvas() epsg4326 = QgsCoordinateReferenceSystem("EPSG:4326") layersFolder = os.path.join(folder, "layers") QDir().mkpath(layersFolder) for count, (layer, encode2json, popup) in enumerate(zip(layers, json, popupField)): if (layer.type() == layer.VectorLayer and (layer.providerType() != "WFS" or encode2json)): feedback.showFeedback("Exporting %s to JSON..." % layer.name()) cleanLayer = writeTmpLayer(layer, popup, restrictToExtent, iface, extent) fields = layer.pendingFields() for field in fields: exportImages(layer, field.name(), layersFolder + "/tmp.tmp") if is25d(layer, canvas, restrictToExtent, extent): provider = cleanLayer.dataProvider() provider.addAttributes([ QgsField("height", QVariant.Double), QgsField("wallColor", QVariant.String), QgsField("roofColor", QVariant.String) ]) cleanLayer.updateFields() fields = cleanLayer.pendingFields() renderer = layer.rendererV2() renderContext = QgsRenderContext.fromMapSettings( canvas.mapSettings()) feats = layer.getFeatures() context = QgsExpressionContext() context.appendScope( QgsExpressionContextUtils.layerScope(layer)) expression = QgsExpression('eval(@qgis_25d_height)') heightField = fields.indexFromName("height") wallField = fields.indexFromName("wallColor") roofField = fields.indexFromName("roofColor") renderer.startRender(renderContext, fields) cleanLayer.startEditing() for feat in feats: context.setFeature(feat) height = expression.evaluate(context) if isinstance(renderer, QgsCategorizedSymbolRendererV2): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) catIndex = renderer.categoryIndexForValue(attrValue) categories = renderer.categories() symbol = categories[catIndex].symbol() elif isinstance(renderer, QgsGraduatedSymbolRendererV2): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) ranges = renderer.ranges() for range in ranges: if (attrValue >= range.lowerValue() and attrValue <= range.upperValue()): symbol = range.symbol().clone() else: symbol = renderer.symbolForFeature2( feat, renderContext) sl1 = symbol.symbolLayer(1) sl2 = symbol.symbolLayer(2) wallColor = sl1.subSymbol().color().name() roofColor = sl2.subSymbol().color().name() provider.changeAttributeValues({ feat.id() + 1: { heightField: height, wallField: wallColor, roofField: roofColor } }) cleanLayer.commitChanges() renderer.stopRender(renderContext) sln = safeName(cleanLayer.name()) + unicode(count) tmpPath = os.path.join(layersFolder, sln + ".json") path = os.path.join(layersFolder, sln + ".js") options = [] if precision != "maintain": options.append("COORDINATE_PRECISION=" + unicode(precision)) QgsVectorFileWriter.writeAsVectorFormat(cleanLayer, tmpPath, "utf-8", epsg4326, 'GeoJson', 0, layerOptions=options) with open(path, "w") as f: f.write("var %s = " % ("geojson_" + sln)) with open(tmpPath, "r") as f2: for line in f2: if optimize: line = line.strip("\n\t ") line = removeSpaces(line) f.write(line) os.remove(tmpPath) elif (layer.type() == layer.RasterLayer and layer.providerType() != "wms"): feedback.showFeedback("Exporting %s to PNG..." % layer.name()) name_ts = (safeName(layer.name()) + unicode(count) + unicode(int(time.time()))) # We need to create a new file to export style piped_file = os.path.join(tempfile.gettempdir(), name_ts + '_piped.tif') piped_extent = layer.extent() piped_width = layer.height() piped_height = layer.width() piped_crs = layer.crs() piped_renderer = layer.renderer() piped_provider = layer.dataProvider() pipe = QgsRasterPipe() pipe.set(piped_provider.clone()) pipe.set(piped_renderer.clone()) file_writer = QgsRasterFileWriter(piped_file) file_writer.writeRaster(pipe, piped_width, piped_height, piped_extent, piped_crs) # Extent of the layer in EPSG:3857 crsSrc = layer.crs() crsDest = QgsCoordinateReferenceSystem(3857) xform = QgsCoordinateTransform(crsSrc, crsDest) extentRep = xform.transform(layer.extent()) extentRepNew = ','.join([ unicode(extentRep.xMinimum()), unicode(extentRep.xMaximum()), unicode(extentRep.yMinimum()), unicode(extentRep.yMaximum()) ]) # Reproject in 3857 piped_3857 = os.path.join(tempfile.gettempdir(), name_ts + '_piped_3857.tif') # Export layer as PNG out_raster = os.path.join( layersFolder, safeName(layer.name()) + unicode(count) + ".png") qgis_version = QGis.QGIS_VERSION if int(qgis_version.split('.')[1]) < 15: processing.runalg("gdalogr:warpreproject", piped_file, layer.crs().authid(), "EPSG:3857", "", 0, 1, 0, -1, 75, 6, 1, False, 0, False, "", piped_3857) processing.runalg("gdalogr:translate", piped_3857, 100, True, "", 0, "", extentRepNew, False, 0, 0, 75, 6, 1, False, 0, False, "", out_raster) else: try: warpArgs = { "INPUT": piped_file, "SOURCE_SRS": layer.crs().authid(), "DEST_SRS": "EPSG:3857", "NO_DATA": "", "TR": 0, "METHOD": 2, "RAST_EXT": extentRepNew, "EXT_CRS": "EPSG:3857", "RTYPE": 0, "COMPRESS": 4, "JPEGCOMPRESSION": 75, "ZLEVEL": 6, "PREDICTOR": 1, "TILED": False, "BIGTIFF": 0, "TFW": False, "EXTRA": "", "OUTPUT": piped_3857 } procRtn = processing.runalg("gdalogr:warpreproject", warpArgs) # force exception on algorithm fail for val in procRtn: pass except: try: warpArgs = { "INPUT": piped_file, "SOURCE_SRS": layer.crs().authid(), "DEST_SRS": "EPSG:3857", "NO_DATA": "", "TR": 0, "METHOD": 2, "RAST_EXT": extentRepNew, "RTYPE": 0, "COMPRESS": 4, "JPEGCOMPRESSION": 75, "ZLEVEL": 6, "PREDICTOR": 1, "TILED": False, "BIGTIFF": 0, "TFW": False, "EXTRA": "", "OUTPUT": piped_3857 } procRtn = processing.runalg("gdalogr:warpreproject", warpArgs) # force exception on algorithm fail for val in procRtn: pass except: try: warpArgs = { "INPUT": piped_file, "SOURCE_SRS": layer.crs().authid(), "DEST_SRS": "EPSG:3857", "NO_DATA": "", "TR": 0, "METHOD": 2, "RTYPE": 0, "COMPRESS": 4, "JPEGCOMPRESSION": 75, "ZLEVEL": 6, "PREDICTOR": 1, "TILED": False, "BIGTIFF": 0, "TFW": False, "EXTRA": "", "OUTPUT": piped_3857 } procRtn = processing.runalg( "gdalogr:warpreproject", warpArgs) # force exception on algorithm fail for val in procRtn: pass except: shutil.copyfile(piped_file, piped_3857) try: processing.runalg("gdalogr:translate", piped_3857, 100, True, "", 0, "", extentRepNew, False, 5, 4, 75, 6, 1, False, 0, False, "", out_raster) except: shutil.copyfile(piped_3857, out_raster) feedback.completeStep()