def mg_flow_trace_select_features(self, layer, elem_type): sql = "SELECT * FROM "+self.schema_name+".temp_mincut_"+elem_type+" ORDER BY "+elem_type+"_id" rows = self.dao.get_rows(sql) self.dao.commit() # Build an expression to select them aux = "\""+elem_type+"_id\" IN (" for elem in rows: aux+= elem[0]+", " aux = aux[:-2]+")" # Get a featureIterator from this expression: expr = QgsExpression(aux) if expr.hasParserError(): self.showWarning("Expression Error: "+str(expr.parserErrorString())) return it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result id_list = [i.id() for i in it] # Select features with these id's layer.setSelectedFeatures(id_list)
def get_feature_value(self, model=None): self.layer.startEditing() feature = None request = QgsFeatureRequest() if model is None: model = self.host.model() request.setFilterFid(model.id) feature_itr = self.layer.getFeatures(request) for feat in feature_itr: feature = feat break exp = QgsExpression(self.column.expression) if exp.hasParserError(): raise Exception(exp.parserErrorString()) exp.prepare(self.layer.pendingFields()) if feature is not None: value = exp.evaluate(feature) return value else: return None
def getValue(self): fileName = unicode(self.leText.text()) context = self.expressionContext() exp = QgsExpression(fileName) if not exp.hasParserError(): result = exp.evaluate(context) if not exp.hasEvalError(): fileName = result if fileName.startswith("[") and fileName.endswith("]"): fileName = fileName[1:-1] if fileName.strip() in ['', self.SAVE_TO_TEMP_FILE]: value = None elif fileName.startswith('memory:'): value = fileName elif fileName.startswith('postgis:'): value = fileName elif fileName.startswith('spatialite:'): value = fileName elif not os.path.isabs(fileName): value = ProcessingConfig.getSetting( ProcessingConfig.OUTPUT_FOLDER) + os.sep + fileName else: value = fileName return value
def getValue(self): fileName = unicode(self.leText.text()) context = self.expressionContext() exp = QgsExpression(fileName) if not exp.hasParserError(): result = exp.evaluate(context) if not exp.hasEvalError(): fileName = result if fileName.startswith("[") and fileName.endswith("]"): fileName = fileName[1:-1] if fileName.strip() in ["", self.SAVE_TO_TEMP_FILE, self.SAVE_TO_TEMP_LAYER]: if isinstance(self.output, OutputVector) and self.alg.provider.supportsNonFileBasedOutput(): # use memory layers for temporary files if supported value = "memory:" else: value = None elif fileName.startswith("memory:"): value = fileName elif fileName.startswith("postgis:"): value = fileName elif fileName.startswith("spatialite:"): value = fileName elif not os.path.isabs(fileName): value = ProcessingConfig.getSetting(ProcessingConfig.OUTPUT_FOLDER) + os.sep + fileName else: value = fileName return value
def setSearchAndReplaceDictionaryFromLayer(self, layer, expression): ''' Set the search and replace dictionary from a given layer and an expression. The first found features is the data source ''' searchAndReplaceDictionary = {} # Get and validate expression qExp = QgsExpression( expression ) if not qExp.hasParserError(): qReq = QgsFeatureRequest( qExp ) features = layer.getFeatures( qReq ) else: QgsMessageLog.logMessage( 'An error occured while parsing the given expression: %s' % qExp.parserErrorString() ) features = layer.getFeatures() # Get layer fields name fields = layer.pendingFields() field_names = [ field.name() for field in fields ] # Take only first feature for feat in features: # Build dictionnary searchAndReplaceDictionary = dict(zip(field_names, feat.attributes())) break self.searchAndReplaceDictionary = searchAndReplaceDictionary
def generic_zoom(self, fieldname, combo, field_index=0): ''' Get selected element from the combo, and returns a feature request expression ''' # Get selected element from combo element = combo.currentText() if element.strip() == '': print "Any record selected" return None elem = combo.itemData(combo.currentIndex()) if not elem: # that means that user has edited manually the combo but the element # does not correspond to any combo element message = 'Element {} does not exist'.format(element) self.controller.show_warning(message) return None # Select this feature in order to copy to memory layer aux = fieldname+" = '"+str(elem[field_index])+"'" expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return return expr
def resolveValue(self, alg): if self.hidden: return if not bool(self.value): self.value = self._resolveTemporary(alg) else: exp = QgsExpression(self.value) if not exp.hasParserError(): value = exp.evaluate(_expressionContext(alg)) if not exp.hasEvalError(): self.value = value if ":" not in self.value: if not os.path.isabs(self.value): self.value = os.path.join(ProcessingConfig.getSetting(ProcessingConfig.OUTPUT_FOLDER), self.value) supported = self._supportedExtensions() if supported: idx = self.value.rfind('.') if idx == -1: self.value = self.value + '.' + self.getDefaultFileExtension() else: ext = self.value[idx + 1:] if ext not in supported: self.value = self.value + '.' + self.getDefaultFileExtension()
def processAlgorithm(self, progress): layer = layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) expression = self.getParameterValue(self.EXPRESSION) qExp = QgsExpression(expression) if qExp.hasParserError(): raise GeoAlgorithmExecutionException(qExp.parserErrorString()) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( layer.fields(), layer.wkbType(), layer.crs()) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) count = layer.featureCount() step = 100.0 / count if count else 1 request = QgsFeatureRequest(qExp, context) for current, f in enumerate(layer.getFeatures(request)): writer.addFeature(f) progress.setPercentage(int(current * step)) del writer
def mg_flow_trace_select_features(self, layer, elem_type): if self.index_action == '56': sql = "SELECT * FROM "+self.schema_name+".anl_flow_trace_"+elem_type+" ORDER BY "+elem_type+"_id" else: sql = "SELECT * FROM "+self.schema_name+".anl_flow_exit_"+elem_type+" ORDER BY "+elem_type+"_id" rows = self.controller.get_rows(sql) if rows: # Build an expression to select them aux = "\""+elem_type+"_id\" IN (" for elem in rows: aux += elem[0] + ", " aux = aux[:-2] + ")" # Get a featureIterator from this expression: expr = QgsExpression(aux) if expr.hasParserError(): message = "Expression Error: " + str(expr.parserErrorString()) self.controller.show_warning(message, context_name='ui_message') return it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result id_list = [i.id() for i in it] # Select features with these id's layer.setSelectedFeatures(id_list)
def prepareAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, 'INPUT', context) mapping = self.parameterAsFieldsMapping(parameters, self.FIELDS_MAPPING, context) self.fields = QgsFields() self.expressions = [] da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs()) da.setEllipsoid(context.project().ellipsoid()) # create an expression context using thread safe processing context self.expr_context = self.createExpressionContext(parameters, context, source) for field_def in mapping: self.fields.append(QgsField(name=field_def['name'], type=field_def['type'], typeName="", len=field_def.get('length', 0), prec=field_def.get('precision', 0))) expression = QgsExpression(field_def['expression']) expression.setGeomCalculator(da) expression.setDistanceUnits(context.project().distanceUnits()) expression.setAreaUnits(context.project().areaUnits()) if expression.hasParserError(): raise QgsProcessingException( self.tr(u'Parser error in expression "{}": {}') .format(str(expression.expression()), str(expression.parserErrorString()))) self.expressions.append(expression) return True
def processAlgorithm(self, 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 showExpressionsBuilder(self): context = self.param.expressionContext() dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, 'generic', context) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): self.setValue(dlg.expressionText())
def address_get_numbers(self): ''' Populate civic numbers depending on selected street. Available civic numbers are linked with self.street_field_code column code in self.portal_layer and self.street_layer ''' # get selected street selected = self.dlg.adress_street.currentText() if selected == '': print "Any record selected" return # get street code elem = self.dlg.adress_street.itemData(self.dlg.adress_street.currentIndex()) code = elem[0] # to know the index see the query that populate the combo records = [[-1, '']] # Set filter expression layer = self.layers['portal_layer'] idx_field_code = layer.fieldNameIndex(self.params['portal_field_code']) idx_field_number = layer.fieldNameIndex(self.params['portal_field_number']) aux = self.params['portal_field_code']+" = '"+str(code)+"'" # Check filter and existence of fields expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return if idx_field_code == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['portal_field_code'], layer.name(), self.setting_file, 'portal_field_code') self.controller.show_warning(message) return if idx_field_number == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['portal_field_number'], layer.name(), self.setting_file, 'portal_field_number') self.controller.show_warning(message) return # Get a featureIterator from an expression: # Get features from the iterator and do something it = layer.getFeatures(QgsFeatureRequest(expr)) for feature in it: attrs = feature.attributes() field_number = attrs[idx_field_number] if not type(field_number) is QPyNullVariant: elem = [code, field_number] records.append(elem) # Fill numbers combo records_sorted = sorted(records, key = operator.itemgetter(1)) self.dlg.adress_number.blockSignals(True) self.dlg.adress_number.clear() for record in records_sorted: self.dlg.adress_number.addItem(str(record[1]), record) self.dlg.adress_number.blockSignals(False)
def urban_zoom(self): ''' Zoom to layer 'urban' filtering values of all combos ''' # Build expresion search aux = "" layer = self.layers['urban_propierties_layer'] fieldname = self.params['urban_propierties_field_pzone'] combo = self.dlg.urban_properties_zone text = utils_giswater.getSelectedItem(combo) if text != "null": if aux != "": aux+= " AND " aux+= fieldname+" = '"+str(text)+"'" fieldname = self.params['urban_propierties_field_block'] combo = self.dlg.urban_properties_block text = utils_giswater.getSelectedItem(combo) if text != "null": if aux != "": aux+= " AND " aux+= fieldname+" = '"+str(text)+"'" fieldname = self.params['urban_propierties_field_number'] combo = self.dlg.urban_properties_number text = utils_giswater.getSelectedItem(combo) if text != "null": if aux != "": aux+= " AND " aux+= fieldname+" = '"+str(text)+"'" # Build a list of feature id's from the expression and select them if aux != '': expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return it = layer.getFeatures(QgsFeatureRequest(expr)) ids = [i.id() for i in it] layer.setSelectedFeatures(ids) # Select all features else: layer.selectAll() # Copy selected features to memory layer self.urbanMemLayer = self.copy_selected(layer, self.urbanMemLayer, "Polygon") # Zoom to generated memory layer self.zoom_to_scale() # Load style if self.QML_URBAN is not None: self.load_style(self.urbanMemLayer, self.QML_URBAN) # Toggles 'Show feature count' self.show_feature_count()
def createExpression(self, text, da, context): expr = QgsExpression(text) expr.setGeomCalculator(da) expr.setDistanceUnits(context.project().distanceUnits()) expr.setAreaUnits(context.project().areaUnits()) if expr.hasParserError(): raise QgsProcessingException( self.tr(u'Parser error in expression "{}": {}') .format(text, expr.parserErrorString())) return expr
def getStreetNumbers(self): ''' Populate civic numbers depending on selected street. Available civic numbers are linked with self.STREET_FIELD_CODE column code in self.PORTAL_LAYER and self.STREET_LAYER ''' # get selected street selected = self.dlg.cboStreet.currentText() if selected == '': return # get street code sel_street = self.dlg.cboStreet.itemData(self.dlg.cboStreet.currentIndex()) code = sel_street[2] # to know the index see the query that populate the combo records = [[-1, '']] # Set filter expression layer = self.portalLayer idx_field_code = layer.fieldNameIndex(self.PORTAL_FIELD_CODE) idx_field_number = layer.fieldNameIndex(self.PORTAL_FIELD_NUMBER) aux = self.PORTAL_FIELD_CODE+"='"+str(code)+"'" # Check filter and existence of fields expr = QgsExpression(aux) if expr.hasParserError(): self.iface.messageBar().pushMessage(expr.parserErrorString() + ": " + aux, self.app_name, QgsMessageBar.WARNING, 10) return if idx_field_code == -1: message = self.tr("Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'". format(self.PORTAL_FIELD_CODE, layer.name(), self.setting_file, 'PORTAL_FIELD_CODE')) self.iface.messageBar().pushMessage(message, '', QgsMessageBar.WARNING) return if idx_field_number == -1: message = self.tr("Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'". format(self.PORTAL_FIELD_NUMBER, layer.name(), self.setting_file, 'PORTAL_FIELD_NUMBER')) self.iface.messageBar().pushMessage(message, '', QgsMessageBar.WARNING) return # Get a featureIterator from an expression: # Get features from the iterator and do something it = layer.getFeatures(QgsFeatureRequest(expr)) for feature in it: attrs = feature.attributes() field_number = attrs[idx_field_number] if not type(field_number) is QPyNullVariant: elem = [code, field_number] records.append(elem) # Fill numbers combo records_sorted = sorted(records, key = operator.itemgetter(1)) self.dlg.cboNumber.blockSignals(True) self.dlg.cboNumber.clear() for record in records_sorted: self.dlg.cboNumber.addItem(record[1], record) self.dlg.cboNumber.blockSignals(False)
def where(layer, exp): exp = QgsExpression(exp) if exp.hasParserError(): raise Exception(exp.parserErrorString()) exp.prepare(layer.pendingFields()) for feature in layer.getFeatures(): value = exp.evaluate(feature) if exp.hasEvalError(): raise ValueError(exp.evalErrorString()) if bool(value): yield feature
def evaluateExpression(self, text): context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) exp = QgsExpression(text) if exp.hasParserError(): raise Exception(exp.parserErrorString()) result = exp.evaluate(context) if exp.hasEvalError(): raise ValueError(exp.evalErrorString()) return result
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() exp = QgsExpression(expression) exp.prepare(searchlayer.pendingFields()) if exp.hasParserError(): error = exp.parserErrorString() roam.utils.warning(error) for f in features: value = exp.evaluate(f) if exp.hasEvalError(): error = exp.evalErrorString() roam.utils.warning(error) if value: return f[field] raise DefaultError('No features found')
def where(self, exp): '''Request feature by QgsExpression Usage : for feat in layer.where("myfield" = 2): ... ''' exp = QgsExpression(exp) if exp.hasParserError(): raise Exception(exp.parserErrorString()) if exp.hasEvalError(): raise ValueError(exp.evalErrorString()) return self.getFeatures(QgsFeatureRequest(exp))
def _buildfromlayer(self, widget, layerconfig): layername = layerconfig['layer'] keyfield = layerconfig['key'] valuefield = layerconfig['value'] filterexp = layerconfig.get('filter', None) try: layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0] except IndexError: roam.utils.warning("Can't find layer {} in project".format(layername)) return keyfieldindex = layer.fieldNameIndex(keyfield) valuefieldindex = layer.fieldNameIndex(valuefield) if keyfieldindex == -1 or valuefieldindex == -1: roam.utils.warning("Can't find key or value column") return if not filterexp and valuefieldindex == keyfieldindex: values = layer.uniqueValues(keyfieldindex) for value in values: widget.addItem(value, value) return attributes = {keyfieldindex, valuefieldindex} flags = QgsFeatureRequest.NoGeometry expression = None if filterexp: expression = QgsExpression(filterexp) expression.prepare(layer.pendingFields()) if expression.hasParserError(): roam.utils.warning("Expression has parser error: {}".format(expression.parserErrorString())) return if expression.needsGeometry(): flags = QgsFeatureRequest.NoFlags for field in expression.referencedColumns(): index = layer.fieldNameIndex(field) attributes.add(index) values = set() request = QgsFeatureRequest().setFlags(flags).setSubsetOfAttributes(list(attributes)) for feature in layer.getFeatures(request): if expression and not expression.evaluate(feature): continue widget.addItem(feature[keyfieldindex], feature[valuefield]) if self.allownulls: widget.insertItem(0, '(no selection)', None)
def getPlots(self): # get selected urban core selected = self.dlg.cboUrbanCore.currentText() if selected == '': return # get urban core code sel_core = self.dlg.cboUrbanCore.itemData(self.dlg.cboUrbanCore.currentIndex()) code = sel_core[2] # to know the index see the query that populate the combo records = [[-1, '']] # Set filter expression layer = self.plotLayer idx_field_code = layer.fieldNameIndex(self.PLOT_FIELD_CODE) idx_field_number = layer.fieldNameIndex(self.PLOT_FIELD_ADDRESS) idx_field_id = layer.fieldNameIndex('id') aux = self.PLOT_FIELD_CODE+"='"+str(code)+"'" # Check filter and existence of fields expr = QgsExpression(aux) if expr.hasParserError(): self.iface.messageBar().pushMessage(expr.parserErrorString() + ": " + aux, self.app_name, QgsMessageBar.WARNING, 10) return if idx_field_code == -1: message = self.tr("Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'". format(self.PLOT_FIELD_CODE, layer.name(), self.setting_file, 'PLOT_FIELD_CODE')) self.iface.messageBar().pushMessage(message, '', QgsMessageBar.WARNING) return if idx_field_number == -1: message = self.tr("Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'". format(self.PLOT_FIELD_ADDRESS, layer.name(), self.setting_file, 'PLOT_FIELD_ADDRESS')) self.iface.messageBar().pushMessage(message, '', QgsMessageBar.WARNING) return # Get a featureIterator from an expression: # Get features from the iterator and do something it = layer.getFeatures(QgsFeatureRequest(expr)) for feature in it: attrs = feature.attributes() plot_id = attrs[idx_field_id] field_number = attrs[idx_field_number] if not type(field_number) is QPyNullVariant: elem = [plot_id, field_number] records.append(elem) # Fill numbers combo records_sorted = sorted(records, key = operator.itemgetter(1)) self.dlg.cboPlot.blockSignals(True) self.dlg.cboPlot.clear() for record in records_sorted: self.dlg.cboPlot.addItem(record[1], record) self.dlg.cboPlot.blockSignals(False)
def processAlgorithm(self, parameters, context, feedback): layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) fieldName = self.parameterAsString(parameters, self.FIELD, context) operator = self.OPERATORS[self.parameterAsEnum(parameters, self.OPERATOR, context)] value = self.parameterAsString(parameters, self.VALUE, context) fields = layer.fields() idx = layer.fields().lookupField(fieldName) if idx < 0: raise QgsProcessingException(self.tr("Field '{}' was not found in layer").format(fieldName)) fieldType = fields[idx].type() if fieldType != QVariant.String and operator in self.STRING_OPERATORS: op = ''.join(['"%s", ' % o for o in self.STRING_OPERATORS]) raise QgsProcessingException( self.tr('Operators {0} can be used only with string fields.').format(op)) field_ref = QgsExpression.quotedColumnRef(fieldName) quoted_val = QgsExpression.quotedValue(value) if operator == 'is null': expression_string = '{} IS NULL'.format(field_ref) elif operator == 'is not null': expression_string = '{} IS NOT NULL'.format(field_ref) elif operator == 'begins with': expression_string = "{} LIKE '{}%'".format(field_ref, value) elif operator == 'contains': expression_string = "{} LIKE '%{}%'".format(field_ref, value) elif operator == 'does not contain': expression_string = "{} NOT LIKE '%{}%'".format(field_ref, value) else: expression_string = '{} {} {}'.format(field_ref, operator, quoted_val) method = self.parameterAsEnum(parameters, self.METHOD, context) if method == 0: behavior = QgsVectorLayer.SetSelection elif method == 1: behavior = QgsVectorLayer.AddToSelection elif method == 2: behavior = QgsVectorLayer.RemoveFromSelection elif method == 3: behavior = QgsVectorLayer.IntersectSelection expression = QgsExpression(expression_string) if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) layer.selectByExpression(expression_string, behavior) return {self.OUTPUT: parameters[self.INPUT]}
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 showExpressionsBuilder(self): context = self.param.expressionContext() dlg = QgsExpressionBuilderDialog(None, str(self.spnValue.value()), self, 'generic', context) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): try: val = float(exp.evaluate(context)) self.setValue(val) except: return
def showExpressionsBuilder(self): context = self.expressionContext() dlg = QgsExpressionBuilderDialog(None, self.spnValue.text(), self, 'generic', context) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): result = exp.evaluate(context) if not exp.hasEvalError(): try: self.spnValue.setValue(float(result)) except: pass
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 showExpressionsBuilder(self): context = createExpressionContext() dlg = QgsExpressionBuilderDialog(None, str(self.leText.text()), self, 'generic', context) context.popScope() values = self.modelParametersDialog.getAvailableValuesOfType(QgsProcessingParameterNumber, QgsProcessingOutputNumber) variables = {} for value in values: if isinstance(value, QgsProcessingModelAlgorithm.ChildParameterSource): if value.source() == QgsProcessingModelAlgorithm.ChildParameterSource.ModelParameter: name = value.parameterName() element = self.modelParametersDialog.model.parameterDefinition(name) desc = element.description() elif value.source() == QgsProcessingModelAlgorithm.ChildParameterSource.ChildOutput: name = "%s_%s" % (value.outputChildId(), value.outputName()) alg = self.modelParametersDialog.model.childAlgorithm(value.outputChildId()) out = alg.algorithm().outputDefinition(value.outputName()) desc = self.tr("Output '{0}' from algorithm '{1}'").format(out.description(), alg.description()) variables[name] = desc values = self.modelParametersDialog.getAvailableValuesOfType([QgsProcessingParameterFeatureSource, QgsProcessingParameterRasterLayer], [QgsProcessingOutputVectorLayer, QgsProcessingOutputRasterLayer]) for value in values: if isinstance(value, QgsProcessingModelAlgorithm.ChildParameterSource): if value.source() == QgsProcessingModelAlgorithm.ChildParameterSource.ModelParameter: name = value.parameterName() element = self.modelParametersDialog.model.parameterDefinition(name) desc = element.description() elif value.source() == QgsProcessingModelAlgorithm.ChildParameterSource.ChildOutput: name = "%s_%s" % (value.outputChildId(), value.outputName()) alg = self.modelParametersDialog.model.childAlgorithm(value.outputChildId()) out = alg.algorithm().outputDefinition(value.outputName()) desc = self.tr("Output '{0}' from algorithm '{1}'").format(out.description(), alg.description()) variables['%s_minx' % name] = self.tr("Minimum X of {0}").format(desc) variables['%s_miny' % name] = self.tr("Minimum Y of {0}").format(desc) variables['%s_maxx' % name] = self.tr("Maximum X of {0}").format(desc) variables['%s_maxy' % name] = self.tr("Maximum Y of {0}").format(desc) if isinstance(element, (QgsProcessingParameterRasterLayer, QgsProcessingOutputRasterLayer)): variables['%s_min' % name] = self.tr("Minimum value of {0}").format(desc) variables['%s_max' % name] = self.tr("Maximum value of {0}").format(desc) variables['%s_avg' % name] = self.tr("Mean value of {0}").format(desc) variables['%s_stddev' % name] = self.tr("Standard deviation of {0}").format(desc) for variable, desc in variables.items(): dlg.expressionBuilder().registerItem("Modeler", variable, "@" + variable, desc, highlightedItem=True) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): self.setValue(dlg.expressionText())
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) fieldName = self.getParameterValue(self.FIELD) operator = self.OPERATORS[self.getParameterValue(self.OPERATOR)] value = self.getParameterValue(self.VALUE) fields = layer.pendingFields() writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, layer.wkbType(), layer.crs()) idx = layer.fieldNameIndex(fieldName) fieldType = fields[idx].type() if fieldType != QVariant.String and operator in self.OPERATORS[-2:]: op = ''.join(['"%s", ' % o for o in self.OPERATORS[-2:]]) raise GeoAlgorithmExecutionException( self.tr('Operators %s can be used only with string fields.' % op)) if fieldType in [QVariant.Int, QVariant.Double]: progress.setInfo(self.tr('Numeric field')) expr = '"%s" %s %s' % (fieldName, operator, value) progress.setInfo(expr) elif fieldType == QVariant.String: progress.setInfo(self.tr('String field')) if operator not in self.OPERATORS[-2:]: expr = """"%s" %s '%s'""" % (fieldName, operator, value) elif operator == 'begins with': expr = """"%s" LIKE '%s%%'""" % (fieldName, value) elif operator == 'contains': expr = """"%s" LIKE '%%%s%%'""" % (fieldName, value) progress.setInfo(expr) elif fieldType in [QVariant.Date, QVariant.DateTime]: progress.setInfo(self.tr('Date field')) expr = """"%s" %s '%s'""" % (fieldName, operator, value) progress.setInfo(expr) else: raise GeoAlgorithmExecutionException( self.tr('Unsupported field type "%s"' % fields[idx].typeName())) expression = QgsExpression(expr) if not expression.hasParserError(): req = QgsFeatureRequest(expression) else: raise GeoAlgorithmExecutionException(expression.parserErrorString()) for f in layer.getFeatures(req): writer.addFeature(f) del writer
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) expression_string = self.getParameterValue(self.EXPRESSION) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs()) expression = QgsExpression(expression_string) if not expression.hasParserError(): req = QgsFeatureRequest().setFilterExpression(expression_string) else: raise GeoAlgorithmExecutionException(expression.parserErrorString()) for f in layer.getFeatures(req): writer.addFeature(f) del writer
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 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 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 shortest_path(self, start_point, end_point): """ Calculating shortest path using dijkstra algorithm """ args = [] start_point_id = start_point end_point_id = end_point args.append(start_point_id) args.append(end_point_id) self.rnode_id = [] self.rarc_id = [] rstart_point = None sql = "SELECT rid" sql += " FROM " + self.schema_name + ".v_anl_pgrouting_node" sql += " WHERE node_id = '" + start_point + "'" row = self.controller.get_row(sql) if row: rstart_point = int(row[0]) rend_point = None sql = "SELECT rid" sql += " FROM " + self.schema_name + ".v_anl_pgrouting_node" sql += " WHERE node_id = '" + end_point + "'" row = self.controller.get_row(sql) if row: rend_point = int(row[0]) # Check starting and end points if rstart_point is None or rend_point is None: message = "Start point or end point not found" self.controller.show_warning(message) return # Clear list of arcs and nodes - preparing for new profile sql = "SELECT * FROM public.pgr_dijkstra('SELECT id::integer, source, target, cost" sql += " FROM " + self.schema_name + ".v_anl_pgrouting_arc', " + str( rstart_point) + ", " + str(rend_point) + ", false" if self.version == '2': sql += ", false" elif self.version == '3': pass else: message = "You need to upgrade your version of pg_routing!" self.controller.show_info(message) return sql += ")" rows = self.controller.get_rows(sql) for i in range(0, len(rows)): if self.version == '2': self.rnode_id.append(str(rows[i][1])) self.rarc_id.append(str(rows[i][2])) elif self.version == '3': self.rnode_id.append(str(rows[i][2])) self.rarc_id.append(str(rows[i][3])) self.rarc_id.pop() self.arc_id = [] self.node_id = [] for n in range(0, len(self.rarc_id)): # convert arc_ids sql = "SELECT arc_id" sql += " FROM " + self.schema_name + ".v_anl_pgrouting_arc" sql += " WHERE id = '" + str(self.rarc_id[n]) + "'" row = self.controller.get_row(sql) if row: self.arc_id.append(str(row[0])) for m in range(0, len(self.rnode_id)): # convert node_ids sql = "SELECT node_id" sql += " FROM " + self.schema_name + ".v_anl_pgrouting_node" sql += " WHERE rid = '" + str(self.rnode_id[m]) + "'" row = self.controller.get_row(sql) if row: self.node_id.append(str(row[0])) # Select arcs of the shortest path if rows: # Build an expression to select them aux = "\"arc_id\" IN (" for i in range(len(self.arc_id)): aux += "'" + str(self.arc_id[i]) + "', " aux = aux[:-2] + ")" expr = QgsExpression(aux) if expr.hasParserError(): message = "Expression Error: " + str(expr.parserErrorString()) self.controller.show_warning(message) return # Loop which is pasing trough all layer of arc_group searching for feature for layer_arc in self.group_pointers_arc: it = layer_arc.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result id_list = [i.id() for i in it] # Select features with these id's layer_arc.selectByIds(id_list) # Select nodes of shortest path aux = "\"node_id\" IN (" for i in range(len(self.node_id)): aux += "'" + str(self.node_id[i]) + "', " aux = aux[:-2] + ")" expr = QgsExpression(aux) if expr.hasParserError(): message = "Expression Error: " + str(expr.parserErrorString()) self.controller.show_warning(message) return # Loop which is pasing trough all layers of node_group searching for feature for layer_node in self.group_pointers_node: it = layer_node.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result self.id_list = [i.id() for i in it] # Select features with these id's layer_node.selectByIds(self.id_list) if self.id_list != []: layer = layer_node center_widget = self.id_list[0] # Center profile (first node) canvas = self.iface.mapCanvas() layer.selectByIds([center_widget]) canvas.zoomToSelected(layer) self.tbl_list_arc = self.dlg.findChild(QListWidget, "tbl_list_arc") list_arc = [] # Clear list self.tbl_list_arc.clear() for i in range(len(self.arc_id)): item_arc = QListWidgetItem(self.arc_id[i]) self.tbl_list_arc.addItem(item_arc) list_arc.append(self.arc_id[i]) self.dlg.findChild(QPushButton, "btn_draw").clicked.connect( partial(self.paint_event, self.arc_id, self.node_id)) self.dlg.findChild(QPushButton, "btn_clear_profile").clicked.connect( self.clear_profile)
def _buildfromlayer(self, widget, layerconfig): layername = layerconfig['layer'] keyfield = layerconfig['key'] valuefield = layerconfig['value'] filterexp = layerconfig.get('filter', None) try: layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0] except IndexError: roam.utils.warning("Can't find layer {} in project".format(layername)) return keyfieldindex = layer.fieldNameIndex(keyfield) valuefieldindex = layer.fieldNameIndex(valuefield) if keyfieldindex == -1 or valuefieldindex == -1: roam.utils.warning("Can't find key or value column") return if self.allownulls: item = QStandardItem('(no selection)') item.setData(None, Qt.UserRole) self.listmodel.appendRow(item) if not filterexp and valuefieldindex == keyfieldindex: values = layer.uniqueValues(keyfieldindex) for value in values: value = nullconvert(value) item = QStandardItem(value) item.setData(value, Qt.UserRole) self.listmodel.appendRow(item) return attributes = {keyfieldindex, valuefieldindex} flags = QgsFeatureRequest.NoGeometry iconfieldindex = layer.fieldNameIndex('icon') if iconfieldindex > -1: attributes.add(iconfieldindex) expression = None if filterexp: expression = QgsExpression(filterexp) expression.prepare(layer.pendingFields()) if expression.hasParserError(): roam.utils.warning("Expression has parser error: {}".format(expression.parserErrorString())) return if expression.needsGeometry(): flags = QgsFeatureRequest.NoFlags for field in expression.referencedColumns(): index = layer.fieldNameIndex(field) attributes.add(index) request = QgsFeatureRequest().setFlags(flags).setSubsetOfAttributes(list(attributes)) for feature in layer.getFeatures(request): if expression and not expression.evaluate(feature): continue keyvalue = nullconvert(feature[keyfieldindex]) valuvalue = nullconvert(feature[valuefield]) try: path = feature[iconfieldindex] icon = QIcon(path) except KeyError: icon = QIcon() item = QStandardItem(unicode(keyvalue)) item.setData(unicode(valuvalue), Qt.UserRole) item.setIcon(icon) self.listmodel.appendRow(item)
def get_print( self, params: Dict[str, str], response: QgsServerResponse, project: QgsProject ) -> None: """ Get print document """ template = params.get("TEMPLATE") feature_filter = params.get("EXP_FILTER", None) scale = params.get("SCALE") scales = params.get("SCALES") output_format = parse_output_format(params.get("FORMAT", params.get("format"))) try: if not template: raise AtlasPrintException("TEMPLATE is required") if feature_filter: expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( "Expression is invalid: {}".format( expression.parserErrorString() ) ) if scale and scales: raise AtlasPrintException("SCALE and SCALES can not be used together.") if scale: try: scale = int(scale) except ValueError: raise AtlasPrintException("Invalid number in SCALE.") if scales: try: scales = [int(scale) for scale in scales.split(",")] except ValueError: raise AtlasPrintException("Invalid number in SCALES.") additional_params = { k: v for k, v in params.items() if k not in ( "TEMPLATE", "EXP_FILTER", "SCALE", "SCALES", "FORMAT", "MAP", "REQUEST", "SERVICE", ) } Logger().info( "Additional params: " + str(additional_params["PERMIT_REQUEST_ID"]) ) # When the project has OAPIF datasources, the sources must first be reloaded. # In order to prevent massive request to DB, a basic server side filter is added OAPIFRefresher.refresh_geocity_oapif_layers_for_current_atlas_feature( additional_params["PERMIT_REQUEST_ID"] ) Logger().info("Refreshed: " + str(additional_params["PERMIT_REQUEST_ID"])) output_path = print_layout( project=project, layout_name=params["TEMPLATE"], output_format=output_format, scale=scale, scales=scales, feature_filter=feature_filter, **additional_params, ) except AtlasPrintException as e: raise AtlasPrintError( 400, "ATLAS - Error from the user while generating the PDF: {}".format(e), ) except Exception: self.logger.critical( "Unhandled exception:\n{}".format(traceback.format_exc()) ) raise AtlasPrintError(500, "Internal 'atlasprint' service error") path = Path(output_path) if not path.exists(): raise AtlasPrintError(404, "ATLAS {} not found".format(output_format.name)) # Send PDF response.setHeader("Content-Type", output_format.value) response.setStatusCode(200) try: response.write(path.read_bytes()) path.unlink() except Exception: self.logger.critical( "Error occurred while reading {} file".format(output_format.name) ) raise
def print_layout( project: QgsProject, layout_name: str, output_format: OutputFormat, feature_filter: str = None, scales: list = None, scale: int = None, **kwargs, ): """Generate a PDF for an atlas or a report. :param project: The QGIS project. :type project: QgsProject :param layout_name: Name of the layout of the atlas or report. :type layout_name: basestring :param feature_filter: QGIS Expression to use to select the feature. It can return many features, a multiple pages PDF will be returned. This is required to print atlas, not report :type feature_filter: basestring :param scale: A scale to force in the atlas context. Default to None. :type scale: int :param scales: A list of predefined list of scales to force in the atlas context. Default to None. :type scales: list :param output_format: The output format, default to PDF if not provided. :return: Path to the PDF. :rtype: basestring """ canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas) bridge.setCanvasLayers() manager = project.layoutManager() master_layout = manager.layoutByName(layout_name) if output_format == OutputFormat.Svg: settings = QgsLayoutExporter.SvgExportSettings() elif output_format in (OutputFormat.Png, OutputFormat.Jpeg): settings = QgsLayoutExporter.ImageExportSettings() else: # PDF by default settings = QgsLayoutExporter.PdfExportSettings() atlas = None atlas_layout = None report_layout = None logger = Logger() if not master_layout: raise AtlasPrintException("Layout `{}` not found".format(layout_name)) if master_layout.layoutType() == QgsMasterLayoutInterface.PrintLayout: for _print_layout in manager.printLayouts(): if _print_layout.name() == layout_name: atlas_layout = _print_layout break atlas = atlas_layout.atlas() if not atlas.enabled(): raise AtlasPrintException("The layout is not enabled for an atlas") layer = atlas.coverageLayer() if feature_filter is None: raise AtlasPrintException( "EXP_FILTER is mandatory to print an atlas layout") feature_filter = optimize_expression(layer, feature_filter) expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( "Expression is invalid, parser error: {}".format( expression.parserErrorString())) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(project)) context.appendScope( QgsExpressionContextUtils.layoutScope(atlas_layout)) context.appendScope(QgsExpressionContextUtils.atlasScope(atlas)) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(context) if expression.hasEvalError(): raise AtlasPrintException( "Expression is invalid, eval error: {}".format( expression.evalErrorString())) atlas.setFilterFeatures(True) atlas.setFilterExpression(feature_filter) atlas.updateFeatures() if scale: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Fixed) atlas_layout.referenceMap().setScale(scale) if scales: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Predefined) settings.predefinedMapScales = scales if (not scales and atlas_layout.referenceMap().atlasScalingMode() == QgsLayoutItemMap.Predefined): use_project = project.useProjectScales() map_scales = project.mapScales() if not use_project or len(map_scales) == 0: logger.info( "Map scales not found in project, fetching predefined map scales in global config" ) map_scales = global_scales() settings.predefinedMapScales = map_scales elif master_layout.layoutType() == QgsMasterLayoutInterface.Report: report_layout = master_layout else: raise AtlasPrintException("The layout is not supported by the plugin") for key, value in kwargs.items(): found = False if atlas_layout: item = atlas_layout.itemById(key.lower()) if isinstance(item, QgsLayoutItemLabel): item.setText(value) found = True logger.info( 'Additional parameters "{key}" {found} in layout, value "{value}"'. format(key=key, found="found" if found else "not found", value=value)) file_name = "{}_{}.{}".format(clean_string(layout_name), uuid4(), output_format.name.lower()) export_path = Path(tempfile.gettempdir()).joinpath(file_name) Logger().info("Exporting the request in {} using {}".format( export_path, output_format.value)) if output_format in (OutputFormat.Png, OutputFormat.Jpeg): exporter = QgsLayoutExporter(atlas_layout or report_layout) result = exporter.exportToImage(str(export_path), settings) error = result_message(result) elif output_format in (OutputFormat.Svg, ): exporter = QgsLayoutExporter(atlas_layout or report_layout) result = exporter.exportToSvg(str(export_path), settings) error = result_message(result) else: # Default to PDF result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout, str(export_path), settings) # Let's override error message _ = error error = result_message(result) if result != QgsLayoutExporter.Success: raise Exception("Export not generated in QGIS exporter {} : {}".format( export_path, error)) if not export_path.is_file(): logger.warning( "No error from QGIS Exporter, but the file does not exist.\n" "Message from QGIS exporter : {}\n" "File path : {}\n".format(error, export_path)) raise Exception( "Export OK from QGIS, but file not found on the file system : {}". format(export_path)) return export_path
def hydrometer_get_hydrometers(self): """ Populate hydrometers depending on selected connec """ # Get selected connec selected = utils_giswater.getWidgetText(self.dlg.hydrometer_connec) # If any conenc selected, get again all hydrometers if selected == 'null': self.populate_combo('hydrometer_layer', self.dlg.hydrometer_id, self.params['hydrometer_field_urban_propierties_code'], self.params['hydrometer_field_code']) return # Get connec_id elem = self.dlg.hydrometer_connec.itemData(self.dlg.hydrometer_connec.currentIndex()) code = elem[0] # to know the index see the query that populate the combo records = [[-1, '']] # Set filter expression layer = self.layers['hydrometer_layer'] idx_field_code = layer.fieldNameIndex(self.params['hydrometer_field_urban_propierties_code']) idx_field_number = layer.fieldNameIndex(self.params['hydrometer_field_code']) aux = self.params['hydrometer_field_urban_propierties_code'] + " = '" + str(code) + "'" # Check filter and existence of fields expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return if idx_field_code == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['hydrometer_field_urban_propierties_code'], layer.name(), self.setting_file, 'hydrometer_field_urban_propierties_code') self.controller.show_warning(message) return if idx_field_number == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['hydrometer_field_code'], layer.name(), self.setting_file, 'hydrometer_field_code') self.controller.show_warning(message) return # Get a featureIterator from an expression: # Get features from the iterator and do something it = layer.getFeatures(QgsFeatureRequest(expr)) for feature in it: attrs = feature.attributes() field_number = attrs[idx_field_number] if not type(field_number) is QPyNullVariant: elem = [code, field_number] records.append(elem) # Fill hydrometers records_sorted = sorted(records, key=operator.itemgetter(1)) self.dlg.hydrometer_id.blockSignals(True) self.dlg.hydrometer_id.clear() hydrometer_list = [] #hydrometer_list.append('') for record in records_sorted: self.dlg.hydrometer_id.addItem(str(record[1]), record) if record[1] != '': hydrometer_list.append(str(record[1])) self.set_model_by_list(hydrometer_list, self.dlg.hydrometer_id) self.hydrometer_zoom(self.params['hydrometer_urban_propierties_field_code'], self.dlg.hydrometer_connec) self.dlg.hydrometer_id.blockSignals(False)
def address_get_numbers(self, combo, field_code, fill_combo=False): """ Populate civic numbers depending on value of selected @combo. Build an expression with @field_code """ # Get selected street selected = utils_giswater.getWidgetText(combo) if selected == 'null': return # Get street code elem = combo.itemData(combo.currentIndex()) code = elem[0] # to know the index see the query that populate the combo records = [[-1, '']] # Set filter expression layer = self.layers['portal_layer'] idx_field_code = layer.fieldNameIndex(field_code) idx_field_number = layer.fieldNameIndex(self.params['portal_field_number']) aux = field_code + " = '" + str(code) + "'" # Check filter and existence of fields expr = QgsExpression(aux) if expr.hasParserError(): message = expr.parserErrorString() + ": " + aux self.controller.show_warning(message) return if idx_field_code == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['portal_field_code'], layer.name(), self.setting_file, 'portal_field_code') self.controller.show_warning(message) return if idx_field_number == -1: message = "Field '{}' not found in layer '{}'. Open '{}' and check parameter '{}'" \ .format(self.params['portal_field_number'], layer.name(), self.setting_file, 'portal_field_number') self.controller.show_warning(message) return self.dlg.address_number.blockSignals(True) self.dlg.address_number.clear() if fill_combo: it = layer.getFeatures(QgsFeatureRequest(expr)) for feature in it: attrs = feature.attributes() field_number = attrs[idx_field_number] if not type(field_number) is QPyNullVariant: elem = [code, field_number] records.append(elem) # Fill numbers combo records_sorted = sorted(records, key=operator.itemgetter(1)) for record in records_sorted: self.dlg.address_number.addItem(str(record[1]), record) self.dlg.address_number.blockSignals(False) # Get a featureIterator from an expression: # Select featureswith the ids obtained it = layer.getFeatures(QgsFeatureRequest(expr)) ids = [i.id() for i in it] layer.selectByIds(ids) # Zoom to selected feature of the layer self.zoom_to_selected_features(layer)
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 responseComplete(self): ''' Send new response ''' self.request = self.serverIface.requestHandler() params = self.request.parameterMap() # Check if needed params are passed # If not, do not change QGIS Server response if params['SERVICE'].lower() != 'wms': return # Check if getprintatlas request. If not, just send the response if 'REQUEST' not in params or params['REQUEST'].lower() not in [ 'getprintatlas', 'getcapabilitiesatlas' ]: return # Get capabilities if params['REQUEST'].lower() == 'getcapabilitiesatlas': body = {'status': 'success', 'metadata': self.metadata} self.setJsonResponse('200', body) return # Check if needed params are set if 'TEMPLATE' not in params or 'FORMAT' not in params or 'DPI' not in params or 'MAP' not in params or 'EXP_FILTER' not in params: body = { 'status': 'fail', 'message': 'Missing parameters: TEMPLATE, FORMAT, DPI, MAP, EXP_FILTER are required ' } self.setJsonResponse('200', body) return self.project_path = params['MAP'] self.composer_name = params['TEMPLATE'] self.feature_filter = params['EXP_FILTER'] # check expression qExp = QgsExpression(self.feature_filter) if not qExp.hasParserError(): qReq = QgsFeatureRequest(qExp) qReq.setLimit(1) ok = True else: body = { 'status': 'fail', 'message': 'An error occured while parsing the given expression: %s' % qExp.parserErrorString() } syslog.syslog( syslog.LOG_ERR, "ATLAS - ERROR EXPRESSION: %s" % qExp.parserErrorString()) self.setJsonResponse('200', body) return try: pdf = self.print_atlas(project_path=self.project_path, composer_name=self.composer_name, predefined_scales=self.predefined_scales, feature_filter=self.feature_filter) except: pdf = None if not pdf: body = { 'status': 'fail', 'message': 'ATLAS - Error while generating the PDF' } QgsMessageLog.logMessage("ATLAS - No PDF generated in %s" % pdf) self.setJsonResponse('200', body) return # Send PDF self.request.clearHeaders() self.request.setInfoFormat('application/pdf') self.request.setHeader('Content-type', 'application/pdf') self.request.setHeader('Status', '200') self.request.clearBody() try: with open(pdf, 'rb') as f: loads = f.readlines() ba = QByteArray(b''.join(loads)) self.request.appendBody(ba) except: body = { 'status': 'fail', 'message': 'Error occured while reading PDF file', } self.setJsonResponse('200', body) finally: os.remove(pdf) return
def get_print(self, params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Get print document """ template = params.get('TEMPLATE') feature_filter = params.get('EXP_FILTER', None) scale = params.get('SCALE') scales = params.get('SCALES') try: if not template: raise AtlasPrintException('TEMPLATE is required') if feature_filter: expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( 'Expression is invalid: {}'.format( expression.parserErrorString())) if scale and scales: raise AtlasPrintException( 'SCALE and SCALES can not be used together.') if scale: try: scale = int(scale) except ValueError: raise AtlasPrintException('Invalid number in SCALE.') if scales: try: scales = [int(scale) for scale in scales.split(',')] except ValueError: raise AtlasPrintException('Invalid number in SCALES.') additional_params = { k: v for k, v in params.items() if k not in ['TEMPLATE', 'EXP_FILTER', 'SCALE', 'SCALES'] } pdf_path = print_layout(project=project, layout_name=params['TEMPLATE'], scale=scale, scales=scales, feature_filter=feature_filter, **additional_params) except AtlasPrintException as e: raise AtlasPrintError( 400, 'ATLAS - Error from the user while generating the PDF: {}'. format(e)) except Exception: self.logger.critical("Unhandled exception:\n{}".format( traceback.format_exc())) raise AtlasPrintError(500, "Internal 'atlasprint' service error") path = Path(pdf_path) if not path.exists(): raise AtlasPrintError(404, "ATLAS PDF not found") # Send PDF response.setHeader('Content-Type', 'application/pdf') response.setStatusCode(200) try: response.write(path.read_bytes()) path.unlink() except Exception: self.logger.critical("Error occured while reading PDF file") raise
def showExpressionsBuilder(self): context = createExpressionContext() dlg = QgsExpressionBuilderDialog(None, str(self.leText.text()), self, 'generic', context) context.popScope() values = self.modelParametersDialog.getAvailableValuesOfType( QgsProcessingParameterNumber, QgsProcessingOutputNumber) variables = {} for value in values: if isinstance(value, QgsProcessingModelAlgorithm.ChildParameterSource): if value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ModelParameter: name = value.parameterName() element = self.modelParametersDialog.model.parameterDefinition( name) desc = element.description() elif value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ChildOutput: name = "%s_%s" % (value.outputChildId(), value.outputName()) alg = self.modelParametersDialog.model.childAlgorithm( value.outputChildId()) out = alg.algorithm().outputDefinition(value.outputName()) desc = self.tr("Output '{0}' from algorithm '{1}'").format( out.description(), alg.description()) variables[name] = desc values = self.modelParametersDialog.getAvailableValuesOfType([ QgsProcessingParameterFeatureSource, QgsProcessingParameterRasterLayer ], [QgsProcessingOutputVectorLayer, QgsProcessingOutputRasterLayer]) for value in values: if isinstance(value, QgsProcessingModelAlgorithm.ChildParameterSource): if value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ModelParameter: name = value.parameterName() element = self.modelParametersDialog.model.parameterDefinition( name) desc = element.description() elif value.source( ) == QgsProcessingModelAlgorithm.ChildParameterSource.ChildOutput: name = "%s_%s" % (value.outputChildId(), value.outputName()) alg = self.modelParametersDialog.model.childAlgorithm( value.outputChildId()) out = alg.algorithm().outputDefinition(value.outputName()) desc = self.tr("Output '{0}' from algorithm '{1}'").format( out.description(), alg.description()) variables['%s_minx' % name] = self.tr("Minimum X of {0}").format(desc) variables['%s_miny' % name] = self.tr("Minimum Y of {0}").format(desc) variables['%s_maxx' % name] = self.tr("Maximum X of {0}").format(desc) variables['%s_maxy' % name] = self.tr("Maximum Y of {0}").format(desc) if isinstance(element, (QgsProcessingParameterRasterLayer, QgsProcessingOutputRasterLayer)): variables['%s_min' % name] = self.tr("Minimum value of {0}").format(desc) variables['%s_max' % name] = self.tr("Maximum value of {0}").format(desc) variables['%s_avg' % name] = self.tr("Mean value of {0}").format(desc) variables['%s_stddev' % name] = self.tr( "Standard deviation of {0}").format(desc) for variable, desc in variables.items(): dlg.expressionBuilder().registerItem("Modeler", variable, "@" + variable, desc, highlightedItem=True) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): self.setValue(dlg.expressionText())
def export_all_features(self): pdf_painter = None """Export map to pdf atlas style (one page per feature)""" if VRP_DEBUG is True: QgsMessageLog.logMessage(u'exporting map', DLG_CAPTION) try: result = self.__delete_pdf() if not result is None: return result ids = [] exp = QgsExpression(self.feature_filter) if exp.hasParserError(): raise Exception(exp.parserErrorString()) exp.prepare(self.coverage_layer.pendingFields()) for feature in self.coverage_layer.getFeatures(): value = exp.evaluate(feature) if exp.hasEvalError(): raise ValueError(exp.evalErrorString()) if bool(value): if VRP_DEBUG is True: QgsMessageLog.logMessage(u'export map, feature id:{0}'.format(feature.id()), DLG_CAPTION) ids.append(feature.id()) self.coverage_layer.select(ids) bbox = self.coverage_layer.boundingBoxOfSelected() self.canvas.zoomToSelected(self.coverage_layer) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox:{0}'.format(bbox.toString()), DLG_CAPTION) #self.map_renderer.setExtent(bbox) #self.map_renderer.updateScale() #read plotlayout composition = QgsComposition(self.map_renderer) self.composition = composition composition.setPlotStyle(QgsComposition.Print) error, xml_doc = self.__read_template() if not error is None: return error if composition.loadFromTemplate(xml_doc) is False: return u'Konnte Template nicht laden!\n{0}'.format(self.template_qpt) #read textinfo layout self.comp_textinfo = QgsComposition(self.map_renderer) self.comp_textinfo.setPlotStyle(QgsComposition.Print) error, xml_doc = self.__read_template(True) if not error is None: return error if self.comp_textinfo.loadFromTemplate(xml_doc) is False: return u'Konnte Template nicht laden!\n{0}'.format(self.settings.textinfo_layout()) new_ext = bbox if QGis.QGIS_VERSION_INT > 20200: compmaps = self.__get_items(QgsComposerMap) if len(compmaps) < 1: return u'Kein Kartenfenster im Layout vorhanden!' compmap = compmaps[0] else: if len(composition.composerMapItems()) < 1: return u'Kein Kartenfenster im Layout vorhanden!' compmap = composition.composerMapItems()[0] self.composermap = compmap #self.composermap.setPreviewMode(QgsComposerMap.Render) #self.composermap.setPreviewMode(QgsComposerMap.Rectangle) #taken from QgsComposerMap::setNewAtlasFeatureExtent (not yet available in QGIS 2.0) #http://www.qgis.org/api/qgscomposermap_8cpp_source.html#l00610 old_ratio = compmap.rect().width() / compmap.rect().height() new_ratio = new_ext.width() / new_ext.height() if old_ratio < new_ratio: new_height = new_ext.width() / old_ratio delta_height = new_height - new_ext.height() new_ext.setYMinimum( bbox.yMinimum() - delta_height / 2) new_ext.setYMaximum(bbox.yMaximum() + delta_height / 2) else: new_width = old_ratio * new_ext.height() delta_width = new_width - new_ext.width() new_ext.setXMinimum(bbox.xMinimum() - delta_width / 2) new_ext.setXMaximum(bbox.xMaximum() + delta_width / 2) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox old:{0}'.format(compmap.extent().toString()), DLG_CAPTION) compmap.setNewExtent(new_ext) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox new:{0}'.format(compmap.extent().toString()), DLG_CAPTION) #round up to next 1000 compmap.setNewScale(math.ceil((compmap.scale()/1000.0)) * 1000.0) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox new (after scale):{0}'.format(compmap.extent().toString()), DLG_CAPTION) #add ORTHO after new extent -> performance if not self.ortho is None: self.ortho_lyr = self.__add_raster_layer(self.ortho, self.lyrname_ortho) self.__reorder_layers() self.comp_leg = self.__get_items(QgsComposerLegend) self.comp_lbl = self.__get_items(QgsComposerLabel) self.__update_composer_items(self.settings.dkm_gemeinde(self.gem_name)['lyrnamegstk']) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'paperWidth:{0} paperHeight:{1}'.format(composition.paperWidth(), composition.paperHeight()), DLG_CAPTION) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(self.pdf_map) printer.setPaperSize(QSizeF(composition.paperWidth(), composition.paperHeight()), QPrinter.Millimeter) printer.setFullPage(True) printer.setColorMode(QPrinter.Color) printer.setResolution(composition.printResolution()) pdf_painter = QPainter(printer) paper_rect_pixel = printer.pageRect(QPrinter.DevicePixel) paper_rect_mm = printer.pageRect(QPrinter.Millimeter) QgsPaintEngineHack.fixEngineFlags(printer.paintEngine()) #DKM only if len(self.themen) < 1: composition.render(pdf_painter, paper_rect_pixel, paper_rect_mm) else: self.statistics = OrderedDict() try: pass #lyr = QgsVectorLayer('/home/bergw/VoGIS-Raumplanung-Daten/Geodaten/Raumplanung/Flaechenwidmung/Dornbirn/Flaechenwidmungsplan/fwp_flaeche.shp', 'flaeiw', 'ogr') #lyr.loadNamedStyle('/home/bergw/VoGIS-Raumplanung-Daten/Geodaten/Raumplanung/Flaechenwidmung/Vorarlberg/Flaechenwidmungsplan/fwp_flaeche.qml') #QgsMapLayerRegistry.instance().addMapLayer(lyr) except: QgsMessageLog.logMessage('new lyr:{0}'.format(sys.exc_info()[0]), DLG_CAPTION) #QgsMapLayerRegistry.instance().addMapLayer(lyr) cntr = 0 for thema, sub_themen in self.themen.iteritems(): if VRP_DEBUG is True: QgsMessageLog.logMessage('drucke Thema:{0}'.format(thema.name), DLG_CAPTION) if sub_themen is None: layers = self.__add_layers(thema) self.__calculate_statistics(thema, thema, layers) #no qml -> not visible -> means no map if self.__at_least_one_visible(layers) is True: if cntr > 0: printer.newPage() self.__reorder_layers() self.__update_composer_items(thema.name, layers=layers) composition.renderPage(pdf_painter, 0) QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) cntr += 1 else: QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) if not sub_themen is None: for sub_thema in sub_themen: if VRP_DEBUG is True: QgsMessageLog.logMessage(u'drucke SubThema:{0}'.format(sub_thema.name), DLG_CAPTION) layers = self.__add_layers(sub_thema) self.__calculate_statistics(thema, sub_thema, layers) #no qml -> not visible -> means no map if self.__at_least_one_visible(layers) is True: if cntr > 0: printer.newPage() self.__reorder_layers() self.__update_composer_items(thema.name, subthema=sub_thema.name, layers=layers) composition.renderPage(pdf_painter, 0) QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) cntr += 1 else: QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) #output statistics if len(self.statistics) > 0: printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter) tabelle = self.__get_item_byid(self.comp_textinfo, 'TABELLE') if tabelle is None: self.iface.messageBar().pushMessage(u'Layout (Textinfo): Kein Textelement mit ID "TABELLE" vorhanden.', QgsMessageBar.CRITICAL) else: try: str_flaechen = '' idx = 0 for gnr, stats in self.statistics.iteritems(): comma = ', ' if idx > 0 else '' str_flaechen += u'{0}{1} ({2:.2f}m²)'.format(comma, gnr, stats[0].flaeche) idx += 1 lbls = self.__get_items(QgsComposerLabel, self.comp_textinfo) self.__update_composer_items('', labels=lbls, gnrflaeche=str_flaechen) html = tabelle.text() html += u'<table>' #gnrcnt = 0 for gnr, stats in self.statistics.iteritems(): #if gnrcnt > 0: # html += u'<tr class="abstand"><td> </td><td> </td><td> </td></tr>' html += u'<tr><th class="gnr"></th><th class="gnr">{0}</th><th class="gnr"></th></tr>'.format(gnr) #html += u'<tr class="abstand"><td> </td><td> </td><td> </td></tr>' curr_thema = '' for stat in stats: if stat.thema != curr_thema: html += u'<tr><th class="thema"></th><th class="thema">{0}</th><th class="thema"></th></tr>'.format(stat.thema) curr_thema = stat.thema for thema, subthema in stat.subthemen.iteritems(): for quelle in subthema: html += u'<tr><td class="col1">{0}</td>'.format(quelle.name) attr_val = '' attr_area = '' for text, area in quelle.txt_area.iteritems(): attr_val += u'{0}<br />'.format(text) attr_area += u'{0:.2f}m² <br />'.format(area) html += u'<td class="col2">{0}</td><td class="col3">{1}</td></tr>'.format(attr_val, attr_area) #gnrcnt += 1 html += u'</table>' tabelle.setText(html) printer.newPage() self.comp_textinfo.renderPage(pdf_painter, 0) except: msg = 'Statistikausgabe:\n\n{0}'.format(traceback.format_exc()) QgsMessageLog.logMessage(msg, DLG_CAPTION) self.iface.messageBar().pushMessage(msg, QgsMessageBar.CRITICAL) except: msg = 'export pdf (catch all):\n\n{0}'.format(traceback.format_exc()) QgsMessageLog.logMessage(msg, DLG_CAPTION) self.iface.messageBar().pushMessage(msg.replace(u'\n', u''), QgsMessageBar.CRITICAL) return msg finally: #end pdf if not pdf_painter is None: pdf_painter.end() return None
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) expression = QgsExpression( self.parameterAsString(parameters, self.EXPRESSION, context)) if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) expressionContext = self.createExpressionContext( parameters, context, source) expression.prepare(expressionContext) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) total = 100.0 / source.featureCount() if source.featureCount() else 0 current_progress = 0 for current, f in enumerate(source.getFeatures()): if feedback.isCanceled(): break if not f.hasGeometry(): continue current_progress = total * current feedback.setProgress(current_progress) expressionContext.setFeature(f) value = expression.evaluate(expressionContext) if expression.hasEvalError(): feedback.pushInfo( self.tr('Evaluation error for feature ID {}: {}').format( f.id(), expression.evalErrorString())) continue fGeom = f.geometry() engine = QgsGeometry.createGeometryEngine(fGeom.constGet()) engine.prepareGeometry() bbox = fGeom.boundingBox() if strategy == 0: pointCount = int(value) else: pointCount = int(round(value * da.measureArea(fGeom))) if pointCount == 0: feedback.pushInfo( "Skip feature {} as number of points for it is 0.".format( f.id())) continue index = QgsSpatialIndex() points = dict() nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 feature_total = total / pointCount if pointCount else 1 random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) if engine.contains(geom.constGet()) and \ vector.checkMinDistance(p, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.insertFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(current_progress + int(nPoints * feature_total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr('Could not generate requested number of random ' 'points. Maximum number of attempts exceeded.')) feedback.setProgress(100) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = dataobjects.getObjectFromUri(layer) provider = layer.dataProvider() fields = [] expressions = [] for field_def in mapping: fields.append( QgsField(name=field_def['name'], type=field_def['type'], len=field_def['length'], prec=field_def['precision'])) expression = QgsExpression(field_def['expression']) if expression.hasParserError(): raise GeoAlgorithmExecutionException( self.tr(u'Parser error in expression "{}": {}').format( unicode(field_def['expression']), unicode(expression.parserErrorString()))) expression.prepare(provider.fields()) if expression.hasEvalError(): raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}').format( unicode(field_def['expression']), unicode(expression.evalErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, provider.geometryType(), layer.crs()) # Create output vector layer with new attributes error = '' calculationSuccess = True inFeat = QgsFeature() outFeat = QgsFeature() features = vector.features(layer) count = len(features) for current, inFeat in enumerate(features): rownum = current + 1 outFeat.setGeometry(inFeat.geometry()) attrs = [] for i in xrange(0, len(mapping)): field_def = mapping[i] expression = expressions[i] expression.setCurrentRowNumber(rownum) value = expression.evaluate(inFeat) if expression.hasEvalError(): calculationSuccess = False error = expression.evalErrorString() break attrs.append(value) outFeat.setAttributes(attrs) writer.addFeature(outFeat) current += 1 progress.setPercentage(100 * current / float(count)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation' ' string:\n') + error)
def save_vector_data(self, metadata_layer, post_layer_data, has_transactions, post_save_signal=True, **kwargs): """Save vector editing data :param metadata_layer: metadata of the layer being edited :type metadata_layer: MetadataVectorLayer :param post_layer_data: post data with 'add', 'delete' etc. :type post_layer_data: dict :param has_transactions: true if the layer support transactions :type has_transactions: bool :param post_save_signal: if this is a post_save_signal call, defaults to True :type post_save_signal: bool, optional """ # Check atomic capabilities for validation # ----------------------------------------------- #for mode_editing in (EDITING_POST_DATA_ADDED, EDITING_POST_DATA_UPDATED, EDITING_POST_DATA_DELETED): # try to get layer model object from metatada_layer layer = getattr(metadata_layer, 'layer', self.layer) if EDITING_POST_DATA_ADDED in post_layer_data and len( post_layer_data[EDITING_POST_DATA_ADDED]) > 0: if not self.request.user.has_perm('qdjango.add_feature', layer): raise ValidationError( _('Sorry but your user doesn\'t has \'Add Feature\' capability' )) if EDITING_POST_DATA_DELETED in post_layer_data and len( post_layer_data[EDITING_POST_DATA_DELETED]) > 0: if not self.request.user.has_perm('qdjango.delete_feature', layer): raise ValidationError( _('Sorry but your user doesn\'t has \'Delete Feature\' capability' )) if EDITING_POST_DATA_UPDATED in post_layer_data and len( post_layer_data[EDITING_POST_DATA_UPDATED]) > 0: if not self.request.user.has_perm('qdjango.change_feature', layer) and \ not self.request.user.has_perm('qdjango.change_attr_feature', layer): raise ValidationError( _('Sorry but your user doesn\'t has \'Change or Change Attributes Features\' capability' )) # get initial featurelocked metadata_layer.lock.getInitialFeatureLockedIds() # get lockids from client metadata_layer.lock.setLockeFeaturesFromClient( post_layer_data['lockids']) # data for response insert_ids = list() lock_ids = list() # FIXME: check this out # for add check if is a metadata_layer and referenced field is a pk is_referenced_field_is_pk = 'referenced_layer_insert_ids' in kwargs and kwargs['referenced_layer_insert_ids'] \ and hasattr(metadata_layer, 'referenced_field_is_pk') \ and metadata_layer.referenced_field_is_pk # Get the layer qgis_layer = metadata_layer.qgis_layer for mode_editing in (EDITING_POST_DATA_ADDED, EDITING_POST_DATA_UPDATED): if mode_editing in post_layer_data: for geojson_feature in post_layer_data[mode_editing]: data_extra_fields = {'feature': geojson_feature} # Clear any old error qgis_layer.dataProvider().clearErrors() # add media data self.add_media_property(geojson_feature, metadata_layer) # for GEOSGeometry of Django 2.2 it must add crs to feature if is not set if a geo feature if metadata_layer.geometry_type != QGIS_LAYER_TYPE_NO_GEOM: if geojson_feature[ 'geometry'] and 'crs' not in geojson_feature[ 'geometry']: geojson_feature['geometry'][ 'crs'] = "{}:{}".format( self.layer.project.group.srid.auth_name, self.layer.project.group.srid.auth_srid) # reproject data if necessary if kwargs[ 'reproject'] and metadata_layer.geometry_type != QGIS_LAYER_TYPE_NO_GEOM: self.reproject_feature(geojson_feature, to_layer=True) # case relation data ADD, if father referenced field is pk if is_referenced_field_is_pk: for newid in kwargs['referenced_layer_insert_ids']: if geojson_feature['properties'][ metadata_layer. referencing_field] == newid['clientid']: geojson_feature['properties'][ metadata_layer. referencing_field] = newid['id'] if mode_editing == EDITING_POST_DATA_UPDATED: # control feature locked if not metadata_layer.lock.checkFeatureLocked( geojson_feature['id']): raise Exception( self.no_more_lock_feature_msg.format( geojson_feature['id'], metadata_layer.client_var)) # Send for validation # Note that this may raise a validation error pre_save_maplayer.send(self, layer_metadata=metadata_layer, mode=mode_editing, data=data_extra_fields, user=self.request.user) # Validate and save try: original_feature = None feature = QgsFeature(qgis_layer.fields()) if mode_editing == EDITING_POST_DATA_UPDATED: # add patch for shapefile type, geojson_feature['id'] id int() instead of str() # path to fix into QGIS api geojson_feature[ 'id'] = get_layer_fids_from_server_fids( [str(geojson_feature['id'])], qgis_layer)[0] feature.setId(geojson_feature['id']) # Get feature from data provider before update original_feature = qgis_layer.getFeature( geojson_feature['id']) # We use this feature for geometry parsing only: imported_feature = QgsJsonUtils.stringToFeatureList( json.dumps(geojson_feature), qgis_layer.fields(), None # UTF8 codec )[0] feature.setGeometry(imported_feature.geometry()) # There is something wrong in QGIS 3.10 (fixed in later versions) # so, better loop through the fields and set attributes individually for name, value in geojson_feature['properties'].items( ): feature.setAttribute(name, value) # Loop again for set expressions value: # For update store expression result to use later into update condition field_expresion_values = {} for qgis_field in qgis_layer.fields(): if qgis_field.defaultValueDefinition().expression( ): exp = QgsExpression( qgis_field.defaultValueDefinition( ).expression()) if exp.rootNode().nodeType( ) != QgsExpressionNode.ntLiteral and not exp.hasParserError( ): context = QgsExpressionContextUtils.createFeatureBasedContext( feature, qgis_layer.fields()) context.appendScopes( QgsExpressionContextUtils. globalProjectLayerScopes(qgis_layer)) result = exp.evaluate(context) if not exp.hasEvalError(): feature.setAttribute( qgis_field.name(), result) # Check update if expression default value has to run also on update e not # only on insert newone if qgis_field.defaultValueDefinition( ).applyOnUpdate(): field_expresion_values[ qgis_field.name()] = result elif qgis_field.typeName() in ('date', 'datetime', 'time'): if qgis_field.typeName() == 'date': qtype = QDate elif qgis_field.typeName() == 'datetime': qtype = QDateTime else: qtype = QTime field_idx = qgis_layer.fields().indexFromName( qgis_field.name()) options = qgis_layer.editorWidgetSetup( field_idx).config() if 'field_iso_format' in options and not options[ 'field_iso_format']: if geojson_feature['properties'][ qgis_field.name()]: value = qtype.fromString( geojson_feature['properties'][ qgis_field.name()], options['field_format']) feature.setAttribute( qgis_field.name(), value) # Call validator! errors = feature_validator(feature, metadata_layer.qgis_layer) if errors: raise ValidationError(errors) # Save the feature if mode_editing == EDITING_POST_DATA_ADDED: if has_transactions: if not qgis_layer.addFeature(feature): raise Exception( _('Error adding feature: %s') % ', '.join(qgis_layer.dataProvider(). errors())) else: if not qgis_layer.dataProvider().addFeature( feature): raise Exception( _('Error adding feature: %s') % ', '.join(qgis_layer.dataProvider(). errors())) # Patch for Spatialite provider on pk if qgis_layer.dataProvider().name( ) == 'spatialite': pks = qgis_layer.primaryKeyAttributes() if len(pks) > 1: raise Exception( _(f'Error adding feature on Spatialite provider: ' f'layer {qgis_layer.id()} has more than one pk column' )) # update pk attribute: feature.setAttribute( pks[0], server_fid(feature, qgis_layer.dataProvider())) elif mode_editing == EDITING_POST_DATA_UPDATED: attr_map = {} for name, value in geojson_feature[ 'properties'].items(): if name in qgis_layer.dataProvider( ).fieldNameMap(): if name in field_expresion_values: value = field_expresion_values[name] attr_map[qgis_layer.dataProvider(). fieldNameMap()[name]] = value if has_transactions: if not qgis_layer.changeAttributeValues( geojson_feature['id'], attr_map): raise Exception( _('Error changing attribute values: %s' ) % ', '.join(qgis_layer.dataProvider(). errors())) # Check for errors because of https://github.com/qgis/QGIS/issues/36583 if qgis_layer.dataProvider().errors(): raise Exception(', '.join( qgis_layer.dataProvider().errors())) if not feature.geometry().isNull( ) and not qgis_layer.changeGeometry( geojson_feature['id'], feature.geometry()): raise Exception( _('Error changing geometry: %s') % ', '.join(qgis_layer.dataProvider(). errors())) else: if not qgis_layer.dataProvider( ).changeAttributeValues( {geojson_feature['id']: attr_map}): raise Exception( _('Error changing attribute values: %s' ) % ', '.join(qgis_layer.dataProvider(). errors())) if not feature.geometry().isNull( ) and not qgis_layer.dataProvider( ).changeGeometryValues({ geojson_feature['id']: feature.geometry() }): raise Exception( _('Error changing geometry: %s') % ', '.join(qgis_layer.dataProvider(). errors())) to_res = {} to_res_lock = {} if mode_editing == EDITING_POST_DATA_ADDED: # to exclude QgsFormater used into QgsJsonjExporter is necessary build by hand single json feature ex = QgsJsonExporter(qgis_layer) ex.setIncludeAttributes(False) fnames = [f.name() for f in feature.fields()] jfeature = json.loads( ex.exportFeature( feature, dict(zip(fnames, feature.attributes())))) to_res.update({ 'clientid': geojson_feature['id'], # This might be the internal QGIS feature id (< 0) 'id': server_fid( feature, metadata_layer.qgis_layer.dataProvider()), 'properties': jfeature['properties'] }) # lock news: to_res_lock = metadata_layer.lock.modelLock2dict( metadata_layer.lock.lockFeature(server_fid( feature, metadata_layer.qgis_layer.dataProvider()), save=True)) if bool(to_res): insert_ids.append(to_res) if bool(to_res_lock): lock_ids.append(to_res_lock) # Send post vase signal post_save_maplayer.send( self, layer_metadata=metadata_layer, mode=mode_editing, data=data_extra_fields, user=self.request.user, original_feature=original_feature, to_res=to_res) except ValidationError as ex: raise ValidationError({ metadata_layer.client_var: { mode_editing: { 'id': geojson_feature['id'], 'fields': ex.detail, } } }) except Exception as ex: raise ValidationError({ metadata_layer.client_var: { mode_editing: { 'id': geojson_feature['id'], 'fields': str(ex), } } }) # erasing feature if to do if EDITING_POST_DATA_DELETED in post_layer_data: fids = post_layer_data[EDITING_POST_DATA_DELETED] # get feature fids from server fids from client. fids = get_layer_fids_from_server_fids([str(id) for id in fids], qgis_layer) for feature_id in fids: # control feature locked if not metadata_layer.lock.checkFeatureLocked(str(feature_id)): raise Exception( self.no_more_lock_feature_msg.format( feature_id, metadata_layer.client_var)) # Get feature to delete ex = QgsJsonExporter(qgis_layer) deleted_feature = ex.exportFeature( qgis_layer.getFeature(feature_id)) pre_delete_maplayer.send(self, layer_metatada=metadata_layer, data=deleted_feature, user=self.request.user) qgis_layer.dataProvider().clearErrors() if has_transactions: if not qgis_layer.deleteFeatures( [feature_id]) or qgis_layer.dataProvider().errors(): raise Exception( _('Cannot delete feature: %s') % ', '.join(qgis_layer.dataProvider().errors())) else: if not qgis_layer.dataProvider().deleteFeatures( [feature_id]) or qgis_layer.dataProvider().errors(): raise Exception( _('Cannot delete feature: %s') % ', '.join(qgis_layer.dataProvider().errors())) return insert_ids, lock_ids
def processAlgorithm(self, feedback): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = dataobjects.getLayerFromString(layer) fields = [] expressions = [] da = QgsDistanceArea() da.setSourceCrs(layer.crs()) da.setEllipsoidalMode(True) da.setEllipsoid(QgsProject.instance().ellipsoid()) exp_context = layer.createExpressionContext() for field_def in mapping: fields.append( QgsField(name=field_def['name'], type=field_def['type'], len=field_def['length'], prec=field_def['precision'])) expression = QgsExpression(field_def['expression']) expression.setGeomCalculator(da) expression.setDistanceUnits(QgsProject.instance().distanceUnits()) expression.setAreaUnits(QgsProject.instance().areaUnits()) expression.prepare(exp_context) if expression.hasParserError(): raise GeoAlgorithmExecutionException( self.tr(u'Parser error in expression "{}": {}').format( str(expression.expression()), str(expression.parserErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs()) # Create output vector layer with new attributes error_exp = None inFeat = QgsFeature() outFeat = QgsFeature() features = vector.features(layer) if len(features): total = 100.0 / len(features) for current, inFeat in enumerate(features): rownum = current + 1 geometry = inFeat.geometry() outFeat.setGeometry(geometry) attrs = [] for i in range(0, len(mapping)): field_def = mapping[i] expression = expressions[i] exp_context.setFeature(inFeat) exp_context.lastScope().setVariable("row_number", rownum) value = expression.evaluate(exp_context) if expression.hasEvalError(): error_exp = expression break attrs.append(value) outFeat.setAttributes(attrs) writer.addFeature(outFeat) feedback.setProgress(int(current * total)) else: feedback.setProgress(100) del writer if error_exp is not None: raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}').format( str(error_exp.expression()), str(error_exp.parserErrorString())))
def processAlgorithm(self, progress): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = dataobjects.getObjectFromUri(layer) provider = layer.dataProvider() 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( unicode(field_def['expression']), unicode(expression.parserErrorString()))) expression.prepare(exp_context) if expression.hasEvalError(): raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}').format( unicode(field_def['expression']), unicode(expression.evalErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, provider.geometryType(), layer.crs()) # Create output vector layer with new attributes error = '' calculationSuccess = True inFeat = QgsFeature() outFeat = QgsFeature() features = vector.features(layer) total = 100.0 / len(features) for current, inFeat in enumerate(features): rownum = current + 1 geometry = inFeat.geometry() if geometry is not None: outFeat.setGeometry(geometry) attrs = [] for i in xrange(0, len(mapping)): field_def = mapping[i] expression = expressions[i] exp_context.setFeature(inFeat) exp_context.lastScope().setVariable("row_number", rownum) value = expression.evaluate(exp_context) if expression.hasEvalError(): calculationSuccess = False error = expression.evalErrorString() break attrs.append(value) outFeat.setAttributes(attrs) writer.addFeature(outFeat) progress.setPercentage(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation' ' string:\n') + error)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) close_path = self.parameterAsBool(parameters, self.CLOSE_PATH, context) group_field_name = self.parameterAsString(parameters, self.GROUP_FIELD, context) order_field_name = self.parameterAsString(parameters, self.ORDER_FIELD, context) order_expression = self.parameterAsString(parameters, self.ORDER_EXPRESSION, context) date_format = self.parameterAsString(parameters, self.DATE_FORMAT, context) text_dir = self.parameterAsString(parameters, self.OUTPUT_TEXT_DIR, context) group_field_index = source.fields().lookupField(group_field_name) if group_field_index >= 0: group_field_def = source.fields().at(group_field_index) else: group_field_def = None if order_field_name: order_expression = QgsExpression.quotedColumnRef(order_field_name) if not order_expression: raise QgsProcessingException( self.tr('ORDER_EXPRESSION parameter is missing.')) expression_context = self.createExpressionContext( parameters, context, source) expression = QgsExpression(order_expression) if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) expression.prepare(expression_context) order_field_type = QVariant.String if expression.isField(): field_name = next(iter(expression.referencedColumns())) order_field_type = source.fields().field(field_name).type() fields = QgsFields() if group_field_def is not None: fields.append(group_field_def) begin_field = QgsField('begin', order_field_type) fields.append(begin_field) end_field = QgsField('end', order_field_type) fields.append(end_field) output_wkb = QgsWkbTypes.LineString if QgsWkbTypes.hasM(source.wkbType()): output_wkb = QgsWkbTypes.addM(output_wkb) if QgsWkbTypes.hasZ(source.wkbType()): output_wkb = QgsWkbTypes.addZ(output_wkb) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, output_wkb, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) if text_dir and not (os.path.exists(text_dir)): raise QgsProcessingException( self.tr("The text output directory does not exist")) points = dict() required_fields = expression.referencedColumns() required_fields.add(group_field_name) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(required_fields, source.fields()), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().constGet().clone() if group_field_index >= 0: group = f[group_field_index] else: group = 1 expression_context.setFeature(f) order = expression.evaluate(expression_context) if date_format != '': order = datetime.strptime(str(order), date_format) if group in points: points[group].append((order, point)) else: points[group] = [(order, point)] feedback.setProgress(int(current * total)) feedback.setProgress(0) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.ellipsoid()) current = 0 total = 100.0 / len(points) if points else 1 for group, vertices in points.items(): if feedback.isCanceled(): break vertices.sort(key=lambda x: (x[0] is None, x[0])) f = QgsFeature() attributes = [] if group_field_index >= 0: attributes.append(group) attributes.extend([vertices[0][0], vertices[-1][0]]) f.setAttributes(attributes) line = [node[1] for node in vertices] if close_path is True: if line[0] != line[-1]: line.append(line[0]) if text_dir: fileName = os.path.join(text_dir, '%s.txt' % group) with open(fileName, 'w') as fl: fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') for i in range(len(line)): if i == 0: fl.write('startAt=%f;%f;90\n' % (line[i].x(), line[i].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(QgsPointXY(line[i - 1]), QgsPointXY(line[i])) fl.write('%f;%f;90\n' % (angle, distance)) f.setGeometry(QgsGeometry(QgsLineString(line))) sink.addFeature(f, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
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) if zval is None or (isinstance(zval,QVariant) and zval.isNull()): continue 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 showExpressionsBuilder(self): context = self.param.expressionContext() dlg = QgsExpressionBuilderDialog(None, str(self.leText.text()), self, 'generic', context) context.popScope() values = self.modelParametersDialog.getAvailableValuesOfType( ParameterNumber, OutputNumber) variables = {} for value in values: if isinstance(value, ValueFromInput): name = value.name element = self.modelParametersDialog.model.inputs[name].param desc = element.description else: name = "%s_%s" % (value.alg, value.output) alg = self.modelParametersDialog.model.algs[value.alg] out = alg.algorithm.getOutputFromName(value.output) desc = self.tr("Output '{0}' from algorithm '{1}'").format( out.description, alg.description) variables[name] = desc values = self.modelParametersDialog.getAvailableValuesOfType( ParameterVector, OutputVector) values.extend( self.modelParametersDialog.getAvailableValuesOfType( ParameterRaster, OutputRaster)) for value in values: if isinstance(value, ValueFromInput): name = value.name element = self.modelParametersDialog.model.inputs[name].param desc = element.description else: name = "%s_%s" % (value.alg, value.output) alg = self.modelParametersDialog.model.algs[value.alg] element = alg.algorithm.getOutputFromName(value.output) desc = self.tr("Output '{0}' from algorithm '{1}'").format( element.description, alg.description) variables['%s_minx' % name] = self.tr("Minimum X of {0}").format(desc) variables['%s_miny' % name] = self.tr("Minimum Y of {0}").format(desc) variables['%s_maxx' % name] = self.tr("Maximum X of {0}").format(desc) variables['%s_maxy' % name] = self.tr("Maximum Y of {0}").format(desc) if isinstance(element, (ParameterRaster, OutputRaster)): variables['%s_min' % name] = self.tr("Minimum value of {0}").format(desc) variables['%s_max' % name] = self.tr("Maximum value of {0}").format(desc) variables['%s_avg' % name] = self.tr("Mean value of {0}").format(desc) variables['%s_stddev' % name] = self.tr( "Standard deviation of {0}").format(desc) for variable, desc in variables.items(): dlg.expressionBuilder().registerItem("Modeler", variable, "@" + variable, desc, highlightedItem=True) dlg.setWindowTitle(self.tr('Expression based input')) if dlg.exec_() == QDialog.Accepted: exp = QgsExpression(dlg.expressionText()) if not exp.hasParserError(): self.setValue(dlg.expressionText())
def convert(expression: str, engine, advanced, context: Context): # pylint: disable=too-many-branches """ Converts an expression which uses the specified engine """ expression_type = '' if isinstance(engine, AnnotationVBScriptEngine): expression_type = 'VBScript' elif isinstance(engine, AnnotationPythonEngine): expression_type = 'Python' elif isinstance(engine, AnnotationJScriptEngine): expression_type = 'JScript' if advanced: if context.unsupported_object_callback: if context.layer_name: context.unsupported_object_callback( '{}: Cannot automatically convert advanced {} expression: {}'.format(context.layer_name, expression_type, expression), level=Context.WARNING) elif context.symbol_name: context.unsupported_object_callback( '{}: Cannot automatically convert advanced {} expression: {}'.format(context.symbol_name, expression_type, expression), level=Context.WARNING) else: context.unsupported_object_callback( 'Cannot automatically convert advanced {} expression: {}'.format(expression_type, expression), level=Context.WARNING) return expression if isinstance(engine, AnnotationVBScriptEngine): res = ExpressionConverter.convert_vbscript_expression(expression, context) elif isinstance(engine, AnnotationPythonEngine): res = ExpressionConverter.convert_python_expression(expression) elif isinstance(engine, AnnotationJScriptEngine): res = ExpressionConverter.convert_js_expression(expression) else: res = ExpressionConverter.convert_esri_expression(expression) exp = QgsExpression(res) if not advanced and exp.hasParserError() and context.unsupported_object_callback: if context.layer_name: context.unsupported_object_callback( '{}: Could not automatically convert {} expression:\n{}\nPlease check and repair this expression'.format( context.layer_name, expression_type, expression), level=Context.WARNING) elif context.symbol_name: context.unsupported_object_callback( '{}: Cannot automatically convert {} expression:\n{}\nPlease check and repair this expression'.format( context.symbol_name, expression_type, expression), level=Context.WARNING) else: context.unsupported_object_callback( 'Cannot automatically convert {} expression:\n{}\nPlease check and repair this expression'.format( expression_type, expression), level=Context.WARNING) return res
class GeometryByExpression(QgisFeatureBasedAlgorithm): OUTPUT_GEOMETRY = 'OUTPUT_GEOMETRY' WITH_Z = 'WITH_Z' WITH_M = 'WITH_M' EXPRESSION = 'EXPRESSION' def group(self): return self.tr('Vector geometry') def groupId(self): return 'vectorgeometry' def flags(self): return super().flags( ) & ~QgsProcessingAlgorithm.FlagSupportsInPlaceEdits def __init__(self): super().__init__() self.geometry_types = [self.tr('Polygon'), 'Line', 'Point'] def initParameters(self, config=None): self.addParameter( QgsProcessingParameterEnum(self.OUTPUT_GEOMETRY, self.tr('Output geometry type'), options=self.geometry_types, defaultValue=0)) self.addParameter( QgsProcessingParameterBoolean( self.WITH_Z, self.tr('Output geometry has z dimension'), defaultValue=False)) self.addParameter( QgsProcessingParameterBoolean( self.WITH_M, self.tr('Output geometry has m values'), defaultValue=False)) self.addParameter( QgsProcessingParameterExpression(self.EXPRESSION, self.tr("Geometry expression"), defaultValue='$geometry', parentLayerParameterName='INPUT')) def name(self): return 'geometrybyexpression' def displayName(self): return self.tr('Geometry by expression') def outputName(self): return self.tr('Modified geometry') def prepareAlgorithm(self, parameters, context, feedback): self.geometry_type = self.parameterAsEnum(parameters, self.OUTPUT_GEOMETRY, context) self.wkb_type = None if self.geometry_type == 0: self.wkb_type = QgsWkbTypes.Polygon elif self.geometry_type == 1: self.wkb_type = QgsWkbTypes.LineString else: self.wkb_type = QgsWkbTypes.Point if self.parameterAsBool(parameters, self.WITH_Z, context): self.wkb_type = QgsWkbTypes.addZ(self.wkb_type) if self.parameterAsBool(parameters, self.WITH_M, context): self.wkb_type = QgsWkbTypes.addM(self.wkb_type) self.expression = QgsExpression( self.parameterAsString(parameters, self.EXPRESSION, context)) if self.expression.hasParserError(): feedback.reportError(self.expression.parserErrorString()) return False self.expression_context = self.createExpressionContext( parameters, context) self.expression.prepare(self.expression_context) return True def outputWkbType(self, input_wkb_type): return self.wkb_type def inputLayerTypes(self): return [QgsProcessing.TypeVector] def sourceFlags(self): return QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks def processFeature(self, feature, context, feedback): self.expression_context.setFeature(feature) value = self.expression.evaluate(self.expression_context) if self.expression.hasEvalError(): raise QgsProcessingException( self.tr('Evaluation error: {0}').format( self.expression.evalErrorString())) if not value: feature.setGeometry(QgsGeometry()) else: if not isinstance(value, QgsGeometry): raise QgsProcessingException( self.tr('{} is not a geometry').format(value)) feature.setGeometry(value) return [feature]
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 responseComplete(self): """ Send new response """ self.handler = self.serverIface.requestHandler() params = self.handler.parameterMap() # Check if needed params are passed # If not, do not change QGIS Server response service = params.get('SERVICE') if not service: return if service.lower() != 'wms': return # Check if getprintatlas request. If not, just send the response if 'REQUEST' not in params or params['REQUEST'].lower() not in [ 'getreport', 'getprintatlas', 'getcapabilitiesatlas']: return # Get capabilities if params['REQUEST'].lower() == 'getcapabilitiesatlas': body = { 'status': 'success', 'metadata': self.metadata } self.setJsonResponse('200', body) return # Check if needed params are set required = ['TEMPLATE', 'EXP_FILTER'] # For QGIS a report is the same as an atlas. so we use the same calls if params['REQUEST'].lower() == 'getreport': required = ['TEMPLATE'] params['REQUEST'] = 'GetPrintAtlas' # a report has no filters so we can ignore the EXP_FILTER params['EXP_FILTER'] = '""' if not all(elem in params for elem in required): body = { 'status': 'fail', 'message': 'Missing parameters: {} required.'.format( ', '.join(required)) } self.setJsonResponse('400', body) return self.composer_name = params['TEMPLATE'] self.feature_filter = params['EXP_FILTER'] # check expression expression = QgsExpression(self.feature_filter) if expression.hasParserError(): body = { 'status': 'fail', 'message': 'An error occurred while parsing the given expression: %s' % expression.parserErrorString() } QgsMessageLog.logMessage('ATLAS - ERROR EXPRESSION: {}'.format(expression.parserErrorString()), 'atlasprint', Qgis.Critical) self.setJsonResponse('400', body) return # noinspection PyBroadException try: pdf = self.print( composer_name=self.composer_name, predefined_scales=self.predefined_scales, feature_filter=self.feature_filter ) except Exception as e: pdf = None QgsMessageLog.logMessage('ATLAS - PDF CREATION ERROR: {}'.format(e), 'atlasprint', Qgis.Critical) if not pdf: body = { 'status': 'fail', 'message': 'ATLAS - Error while generating the PDF' } QgsMessageLog.logMessage("ATLAS - No PDF generated in %s" % pdf, 'atlasprint', Qgis.Critical) self.setJsonResponse('500', body) return # Send PDF self.handler.clear() self.handler.setResponseHeader('Content-type', 'application/pdf') self.handler.setResponseHeader('Status', '200') # noinspection PyBroadException try: with open(pdf, 'rb') as f: loads = f.readlines() ba = QByteArray(b''.join(loads)) self.handler.appendBody(ba) except Exception as e: QgsMessageLog.logMessage('ATLAS - PDF READING ERROR: {}'.format(e), 'atlasprint', Qgis.Critical) body = { 'status': 'fail', 'message': 'Error occured while reading PDF file', } self.setJsonResponse('500', body) finally: os.remove(pdf) return
def getFeatureWithFormScope(self, params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Get filtered features with a form scope In parameters: LAYER=wms-layer-name FILTER=An expression to filter layer FORM_FEATURE={"type": "Feature", "geometry": {}, "properties": {}} // optionals FIELDS=list of requested field separated by comma WITH_GEOMETRY=False """ layername = params.get('LAYER', '') if not layername: raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' 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 'VirtualField': {} provided". format(layername), 400) # get filter exp_filter = params.get('FILTER', '') if not exp_filter: raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FILTER parameter is mandatory", 400) # get form feature form_feature = params.get('FORM_FEATURE', '') if not form_feature: raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE parameter is mandatory", 400) # Check features geojson = {} try: geojson = json.loads(form_feature) except Exception: QgsMessageLog.logMessage( "JSON loads form feature '{}' exception:\n{}".format( form_feature, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed" .format(form_feature), 400) if not geojson or not isinstance(geojson, dict): raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed" .format(form_feature), 400) if ('type' not in geojson) or geojson['type'] != 'Feature': raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed: type not defined or not Feature." .format(form_feature), 400) # try to load form feature # read fields form_feature_fields = QgsJsonUtils.stringToFields( form_feature, QTextCodec.codecForName("UTF-8")) # read features form_feature_list = QgsJsonUtils.stringToFeatureList( form_feature, form_feature_fields, QTextCodec.codecForName("UTF-8")) # features not well formed if not form_feature_list: raise ExpressionServiceError( "Bad request error", "Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n{}" .format(form_feature), 400) if len(form_feature_list) != 1: raise ExpressionServiceError( "Bad request error", "Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n{}" .format(form_feature), 400) # Get the form feature form_feat = form_feature_list[0] # create expression context exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope( QgsExpressionContextUtils.projectScope(project)) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) exp_context.appendScope(QgsExpressionContextUtils.formScope(form_feat)) # create distance area context da = QgsDistanceArea() da.setSourceCrs(layer.crs(), project.transformContext()) da.setEllipsoid(project.ellipsoid()) # Get filter expression exp_f = QgsExpression(exp_filter) exp_f.setGeomCalculator(da) exp_f.setDistanceUnits(project.distanceUnits()) exp_f.setAreaUnits(project.areaUnits()) if exp_f.hasParserError(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'GetFeatureWithFormScope': Error \"{}\": {}" .format(exp_filter, exp_f.parserErrorString()), 400) if not exp_f.isValid(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'GetFeatureWithFormScope': Expression not valid \"{}\"" .format(exp_filter), 400) exp_f.prepare(exp_context) req = QgsFeatureRequest(exp_f, exp_context) # With geometry 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) response.write(separator + jsonExporter.exportFeature(feat, {}, fid)) response.flush() separator = ',\n' response.write(']}') return
def processLoading(self): """ Load all the layers in QGIS and apply corresponding style """ self.startTime = datetime.now() QApplication.setOverrideCursor(Qt.WaitCursor) # default style to apply for Cadastre layers self.themeDir = str(self.dialog.liTheme.currentText()) if not os.path.exists( os.path.join(self.qc.plugin_dir, "styles/%s" % self.themeDir)): self.themeDir = self.defaultThemeDir # set Cadastre SVG path if not set cadastreSvgPath = os.path.join(self.qc.plugin_dir, "styles/%s/svg" % self.themeDir) s = QSettings() qgisSvgPaths = s.value("svg/searchPathsForSVG", 10, type=str) a = list(qgisSvgPaths) if cadastreSvgPath not in a: a.append(cadastreSvgPath) s.setValue("svg/searchPathsForSVG", a) self.qc.updateLog( u"* Le chemin contenant les SVG du plugin Cadastre a été ajouté dans les options de QGIS" ) # Get selected options providerName = self.dialog.dbpluginclass.providerName() qgisCadastreLayers = [] self.dialog.schema = str(self.dialog.liDbSchema.currentText()) self.dialog.totalSteps = len(self.qgisCadastreLayerList) # Run the loading self.updateTimer() self.qc.updateLog(u'Chargement des tables :') # Get database list of tables if self.dialog.dbType == 'postgis': schemaSearch = [ s for s in self.dialog.db.schemas() if s.name == self.dialog.schema ] schemaInst = schemaSearch[0] dbTables = self.dialog.db.tables(schemaInst) if self.dialog.dbType == 'spatialite': dbTables = self.dialog.db.tables() # Get commune filter by expression communeExpression = self.dialog.communeFilter.text().strip() communeFilter = None cExp = QgsExpression(communeExpression) if communeExpression != '' and not cExp.hasParserError(): self.qc.updateLog(u'Filtrage à partir des communes : %s' % communeExpression) cReq = QgsFeatureRequest(cExp) cTableList = [a for a in dbTables if a.name == 'geo_commune'] cTable = cTableList[0] cUniqueCol = 'ogc_fid' cSchema = self.dialog.schema cGeomCol = 'geom' cLayerUri = self.dialog.db.uri() cLayerUri.setDataSource(cSchema, cTable.name, cGeomCol, '', cUniqueCol) clayer = QgsVectorLayer(cLayerUri.uri(), 'com', providerName) cfeatures = clayer.getFeatures(cReq) cids = [a['commune'] for a in cfeatures] if len(cids): communeFilter = cids else: self.qc.updateLog( u'Filtrage à partir des communes, expression invalide : %s' % cExp.parserErrorString()) # Loop throuhg qgisQastreLayerList and load each corresponding table for item in self.qgisCadastreLayerList: if item['label'] not in self.mainLayers and self.dialog.cbMainLayersOnly.isChecked( ): continue if 'dbType' in item and item['dbType'] != self.dialog.dbType: continue # update progress bar self.qc.updateLog(u'* %s' % item['label']) self.dialog.step += 1 self.qc.updateProgressBar() # Tables - Get db_manager table instance tableList = [a for a in dbTables if a.name == item['table']] if len(tableList) == 0 and 'isView' not in item: self.qc.updateLog(u' - Aucune table trouvée pour %s' % item['label']) continue if tableList: table = tableList[0] source = table.name try: uniqueField = table.getValidQGisUniqueFields(True) uniqueCol = uniqueField.name except: uniqueCol = 'ogc_fid' schema = self.dialog.schema # View if 'isView' in item: if self.dialog.dbType == 'spatialite': schemaReplace = '' else: schemaReplace = '"%s".' % self.dialog.schema source = item['table'].replace('schema.', schemaReplace) uniqueCol = item['key'] schema = None sql = item['sql'] geomCol = item['geom'] if communeFilter: communeFilterText = "'" + "', '".join(communeFilter) + "'" nschema = '' if self.dialog.dbType == 'postgis': nschema = '"%s".' % schema if 'subset' in item: subset = item['subset'] sql += subset % communeFilterText else: itemcol = item['table'] if item['table'] == 'geo_label': itemcol = 'ogc_fid' subset = itemcol + ''' IN ( SELECT b.''' + itemcol + ''' FROM ''' + nschema + item['table'] + ''' b JOIN ''' + nschema + '''geo_commune c ON ST_Within(b.geom, c.geom) WHERE 2>1 AND c.geo_commune IN ( %s ) ) ''' if sql: sql += ' AND ' sql += subset % communeFilterText # Create vector layer alayerUri = self.dialog.db.uri() alayerUri.setDataSource(schema, source, geomCol, sql, uniqueCol) vlayer = QgsVectorLayer(alayerUri.uri(), item['label'], providerName) # apply style qmlPath = os.path.join( self.qc.plugin_dir, "styles/%s/%s.qml" % (self.themeDir, item['name'])) if os.path.exists(qmlPath): vlayer.loadNamedStyle(qmlPath) # append vector layer to the list qgisCadastreLayers.append(vlayer) self.updateTimer() # Get canvas and disable rendering from qgis.utils import iface canvas = iface.mapCanvas() canvas.freeze(True) # Add all layers to QGIS registry (but not yet to the layer tree) self.qc.updateLog(u'Ajout des couches dans le registre de QGIS') QgsProject.instance().addMapLayers(qgisCadastreLayers, False) self.updateTimer() # Create a group "Cadastre" and move all layers into it self.qc.updateLog(u'Ajout des couches dans le groupe Cadastre') root = QgsProject.instance().layerTreeRoot() g1 = root.findGroup(u"Cadastre") if g1: gf = root.findGroup(u"Fond") if not gf: gf = g1.addGroup("Fond") ge = root.findGroup(u'Étiquettes cadastre') if not ge: ge = gf.addGroup(u'Étiquettes cadastre') gd = root.findGroup(u"Données cadastre") if not gd: gd = gf.addGroup(u"Données cadastre") else: g1 = root.insertGroup(0, "Cadastre") gf = g1.addGroup("Fond") ge = gf.addGroup(u'Étiquettes cadastre') gd = gf.addGroup(u'Données cadastre') variables = QgsProject.instance().customVariables() for layer in qgisCadastreLayers: # ~ layer.updateExtents() # Get layertree item nodeLayer = QgsLayerTreeLayer(layer) # Get layer options qlayer = [ a for a in self.qgisCadastreLayerList if a['label'] == layer.name() ] if qlayer: qlayer = qlayer[0] # Move layer to proper group if qlayer['group'] == 'E': ge.insertChildNode(0, nodeLayer) elif qlayer['group'] == 'D': gd.insertChildNode(0, nodeLayer) else: g1.insertChildNode(0, nodeLayer) # Enable/Disable layer if not qlayer['active']: nodeLayer.setItemVisibilityChecked(Qt.Unchecked) else: # Move layer to Cadastre group g1.insertChildNode(-1, nodeLayer) # Do not expand layer legend nodeLayer.setExpanded(False) # set varaibles if layer.name() in self.variableLayers: varlayer = self.variableLayers[layer.name()] variables['cadastre_' + varlayer['var_key'] + '_layer_id'] = layer.id() variables['cadastre_' + varlayer['var_key'] + '_unique_field'] = varlayer['unique_field'] QgsProject.instance().setCustomVariables(variables) self.updateTimer() # Zoom to full extent self.qc.updateLog(u'Zoom sur les couches') canvas.zoomToFullExtent() canvas.freeze(False) canvas.refresh() self.updateTimer() # progress bar self.dialog.step += 1 self.qc.updateProgressBar() # Emit signal self.qc.updateLog(u'Mise à jour des outils cadastre') self.cadastreLoadingFinished.emit() self.updateTimer() # Final message QApplication.restoreOverrideCursor() QMessageBox.information( self.dialog, u"Cadastre", u"Les données ont bien été chargées dans QGIS") self.dialog.pbProcess.setValue(0) QApplication.restoreOverrideCursor()
def read_config(self, key=""): # the following code reads the configuration file which setups the plugin to search in the correct database, # table and method settings = QgsSettings() settings.beginGroup("/Discovery") connection = settings.value(key + "connection", "", type=str) self.data_type = settings.value(key + "data_type", "", type=str) self.file = settings.value(key + "file", "", type=str) self.postgisschema = settings.value(key + "schema", "", type=str) self.postgistable = settings.value(key + "table", "", type=str) self.postgissearchcolumn = settings.value(key + "search_column", "", type=str) self.escapespecchars = settings.value(key + "escape_spec_chars", False, type=bool) self.echosearchcolumn = settings.value(key + "echo_search_column", True, type=bool) self.postgisdisplaycolumn = settings.value(key + "display_columns", "", type=str) self.postgisgeomcolumn = settings.value(key + "geom_column", "", type=str) if settings.value("marker_time_enabled", True, type=bool): self.display_time = settings.value("marker_time", 5000, type=int) else: self.display_time = -1 if settings.value("bar_info_time_enabled", True, type=bool): self.bar_info_time = settings.value("bar_info_time", 30, type=int) else: self.bar_info_time = 0 self.limit_results = settings.value("limit_results", 1000, type=int) self.info_to_clipboard = settings.value("info_to_clipboard", True, type=bool) scale_expr = settings.value(key + "scale_expr", "", type=str) bbox_expr = settings.value(key + "bbox_expr", "", type=str) if self.is_displayed: self.hide_marker() self.hide_rubber_band() self.is_displayed = False self.make_enabled(False) # assume the config is invalid first self.db_conn = None if self.data_type == "postgres": self.conn_info = dbutils.get_postgres_conn_info(connection) self.layer = None if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \ len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0: return if len(self.conn_info) == 0: iface.messageBar().pushMessage("Discovery", "The database connection '%s' does not exist!" % connection, level=Qgis.Critical) return if self.data_type == "mssql": self.conn_info = mssql_utils.get_mssql_conn_info(connection) self.layer = None if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \ len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0: return if len(self.conn_info) == 0: iface.messageBar().pushMessage("Discovery", "The database connection '%s' does not exist!" % connection, level=Qgis.Critical) return elif self.data_type == "gpkg": self.layer = QgsVectorLayer(self.file + '|layername=' + self.postgistable, self.postgistable, 'ogr') self.conn_info = None self.extra_expr_columns = [] self.scale_expr = None self.bbox_expr = None self.make_enabled(True) # optional scale expression when zooming in to results if len(scale_expr) != 0: expr = QgsExpression(scale_expr) if expr.hasParserError(): iface.messageBar().pushMessage("Discovery", "Invalid scale expression: " + expr.parserErrorString(), level=Qgis.Warning) else: self.scale_expr = scale_expr self.extra_expr_columns += expr.referencedColumns() # optional bbox expression when zooming in to results if len(bbox_expr) != 0: expr = QgsExpression(bbox_expr) if expr.hasParserError(): iface.messageBar().pushMessage("Discovery", "Invalid bbox expression: " + expr.parserErrorString(), level=Qgis.Warning) else: self.bbox_expr = bbox_expr self.extra_expr_columns += expr.referencedColumns()
def print_layout(project, layout_name, feature_filter: str = None, scales=None, scale=None, **kwargs): """Generate a PDF for an atlas or a report. :param project: The QGIS project. :type project: QgsProject :param layout_name: Name of the layout of the atlas or report. :type layout_name: basestring :param feature_filter: QGIS Expression to use to select the feature. It can return many features, a multiple pages PDF will be returned. This is required to print atlas, not report :type feature_filter: basestring :param scale: A scale to force in the atlas context. Default to None. :type scale: int :param scales: A list of predefined list of scales to force in the atlas context. Default to None. :type scales: list :return: Path to the PDF. :rtype: basestring """ canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas) bridge.setCanvasLayers() manager = project.layoutManager() master_layout = manager.layoutByName(layout_name) settings = QgsLayoutExporter.PdfExportSettings() atlas = None atlas_layout = None report_layout = None logger = Logger() if not master_layout: raise AtlasPrintException('Layout `{}` not found'.format(layout_name)) if master_layout.layoutType() == QgsMasterLayoutInterface.PrintLayout: for _print_layout in manager.printLayouts(): if _print_layout.name() == layout_name: atlas_layout = _print_layout break atlas = atlas_layout.atlas() if not atlas.enabled(): raise AtlasPrintException('The layout is not enabled for an atlas') layer = atlas.coverageLayer() if feature_filter is None: raise AtlasPrintException( 'EXP_FILTER is mandatory to print an atlas layout') feature_filter = optimize_expression(layer, feature_filter) expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( 'Expression is invalid, parser error: {}'.format( expression.parserErrorString())) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(project)) context.appendScope( QgsExpressionContextUtils.layoutScope(atlas_layout)) context.appendScope(QgsExpressionContextUtils.atlasScope(atlas)) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(context) if expression.hasEvalError(): raise AtlasPrintException( 'Expression is invalid, eval error: {}'.format( expression.evalErrorString())) atlas.setFilterFeatures(True) atlas.setFilterExpression(feature_filter) if scale: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Fixed) atlas_layout.referenceMap().setScale(scale) if scales: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Predefined) if Qgis.QGIS_VERSION_INT >= 30900: settings.predefinedMapScales = scales else: atlas_layout.reportContext().setPredefinedScales(scales) if not scales and atlas_layout.referenceMap().atlasScalingMode( ) == QgsLayoutItemMap.Predefined: if Qgis.QGIS_VERSION_INT >= 30900: use_project = project.useProjectScales() map_scales = project.mapScales() else: map_scales = project_scales(project) use_project = len(map_scales) == 0 if not use_project or len(map_scales) == 0: logger.info( 'Map scales not found in project, fetching predefined map scales in global config' ) map_scales = global_scales() if Qgis.QGIS_VERSION_INT >= 30900: settings.predefinedMapScales = map_scales else: atlas_layout.reportContext().setPredefinedScales(map_scales) elif master_layout.layoutType() == QgsMasterLayoutInterface.Report: report_layout = master_layout else: raise AtlasPrintException('The layout is not supported by the plugin') for key, value in kwargs.items(): found = False if atlas_layout: item = atlas_layout.itemById(key.lower()) if isinstance(item, QgsLayoutItemLabel): item.setText(value) found = True logger.info( 'Additional parameters: {} found in layout {}, value {}'.format( key, found, value)) export_path = os.path.join(tempfile.gettempdir(), '{}_{}.pdf'.format(layout_name, uuid4())) result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout, export_path, settings) if result != QgsLayoutExporter.Success and not os.path.isfile(export_path): raise Exception('export not generated {} ({})'.format( export_path, error)) return export_path