def processAlgorithm(self, progress): ns = {} ns["progress"] = progress ns["scriptDescriptionFile"] = self.descriptionFile for param in self.parameters: ns[param.name] = param.value for out in self.outputs: ns[out.name] = out.value variables = re.findall("@[a-zA-Z0-9_]*", self.script) script = "import processing\n" script += self.script context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) for var in variables: varname = var[1:] if context.hasVariable(varname): script = script.replace(var, context.variable(varname)) else: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, "Cannot find variable: %s" % varname) exec((script), ns) for out in self.outputs: out.setValue(ns[out.name])
def processAlgorithm(self, parameters, context, feedback): ns = {} ns['feedback'] = feedback ns['scriptDescriptionFile'] = self.descriptionFile ns['context'] = context for param in self.parameterDefinitions(): ns[param.name] = parameters[param.name()] for out in self.outputs: ns[out.name] = out.value variables = re.findall('@[a-zA-Z0-9_]*', self.script) script = 'import processing\n' script += self.script context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance())) for var in variables: varname = var[1:] if context.hasVariable(varname): script = script.replace(var, context.variable(varname)) else: QgsMessageLog.logMessage(self.tr('Cannot find variable: {0}').format(varname), self.tr('Processing'), QgsMessageLog.WARNING) exec((script), ns) for out in self.outputs: out.setValue(ns[out.name])
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 processAlgorithm(self, feedback): ns = {} ns['feedback'] = feedback ns['scriptDescriptionFile'] = self.descriptionFile for param in self.parameters: ns[param.name] = param.value for out in self.outputs: ns[out.name] = out.value variables = re.findall('@[a-zA-Z0-9_]*', self.script) script = 'import processing\n' script += self.script context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance())) for var in variables: varname = var[1:] if context.hasVariable(varname): script = script.replace(var, context.variable(varname)) else: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Cannot find variable: %s' % varname) exec((script), ns) for out in self.outputs: out.setValue(ns[out.name])
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 initContext(self): exp_context = self.builder.expressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(self.layer)) exp_context.lastScope().setVariable("row_number", 1) exp_context.setHighlightedVariables(["row_number"]) self.builder.setExpressionContext(exp_context)
def populateByExpression(self, adding=False): """ Populates the panel using an expression """ context = dataobjects.createContext() expression_context = context.expressionContext() # use the first row parameter values as a preview during expression creation params = self.panel.parametersForRow(0, warnOnInvalid=False) alg_scope = QgsExpressionContextUtils.processingAlgorithmScope(self.panel.alg, params, context) # create explicit variables corresponding to every parameter for k, v in params.items(): alg_scope.setVariable(k, v, True) expression_context.appendScope(alg_scope) # mark the parameter variables as highlighted for discoverability highlighted_vars = expression_context.highlightedVariables() highlighted_vars.extend(list(params.keys())) expression_context.setHighlightedVariables(highlighted_vars) dlg = QgsExpressionBuilderDialog(layer=None, context=context.expressionContext()) if adding: dlg.setExpectedOutputFormat(self.tr('An array of values corresponding to each new row to add')) if not dlg.exec_(): return if adding: exp = QgsExpression(dlg.expressionText()) res = exp.evaluate(expression_context) if type(res) is not list: res = [res] first_row = self.panel.batchRowCount() if self.panel.batchRowCount() > 1 else 0 for row, value in enumerate(res): self.setRowValue(row + first_row, value, context) else: for row in range(self.panel.batchRowCount()): params = self.panel.parametersForRow(row, warnOnInvalid=False) # remove previous algorithm scope -- we need to rebuild this completely, using the # other parameter values from the current row expression_context.popScope() alg_scope = QgsExpressionContextUtils.processingAlgorithmScope(self.panel.alg, params, context) for k, v in params.items(): alg_scope.setVariable(k, v, True) expression_context.appendScope(alg_scope) # rebuild a new expression every time -- we don't want the expression compiler to replace # variables with precompiled values exp = QgsExpression(dlg.expressionText()) value = exp.evaluate(expression_context) self.setRowValue(row, value, context)
def expressionContext(self): context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) processingScope = QgsExpressionContextScope() for param in self.alg.parameters: processingScope.setVariable("%s_value" % param.name, "") context.appendScope(processingScope) return context
def _expressionContext(alg): context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) processingScope = QgsExpressionContextScope() for param in alg.parameters: processingScope.setVariable('%s_value' % param.name, '') context.appendScope(processingScope) return context
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 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 __init__(self, provider): super(PyFeatureSource, self).__init__() self._provider = provider self._features = provider._features self._expression_context = QgsExpressionContext() self._expression_context.appendScope(QgsExpressionContextUtils.globalScope()) self._expression_context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance())) self._expression_context.setFields(self._provider.fields()) if self._provider.subsetString(): self._subset_expression = QgsExpression(self._provider.subsetString()) self._subset_expression.prepare(self._expression_context) else: self._subset_expression = None
def updateLayer(self): self.layer = dataobjects.getObject(self.cmbInputLayer.currentText()) self.builder.setLayer(self.layer) self.builder.loadFieldNames() exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(self.layer)) exp_context.lastScope().setVariable("row_number", 1) exp_context.setHighlightedVariables(["row_number"]) self.builder.setExpressionContext(exp_context) self.populateFields()
def inasafe_analysis_summary_field_value(field, feature, parent): """Retrieve a value from a field in the analysis summary layer. e.g. inasafe_analysis_summary_field_value('total_not_exposed') -> 3 """ _ = feature, parent # NOQA project_context_scope = QgsExpressionContextUtils.projectScope( QgsProject.instance()) registry = QgsProject.instance() key = provenance_layer_analysis_impacted_id['provenance_key'] if not project_context_scope.hasVariable(key): return None layer = registry.mapLayer(project_context_scope.variable(key)) if not layer: return None index = layer.fields().lookupField(field) if index < 0: return None feature = next(layer.getFeatures()) return feature[index]
def processAlgorithm(self, progress): ns = {} ns['progress'] = progress ns['scriptDescriptionFile'] = self.descriptionFile for param in self.parameters: ns[param.name] = param.value for out in self.outputs: ns[out.name] = out.value variables = re.findall("@[a-zA-Z0-9_]*", self.script) print variables script = 'import processing\n' script += self.script scope = QgsExpressionContextUtils.projectScope() for var in variables: varname = var[1:] if not scope.hasVariable(varname): raise GeoAlgorithmExecutionException("Wrong variable: %s" % varname) script = script.replace(var, scope.variable(varname)) exec((script), ns) for out in self.outputs: out.setValue(ns[out.name])
def inasafe_sub_analysis_summary_field_value( exposure_key, field, feature, parent): """Retrieve a value from field in the specified exposure analysis layer. """ _ = feature, parent # NOQA project_context_scope = QgsExpressionContextUtils.projectScope( QgsProject.instance()) project = QgsProject.instance() key = ('{provenance}__{exposure}').format( provenance=provenance_multi_exposure_analysis_summary_layers_id[ 'provenance_key'], exposure=exposure_key) if not project_context_scope.hasVariable(key): return None analysis_summary_layer = project.mapLayer( project_context_scope.variable(key)) if not analysis_summary_layer: return None index = analysis_summary_layer.fields().lookupField(field) if index < 0: return None feature = next(analysis_summary_layer.getFeatures()) return feature[index]
def load_shapefile(self, feature_type, base_path): """Load downloaded shape file to QGIS Main Window. :param feature_type: What kind of features should be downloaded. Currently 'buildings', 'building-points' or 'roads' are supported. :type feature_type: str :param base_path: The base path of the shape file (without extension). :type base_path: str :raises: FileMissingError - when buildings.shp not exist """ path = '%s.shp' % base_path if not os.path.exists(path): message = self.tr( '%s does not exist. The server does not have any data for ' 'this extent.' % path) raise FileMissingError(message) layer = self.iface.addVectorLayer(path, feature_type, 'ogr') # Check if it's a building layer and if it's QGIS 2.14 about the 2.5D if qgis_version() >= 21400 and feature_type == 'buildings': layer_scope = QgsExpressionContextUtils.layerScope(layer) if not layer_scope.variable('qgis_25d_height'): QgsExpressionContextUtils.setLayerVariable( layer, 'qgis_25d_height', 0.0002) if not layer_scope.variable('qgis_25d_angle'): QgsExpressionContextUtils.setLayerVariable( layer, 'qgis_25d_angle', 70) canvas_srid = self.canvas.mapSettings().destinationCrs().srsid() on_the_fly_projection = self.canvas.hasCrsTransformEnabled() if canvas_srid != 4326 and not on_the_fly_projection: if QGis.QGIS_VERSION_INT >= 20400: self.canvas.setCrsTransformEnabled(True) else: display_warning_message_bar( self.iface, self.tr('Enable \'on the fly\''), self.tr( 'Your current projection is different than EPSG:4326. ' 'You should enable \'on the fly\' to display ' 'correctly your layers') )
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) field_name = self.parameterAsString(parameters, self.FIELD_NAME, context) field_type = self.TYPES[self.parameterAsEnum(parameters, self.FIELD_TYPE, context)] width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context) precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context) new_field = self.parameterAsBool(parameters, self.NEW_FIELD, context) formula = self.parameterAsString(parameters, self.FORMULA, context) expression = QgsExpression(formula) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs()) da.setEllipsoid(context.project().ellipsoid()) expression.setGeomCalculator(da) expression.setDistanceUnits(context.project().distanceUnits()) expression.setAreaUnits(context.project().areaUnits()) fields = source.fields() field_index = fields.lookupField(field_name) if new_field or field_index < 0: fields.append(QgsField(field_name, field_type, '', width, precision)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source.wkbType(), source.sourceCrs()) exp_context = self.createExpressionContext(parameters, context) if layer is not None: exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) if not expression.prepare(exp_context): raise QgsProcessingException( self.tr('Evaluation error: {0}').format(expression.parserErrorString())) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break rownum = current + 1 exp_context.setFeature(f) exp_context.lastScope().setVariable("row_number", rownum) value = expression.evaluate(exp_context) if expression.hasEvalError(): feedback.reportError(expression.evalErrorString()) else: attrs = f.attributes() if new_field or field_index < 0: attrs.append(value) else: attrs[field_index] = value f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def createExpressionContext(): context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance())) if iface.mapCanvas(): context.appendScope(QgsExpressionContextUtils.mapSettingsScope(iface.mapCanvas().mapSettings())) processingScope = QgsExpressionContextScope() extent = iface.mapCanvas().fullExtent() processingScope.setVariable('fullextent_minx', extent.xMinimum()) processingScope.setVariable('fullextent_miny', extent.yMinimum()) processingScope.setVariable('fullextent_maxx', extent.xMaximum()) processingScope.setVariable('fullextent_maxy', extent.yMaximum()) context.appendScope(processingScope) return context
def new_context(self): feature = QgsFeature() fields = QgsFields() fields.append(QgsField("testfield", QVariant.Int)) feature.setFields(fields, True) feature["testfield"] = 20 context = QgsExpressionContextUtils.createFeatureBasedContext(feature, fields) return context
def test_length_expression(self): # compare length using the ellipsoid in kms and the planimetric distance in meters self.lyr.fieldName = "round($length,5) || ' - ' || round(length($geometry),2)" self.lyr.isExpression = True QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:32613")) QgsProject.instance().setEllipsoid("WGS84") QgsProject.instance().setDistanceUnits(QgsUnitTypes.DistanceKilometers) ctxt = QgsExpressionContext() ctxt.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance())) ctxt.appendScope(QgsExpressionContextUtils.layerScope(self.layer)) self._TestMapSettings.setExpressionContext(ctxt) self.lyr.placement = QgsPalLayerSettings.Curved self.lyr.placementFlags = QgsPalLayerSettings.AboveLine | QgsPalLayerSettings.MapOrientation self.checkTest()
def test_MatchesTrueForFields(self): feature = QgsFeature() fields = QgsFields() fields.append(QgsField("testfield", QVariant.Int)) feature.setFields(fields, True) feature["testfield"] = 20 style = QgsConditionalStyle('"testfield" = @value') context = QgsExpressionContextUtils.createFeatureBasedContext(feature, fields) assert style.matches(20, context)
def _layer_tooltip(self, layer, feat): try: df = layer.displayField() if df: return str(feat.attribute(df)) else: context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance())) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) context.appendScope(QgsExpressionContextUtils.mapSettingsScope( self.canvas.mapSettings() ) ) context.setFeature( feat ) x = QgsExpression(layer.displayExpression()) x.prepare(context) return x.evaluate(context).replace('\n', "<br/>") except: return ""
def pointPicked(self, event): for b in self.rubberBands: del b self.rubberBands[:] = [] fieldX = self.cmbXField.currentField() fieldY = self.cmbYField.currentField() artist = event.artist indices = event.ind for i in indices: x = self.xData[artist.name][i] y = self.yData[artist.name][i] if isinstance(x, int): expr = '"{}" = {} AND '.format(fieldX, x) elif isinstance(x, float): expr = 'abs("{}" - {}) <= 0.0000001 AND '.format(fieldX, x) elif isinstance(x, (str, unicode)): expr = """"{}" = '{}' AND """.format(fieldX, x) else: expr = """"{}" = '{}' AND """.format(fieldX, x.toString('yyyy-MM-dd')) if isinstance(y, float): expr += 'abs("{}" - {}) <= 0.0000001'.format(fieldY, y) elif isinstance(y, (str, unicode)): expr += """"{}" = '{}'""".format(fieldY, y) else: expr += '"{}" = {}'.format(fieldY, y) layer = self.cmbLayer.currentLayer() expression = QgsExpression(expr) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) context.appendScope(QgsExpressionContextUtils.mapSettingsScope(self.canvas.mapSettings())) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) request = QgsFeatureRequest(expression, context) for f in layer.getFeatures(request): hl = QgsHighlight(self.canvas, f.geometry(), layer) hl.setColor(QColor(255, 0, 0)) hl.setWidth(2) self.rubberBands.append(hl)
def analysis_summary_report(feature, parent): """Retrieve an HTML table report of current selected analysis. """ _ = feature, parent # NOQA project_context_scope = QgsExpressionContextUtils.projectScope( QgsProject.instance()) key = provenance_layer_analysis_impacted['provenance_key'] if not project_context_scope.hasVariable(key): return None analysis_dir = dirname(project_context_scope.variable(key)) return get_impact_report_as_string(analysis_dir)
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 expressionContext(self): context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope()) processingScope = QgsExpressionContextScope() layers = dataobjects.getAllLayers() for layer in layers: name = layer.name() processingScope.setVariable('%s_minx' % name, layer.extent().xMinimum()) processingScope.setVariable('%s_miny' % name, layer.extent().yMinimum()) processingScope.setVariable('%s_maxx' % name, layer.extent().xMaximum()) processingScope.setVariable('%s_maxy' % name, layer.extent().yMaximum()) if isinstance(layer, QgsRasterLayer): cellsize = (layer.extent().xMaximum() - layer.extent().xMinimum()) / layer.width() processingScope.setVariable('%s_cellsize' % name, cellsize) layers = dataobjects.getRasterLayers() for layer in layers: for i in range(layer.bandCount()): stats = layer.dataProvider().bandStatistics(i + 1) processingScope.setVariable('%s_band%i_avg' % (name, i + 1), stats.mean) processingScope.setVariable('%s_band%i_stddev' % (name, i + 1), stats.stdDev) processingScope.setVariable('%s_band%i_min' % (name, i + 1), stats.minimumValue) processingScope.setVariable('%s_band%i_max' % (name, i + 1), stats.maximumValue) extent = iface.mapCanvas().extent() processingScope.setVariable('canvasextent_minx', extent.xMinimum()) processingScope.setVariable('canvasextent_miny', extent.yMinimum()) processingScope.setVariable('canvasextent_maxx', extent.xMaximum()) processingScope.setVariable('canvasextent_maxy', extent.yMaximum()) extent = iface.mapCanvas().fullExtent() processingScope.setVariable('fullextent_minx', extent.xMinimum()) processingScope.setVariable('fullextent_miny', extent.yMinimum()) processingScope.setVariable('fullextent_maxx', extent.xMaximum()) processingScope.setVariable('fullextent_maxy', extent.yMaximum()) context.appendScope(processingScope) return context
def exposure_summary_layer(): """Helper method for retrieving exposure summary layer. If the analysis is multi-exposure, then it will return the exposure summary layer from place exposure analysis. """ project_context_scope = QgsExpressionContextUtils.projectScope( QgsProject.instance()) project = QgsProject.instance() key = provenance_layer_analysis_impacted_id['provenance_key'] analysis_summary_layer = project.mapLayer( project_context_scope.variable(key)) if not analysis_summary_layer: key = provenance_layer_analysis_impacted['provenance_key'] if project_context_scope.hasVariable(key): analysis_summary_layer = load_layer( project_context_scope.variable(key))[0] if not analysis_summary_layer: return None keywords = KeywordIO.read_keywords(analysis_summary_layer) extra_keywords = keywords.get(property_extra_keywords['key'], {}) is_multi_exposure = extra_keywords.get(extra_keyword_analysis_type['key']) key = provenance_layer_exposure_summary_id['provenance_key'] if is_multi_exposure: key = ('{provenance}__{exposure}').format( provenance=provenance_multi_exposure_summary_layers_id[ 'provenance_key'], exposure=exposure_place['key']) if not project_context_scope.hasVariable(key): return None exposure_summary_layer = project.mapLayer( project_context_scope.variable(key)) if not exposure_summary_layer: key = provenance_layer_exposure_summary['provenance_key'] if is_multi_exposure: key = ('{provenance}__{exposure}').format( provenance=provenance_multi_exposure_summary_layers[ 'provenance_key'], exposure=exposure_place['key']) if project_context_scope.hasVariable(key): exposure_summary_layer = load_layer( project_context_scope.variable(key))[0] else: return None return exposure_summary_layer
def setCustomExpression( self ): """ Initialize and show the expression builder dialog """ layer = None if len( self.tblLayers.selectedItems() ) / 3 == 1: # Single layer selected? for item in self.tblLayers.selectedItems(): if item.column() == 1: # It's the layer name item layer = QgsMapLayerRegistry.instance().mapLayer( item.data( Qt.UserRole ) ) if not self.expressionDlg: self.expressionDlg = ExpressionBuilderDialog( self.iface.mainWindow() ) context = QgsExpressionContext() context.appendScope( QgsExpressionContextUtils.globalScope() ) context.appendScope( QgsExpressionContextUtils.projectScope() ) # Initialize dialog with layer-based names and variables if single layer selected if len( self.tblLayers.selectedItems() ) / 3 == 1: context.appendScope( QgsExpressionContextUtils.layerScope( layer ) ) self.expressionDlg.expressionBuilderWidget.setLayer( layer ) self.expressionDlg.expressionBuilderWidget.loadFieldNames() # 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] ) self.expressionDlg.expressionBuilderWidget.setGeomCalculator( da ) # If this layer-field is an AutoField, get its expression if self.optExistingField.isChecked(): fieldName = self.cboField.currentText() expression = self.autoFieldManager.getFieldExpression( layer, fieldName ) self.expressionDlg.expressionBuilderWidget.setExpressionText( expression ) self.expressionDlg.expression = expression # To remember it when closing/opening self.expressionDlg.expressionBuilderWidget.setExpressionContext( context ) self.expressionDlg.show()
def aggregation_result_report(feature, parent): """Retrieve the aggregation result section from InaSAFE report. """ _ = feature, parent # NOQA project_context_scope = QgsExpressionContextUtils.projectScope() key = provenance_layer_analysis_impacted['provenance_key'] if not project_context_scope.hasVariable(key): return None analysis_dir = dirname(project_context_scope.variable(key)) complete_html_report = get_impact_report_as_string(analysis_dir) requested_html_report = get_report_section( complete_html_report, component_id=aggregation_result_component['key']) return requested_html_report
def processAlgorithm(self, parameters, context, feedback): # parameters # Database connection parameters connection_name = QgsExpressionContextUtils.projectScope( context.project()).variable('gobs_connection_name') field_spatial_object = self.parameterAsString( parameters, self.FIELD_SPATIAL_OBJECT, context) new_params_values = [] # Parse new parameters # Add add values in the correct order new_params = self.getAdditionnalParameters(context.project()) if new_params: for param in new_params: new_params_values.append( self.parameterAsString(parameters, param['name'], context)) msg = '' status = 1 # Get series id from first combo box id_serie = self.getSerieId() # Import data to temporary table feedback.pushInfo(tr('IMPORT SOURCE DATA INTO TEMPORARY TABLE')) temp_schema = 'public' temp_table = 'temp_' + str(time.time()).replace('.', '') processing.run("qgis:importintopostgis", { 'INPUT': parameters[self.SOURCELAYER], 'DATABASE': connection_name, 'SCHEMA': temp_schema, 'TABLENAME': temp_table, 'PRIMARY_KEY': 'gobs_id', 'GEOMETRY_COLUMN': None, 'ENCODING': 'UTF-8', 'OVERWRITE': True, 'CREATEINDEX': False, 'LOWERCASE_NAMES': False, 'DROP_STRING_LENGTH': True, 'FORCE_SINGLEPART': False }, context=context, feedback=feedback) feedback.pushInfo( tr('* Source layer has been imported into temporary table')) # Create import data feedback.pushInfo(tr('LOG IMPORT INTO import TABLE')) sql = ''' INSERT INTO gobs.import (im_timestamp, fk_id_series, im_status) SELECT -- import date now()::timestamp(0), -- serie id %s, --pending validation 'P' RETURNING id ; ''' % id_serie id_import = None try: [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery(connection_name, sql) if not ok: msg = tr('* The following error has been raised' ) + ' %s' % error_message feedback.reportError(msg) else: id_import = data[0][0] msg = tr('* New import data has been created with ID') msg += ' = %s !' % id_import feedback.pushInfo(msg) except Exception: msg = tr('* An unknown error occured while adding import log item') feedback.reportError(msg) # GET INFORMATION of indicator feedback.pushInfo(tr('GET DATA OF RELATED indicator')) sql = ''' SELECT id_date_format, array_to_string(id_value_type, ',') FROM gobs.indicator AS i WHERE id = ( SELECT s.fk_id_indicator FROM gobs.series AS s WHERE s.id = {0} LIMIT 1 ) ; '''.format(id_serie) id_date_format = None id_value_types = None try: [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery(connection_name, sql) if not ok: status = 0 msg = tr('* The following error has been raised' ) + ' %s' % error_message feedback.reportError(msg) else: status = 1 id_date_format = data[0][0] id_value_types = data[0][1].split(',') msg = tr('* Indicator date format is') msg += " '%s'" % id_date_format feedback.pushInfo(msg) except Exception as e: status = 0 msg = tr( '* An unknown error occured while getting indicator date format' ) feedback.reportError(msg + ' ' + str(e)) # COPY DATA TO OBSERVATION TABLE if status: feedback.pushInfo(tr('COPY IMPORTED DATA TO observation TABLE')) # Calculate value for jsonb array destination jsonb_array = 'json_build_array(' jsonb_array_list = [] for i, fieldname in enumerate(new_params_values): id_value_type = id_value_types[i] convertor_a = '' convertor_b = '' if id_value_type in ('integer', 'real'): # remove useless spaces if data is supposed to be integer or real convertor_a = "regexp_replace(" convertor_b = ", '[^0-9,\.]', '', 'g')" # NOQA vector_value = '{a}trim(s."{fieldname}"::text){b}::{value_type}'.format( a=convertor_a, fieldname=fieldname, b=convertor_b, value_type=id_value_type) jsonb_array_list.append(vector_value) jsonb_array += ', '.join(jsonb_array_list) jsonb_array += ')' # Use the correct expression for casting date and/or time caster = 'timestamp' if id_date_format in ('year', 'month', 'day'): caster = 'date' casted_timestamp = {} for k, v in self.DATE_FIELDS.items(): field_timestamp = self.parameterAsString( parameters, v['field'], context) manualdate = (self.parameterAsString(parameters, v['manual'], context)).strip().replace( '/', '-') if manualdate.strip(): manualdate = manualdate.strip().replace('/', '-') if id_date_format == 'year': manualdate = manualdate[0:4] + '-01-01' elif id_date_format == 'month': manualdate = manualdate[0:7] + '-01' elif id_date_format == 'day': manualdate = manualdate[0:10] else: manualdate = manualdate.strip() casted_timestamp[k] = ''' '{0}'::{1} '''.format(manualdate, caster) else: if field_timestamp: casted_timestamp_text = '' if id_date_format == 'year': casted_timestamp_text = ''' concat( trim(s."{0}"::text), '-01-01')::{1} '''.format(field_timestamp, caster) else: casted_timestamp_text = ''' date_trunc('{0}', s."{1}"::{2}) '''.format(id_date_format, field_timestamp, caster) casted_timestamp[k] = casted_timestamp_text else: casted_timestamp[k] = 'NULL' # We use the unique constraint to override or not the data # "observation_data_unique" UNIQUE CONSTRAINT, btree (fk_id_series, fk_id_spatial_object, ob_start_timestamp) # ob_validation is automatically set by the trigger gobs.trg_after_import_validation() # to now() when the import is validated sql = ''' INSERT INTO gobs.observation AS o ( fk_id_series, fk_id_spatial_object, fk_id_import, ob_value, ob_start_timestamp, ob_end_timestamp ) SELECT -- id of the serie {id_serie}, -- id of the spatial object so.id, -- id of the import log {id_import}, -- jsonb array value computed {jsonb_array}, -- start timestamp from the source {casted_timestamp_start}, -- end timestamp from the source {casted_timestamp_end} FROM "{temp_schema}"."{temp_table}" AS s JOIN gobs.spatial_object AS so ON True AND so.fk_id_spatial_layer = ( SELECT fk_id_spatial_layer FROM gobs.series WHERE id = {id_serie} ) AND so.so_unique_id = s."{field_spatial_object}"::text AND ( (so.so_valid_from IS NULL OR so.so_valid_from <= {casted_timestamp_start}::date) AND (so.so_valid_to IS NULL OR so.so_valid_to > {casted_timestamp_start}::date) ) '''.format(id_serie=id_serie, id_import=id_import, jsonb_array=jsonb_array, casted_timestamp_start=casted_timestamp['Start'], casted_timestamp_end=casted_timestamp['End'], temp_schema=temp_schema, temp_table=temp_table, field_spatial_object=field_spatial_object) # If end date field or manual date is given, use it in the JOIN too if casted_timestamp['End'] != 'NULL': sql += ''' AND ( (so.so_valid_from IS NULL OR so.so_valid_from <= {casted_timestamp_end}::date) AND (so.so_valid_to IS NULL OR so.so_valid_to > {casted_timestamp_end}::date) ) '''.format(casted_timestamp_end=casted_timestamp['End'], ) # Manage INSERT conflicts # If the observation has the same id_series, same start timestamp and same spatial object # The observation is modified with the new end timestamp and ob_value # This means a new observation is created when the start timestamp is different for the same series id and spatial object sql += ''' -- Update line if data already exists -- AND data is not validated ON CONFLICT ON CONSTRAINT observation_data_unique DO UPDATE SET (fk_id_import, ob_value, ob_end_timestamp) = (EXCLUDED.fk_id_import, EXCLUDED.ob_value, EXCLUDED.ob_end_timestamp) WHERE o.ob_validation IS NULL ''' # feedback.pushInfo(sql) try: [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery(connection_name, sql) if not ok: status = 0 msg = tr('* The following error has been raised' ) + ' %s' % error_message feedback.reportError(msg + ' \n' + sql) else: status = 1 msg = tr('* Source data has been successfully imported !') feedback.pushInfo(msg) except Exception: status = 0 msg = tr( '* An unknown error occured while adding features to spatial_object table' ) feedback.reportError(msg) finally: # Remove temporary table remove_temp = True if remove_temp: feedback.pushInfo(tr('DROP TEMPORARY DATA')) sql = ''' DROP TABLE IF EXISTS "%s"."%s" ; ''' % (temp_schema, temp_table) [header, data, rowCount, ok, error_message ] = fetchDataFromSqlQuery(connection_name, sql) if ok: feedback.pushInfo( tr('* Temporary data has been deleted.')) else: feedback.reportError( tr('* An error occured while droping temporary table' ) + ' "%s"."%s"' % (temp_schema, temp_table)) if not status and id_import: [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery( connection_name, 'DELETE FROM gobs.import WHERE id = %s ' % id_import) msg = tr('OBSERVATION DATA HAS BEEN SUCCESSFULLY IMPORTED !') return {self.OUTPUT_STATUS: status, self.OUTPUT_STRING: msg}
def initAlgorithm(self, config): # Database connection parameters label = tr("Connexion PostgreSQL vers la base de données") tooltip = "Base de données de destination" default = QgsExpressionContextUtils.globalScope().variable( 'veloroutes_connection_name') if Qgis.QGIS_VERSION_INT >= 31400: param = QgsProcessingParameterProviderConnection( self.CONNECTION_NAME, label, "postgres", optional=False, defaultValue=default) else: param = QgsProcessingParameterString(self.CONNECTION_NAME, label, defaultValue=default) param.setMetadata({ "widget_wrapper": { "class": "processing.gui.wrappers_postgis.ConnectionWidgetWrapper" } }) if Qgis.QGIS_VERSION_INT >= 31600: param.setHelp(tooltip) else: param.tooltip_3liz = tooltip self.addParameter(param) label = tr("Schéma") tooltip = 'Nom du schéma des données véloroutes et voies vertes' default = 'veloroutes' if Qgis.QGIS_VERSION_INT >= 31400: param = QgsProcessingParameterDatabaseSchema( self.SCHEMA, label, self.CONNECTION_NAME, defaultValue=default, optional=False, ) else: param = QgsProcessingParameterString(self.SCHEMA, label, default, False, True) param.setMetadata({ "widget_wrapper": { "class": "processing.gui.wrappers_postgis.SchemaWidgetWrapper", "connection_param": self.CONNECTION_NAME, } }) if Qgis.QGIS_VERSION_INT >= 31600: param.setHelp(tooltip) else: param.tooltip_3liz = tooltip self.addParameter(param) # OUTPUTS # Add output for status (integer) output = QgsProcessingOutputNumber(self.OUTPUT_STATUS, 'Statut de sortie') output.tooltip_3liz = output.description() self.addOutput(output) # Add output for message output = QgsProcessingOutputString(self.OUTPUT_STRING, 'Message de sortie') output.tooltip_3liz = output.description() self.addOutput(output)
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 feature_validator(feature, layer): """Validate a QGIS feature by checking QGIS fields constraints The logic here is to: - if geometry is not None check if geometry type matches the layer type - loop through the fields and check for constraints: - NOT NULL, skip the next check if this fails - UNIQUE (only if not NULL) - EXPRESSION (QgsExpression configured in the form), always evaluated, even in case of NULLs Note: only hard constraints are checked! :param feature: QGIS feature :type feature: QgsFeature :param layer: QGIS layer :type layer: QgsVectorLayer :return: a dictionary of errors for each field + geometry :rtype: dict """ errors = dict() geometry = feature.geometry() data_provider = layer.dataProvider() def _has_default_value(field_index, field): return ( # Provider level data_provider.defaultValueClause(field_index) or data_provider.defaultValue(field_index) or field.defaultValueDefinition().isValid() ) # Check geometry type if not geometry.isNull() and geometry.wkbType() != layer.wkbType(): errors['geometry'] = _('Feature geometry type %s does not match layer type: %s') % ( QgsWkbTypes.displayString(geometry.wkbType()), QgsWkbTypes.displayString(layer.wkbType())) def _set_error(field_name, error): if not field_name in errors: errors[field_name] = [] errors[field_name].append(error) # Check fields "hard" constraints for field_index in range(layer.fields().count()): field = layer.fields().field(field_index) # check if fields is a join field: if layer.fields().fieldOrigin(field_index) == QgsFields.OriginJoin: continue # Check not null first, if it fails skip other tests (unique and expression) value = feature.attribute(field.name()) # If there is a default value we assume it's not NULL (we cannot really know at this point # what will be the result of the default value clause evaluation, it might even be provider-sideu if (value is None or value == QVariant()) and not _has_default_value(field_index, field): not_null = (field.constraints().constraintOrigin( QgsFieldConstraints.ConstraintNotNull) != QgsFieldConstraints.ConstraintOriginNotSet and field.constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull) == QgsFieldConstraints.ConstraintStrengthHard) if not_null: _set_error(field.name(), _( 'Field value must be NOT NULL')) continue value = feature.attribute(field_index) # Skip if NULL, not sure if we want to continue in this case but it seems pointless # to check for unique or type compatibility on NULLs if value is not None and value != QVariant(): if not QVariant(value).convert(field.type()): _set_error(field.name(), _( 'Field value \'%s\' cannot be converted to %s') % (value, QVariant.typeToName(field.type()))) unique = (field.constraints().constraintOrigin( QgsFieldConstraints.ConstraintUnique) != QgsFieldConstraints.ConstraintOriginNotSet and field.constraints().constraintStrength(QgsFieldConstraints.ConstraintUnique) == QgsFieldConstraints.ConstraintStrengthHard) if unique: # Search for features, excluding self if it's an update request = QgsFeatureRequest() request.setNoAttributes() request.setFlags(QgsFeatureRequest.NoGeometry) request.setLimit(2) if field.isNumeric(): request.setFilterExpression('"%s" = %s' % ( field.name().replace('"', '\\"'), value)) elif field.type() == QVariant.String: request.setFilterExpression('"%s" = \'%s\'' % ( field.name().replace('"', '\\"'), value.replace("'", "\\'"))) elif field.type() == QVariant.Date: request.setFilterExpression('to_date("%s") = \'%s\'' % ( field.name().replace('"', '\\"'), value.toString(Qt.ISODate))) elif field.type() == QVariant.DateTime: request.setFilterExpression('to_datetime("%s") = \'%s\'' % ( field.name().replace('"', '\\"'), value.toString(Qt.ISODate))) elif field.type() == QVariant.Bool: # This does not make any sense, but still request.setFilterExpression('"%s" = %s' % ( field.name().replace('"', '\\"'), 'true' if value else 'false')) else: # All the other formats: let's convert to string and hope for the best request.setFilterExpression('"%s" = \'%s\'' % ( field.name().replace('"', '\\"'), value.toString())) # Exclude same feature by id found = [f.id() for f in layer.getFeatures( request) if f.id() != feature.id()] if len(found) > 0: _set_error(field.name(), _( 'Field value must be UNIQUE')) # Check for expressions, even in case of NULL because expressions may want to check for combined # conditions on multiple fields expression = (field.constraints().constraintOrigin( QgsFieldConstraints.ConstraintExpression) != QgsFieldConstraints.ConstraintOriginNotSet and field.constraints().constraintStrength(QgsFieldConstraints.ConstraintExpression) == QgsFieldConstraints.ConstraintStrengthHard) if expression: constraints = field.constraints() expression = constraints.constraintExpression() description = constraints.constraintDescription() value = feature.attribute(field_index) exp = QgsExpression(expression) context = QgsExpressionContextUtils.createFeatureBasedContext( feature, layer.fields()) context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes(layer)) if not bool(exp.evaluate(context)): if not description: description = _('Expression check violation') _set_error(field.name(), _("%s Expression: %s") % (description, expression)) return errors
def processAlgorithm(self, parameters, context, feedback): if DEBUG_MODE: logMessage( "processAlgorithm(): {}".format(self.__class__.__name__), False) clayer = self.parameterAsLayer(parameters, self.INPUT, context) title_field = self.parameterAsString(parameters, self.TITLE_FIELD, context) cf_filter = self.parameterAsBool(parameters, self.CF_FILTER, context) fixed_scale = self.parameterAsEnum(parameters, self.SCALE, context) # == 1 buf = self.parameterAsDouble(parameters, self.BUFFER, context) tex_width = self.parameterAsInt(parameters, self.TEX_WIDTH, context) orig_tex_height = self.parameterAsInt(parameters, self.TEX_HEIGHT, context) header_exp = QgsExpression( self.parameterAsExpression(parameters, self.HEADER, context)) footer_exp = QgsExpression( self.parameterAsExpression(parameters, self.FOOTER, context)) exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.layerScope(clayer)) out_dir = self.parameterAsString(parameters, self.OUTPUT, context) if not QDir(out_dir).exists(): QDir().mkpath(out_dir) if DEBUG_MODE: openDirectory(out_dir) mapSettings = self.settings.mapSettings baseExtent = self.settings.baseExtent rotation = mapSettings.rotation() orig_size = mapSettings.outputSize() if cf_filter: cf_layer = QgsMemoryProviderUtils.createMemoryLayer( "current feature", clayer.fields(), clayer.wkbType(), clayer.crs()) layers = [ cf_layer if lyr == clayer else lyr for lyr in mapSettings.layers() ] mapSettings.setLayers(layers) doc = QDomDocument("qgis") clayer.exportNamedStyle(doc) cf_layer.importNamedStyle(doc) total = clayer.featureCount() for current, feature in enumerate(clayer.getFeatures()): if feedback.isCanceled(): break if cf_filter: cf_layer.startEditing() cf_layer.deleteFeatures( [f.id() for f in cf_layer.getFeatures()]) cf_layer.addFeature(feature) cf_layer.commitChanges() title = feature.attribute(title_field) feedback.setProgressText("({}/{}) Exporting {}...".format( current + 1, total, title)) logMessage("Exporting {}...".format(title), False) # extent geometry = QgsGeometry(feature.geometry()) geometry.transform(self.transform) center = geometry.centroid().asPoint() if fixed_scale or geometry.type() == QgsWkbTypes.PointGeometry: tex_height = orig_tex_height or int( tex_width * orig_size.height() / orig_size.width()) extent = MapExtent(center, baseExtent.width(), baseExtent.width() * tex_height / tex_width, rotation).scale(1 + buf / 100) else: geometry.rotate(rotation, center) rect = geometry.boundingBox().scaled(1 + buf / 100) center = MapExtent.rotateQgsPoint(rect.center(), rotation, center) if orig_tex_height: tex_height = orig_tex_height tex_ratio = tex_width / tex_height rect_ratio = rect.width() / rect.height() if tex_ratio > rect_ratio: extent = MapExtent(center, rect.height() * tex_ratio, rect.height(), rotation) else: extent = MapExtent(center, rect.width(), rect.width() / tex_ratio, rotation) else: # fit to buffered geometry bounding box extent = MapExtent(center, rect.width(), rect.height(), rotation) tex_height = tex_width * rect.height() / rect.width() extent.toMapSettings(mapSettings) mapSettings.setOutputSize(QSize(tex_width, tex_height)) self.settings.setMapSettings(mapSettings) # labels exp_context.setFeature(feature) self.settings.setHeaderLabel(header_exp.evaluate(exp_context)) self.settings.setFooterLabel(footer_exp.evaluate(exp_context)) self.export(title, out_dir, feedback) feedback.setProgress(int(current / total * 100)) if P_OPEN_DIRECTORY and not DEBUG_MODE: openDirectory(out_dir) return {}
def 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 download_tiles(self, force=None): #calculate zoom_level con current canvas extents ex = self.iface.mapCanvas().extent() wgs84_minimum = self.transformToWGS84( QgsPointXY(ex.xMinimum(), ex.yMinimum())) wgs84_maximum = self.transformToWGS84( QgsPointXY(ex.xMaximum(), ex.yMaximum())) bounds = (wgs84_minimum.x(), wgs84_minimum.y(), wgs84_maximum.x(), wgs84_maximum.y()) map_units_per_pixel = (wgs84_maximum.x() - wgs84_minimum.x() ) / self.iface.mapCanvas().width() zoom_level = ZoomForPixelSize(map_units_per_pixel) if zoom_level > 14: zoom_level = 14 try: ranges = getTileRange(bounds, zoom_level) except ValueError: return if not self.actual_ranges or not ( ranges[0][0] == self.actual_ranges[0][0] and ranges[0][1] == self.actual_ranges[0][1] and ranges[1][0] == self.actual_ranges[1][0] and ranges[1][1] == self.actual_ranges[1][1]): #print ("ZOOM_LEVEL", zoom_level, "NEW RANGES", ranges, "LAST RANGES", self.actual_ranges) self.actual_ranges = ranges x_range = ranges[0] y_range = ranges[1] overview_features = [] sequences_features = [] images_features = [] progress = progressBar(self, 'go2mapillary') start_time = datetime.datetime.now() for y in range(y_range[0], y_range[1] + 1): for x in range(x_range[0], x_range[1] + 1): folderPath = os.path.join(self.cache_dir, str(zoom_level), str(x)) filePathMvt = os.path.join(folderPath, str(y) + '.mvt') #filePathJson = os.path.join(folderPath, str(y) + '.json') if not os.path.exists(folderPath): os.makedirs(folderPath) res = None if not os.path.exists(filePathMvt) or ( datetime.datetime.fromtimestamp( os.path.getmtime(filePathMvt)) < (datetime.datetime.now() - self.expire_time)): # make the URL url = getURL(x, y, zoom_level, SERVER_URL) with open(filePathMvt, 'wb') as f: response = requests.get(url, proxies=getProxiesConf(), stream=True) total_length = response.headers.get( 'content-length') if total_length is None: # no content length header f.write(response.content) else: dl = 0 total_length = int(total_length) progress.start( total_length, 'caching vector tile [%d,%d,%d]' % (x, y, zoom_level)) QgsMessageLog.logMessage("MISS [%d,%d,%d]" % (x, y, zoom_level), tag="go2mapillary", level=Qgis.Info) for data in response.iter_content( chunk_size=4096): dl += len(data) f.write(data) progress.setProgress(dl) if os.path.exists(filePathMvt): progress.start( 0, 'loading vector tile [%d,%d,%d]' % (x, y, zoom_level)) if not res: with open(filePathMvt, "rb") as f: mvt = f.read() QgsMessageLog.logMessage("CACHE [%d,%d,%d]" % (x, y, zoom_level), tag="go2mapillary", level=Qgis.Info) else: mvt = res.content bounds = mercantile.bounds(x, y, zoom_level) tile_box = (bounds.west, bounds.south, bounds.east, bounds.north) json_data = mapbox_vector_tile.decode( mvt, quantize_bounds=tile_box) if "mapillary-sequence-overview" in json_data: overview_features = overview_features + json_data[ "mapillary-sequence-overview"]["features"] elif "mapillary-sequences" in json_data: sequences_features = sequences_features + json_data[ "mapillary-sequences"]["features"] if "mapillary-images" in json_data and zoom_level == 14: images_features = images_features + json_data[ "mapillary-images"]["features"] print("loading time", datetime.datetime.now() - start_time) progress.stop('loading complete') for level in LAYER_LEVELS: geojson_file = os.path.join(self.cache_dir, "mapillary_%s.geojson" % level) try: QgsProject.instance().removeMapLayer( getattr(self, level + 'Layer').id()) except: pass if locals()[level + '_features']: setattr(self, level, True) geojson = { "type": "FeatureCollection", "features": locals()[level + '_features'] } with open(geojson_file, 'w') as outfile: json.dump(geojson, outfile) defLyr = QgsVectorLayer( os.path.join(self.cache_dir, 'mapillary_%s.geojson' % level), "Mapillary " + level, "ogr") defLyr.loadNamedStyle( os.path.join(os.path.dirname(__file__), "res", "mapillary_%s.qml" % level)) QgsExpressionContextUtils.setLayerVariable( defLyr, "mapillaryCurrentKey", self.module.viewer.locationKey) defLyr.setCrs(QgsCoordinateReferenceSystem(4326)) QgsProject.instance().addMapLayer(defLyr) self.iface.addCustomActionForLayerType( getattr(self.module, 'filterAction_' + level), None, QgsMapLayer.VectorLayer, allLayers=False) self.module.filterDialog.applySqlFilter(layer=defLyr) self.iface.addCustomActionForLayer( getattr(self.module, 'filterAction_' + level), defLyr) legendLayerNode = QgsProject.instance().layerTreeRoot( ).findLayer(defLyr.id()) legendLayerNode.setExpanded(False) setattr(self, level + 'Layer', defLyr) else: setattr(self, level, False) else: pass
def photoDetails_field(self, restrictionDialog, currRestrictionLayer, currRestriction): # Function to deal with photo fields self.demandDialog = restrictionDialog self.currDemandLayer = currRestrictionLayer self.currFeature = currRestriction TOMsMessageLog.logMessage("In photoDetails", level=Qgis.Info) photoPath = QgsExpressionContextUtils.projectScope(QgsProject.instance()).variable('PhotoPath') projectFolder = QgsExpressionContextUtils.projectScope(QgsProject.instance()).variable('project_folder') path_absolute = os.path.join(projectFolder, photoPath) if path_absolute == None: reply = QMessageBox.information(None, "Information", "Please set value for PhotoPath.", QMessageBox.Ok) return # Check path exists ... if os.path.isdir(path_absolute) == False: reply = QMessageBox.information(None, "Information", "PhotoPath folder " + str( path_absolute) + " does not exist. Please check value.", QMessageBox.Ok) return # if cv2 is available, check camera nr try: cameraNr = int(self.params.setParam("CameraNr")) except Exception as e: TOMsMessageLog.logMessage("In photoDetails_field: cameraNr issue: {}".format(e), level=Qgis.Info) if cv2_available: cameraNr = QMessageBox.information(None, "Information", "Please set value for CameraNr.", QMessageBox.Ok) cameraNr = None TOMsMessageLog.logMessage("In photoDetails_field: cameraNr is: {}".format(cameraNr), level=Qgis.Info) layerName = self.currDemandLayer.name() # Generate the full path to the file fileName1 = "Photos_01" fileName2 = "Photos_02" fileName3 = "Photos_03" idx1 = self.currDemandLayer.fields().indexFromName(fileName1) idx2 = self.currDemandLayer.fields().indexFromName(fileName2) idx3 = self.currDemandLayer.fields().indexFromName(fileName3) TOMsMessageLog.logMessage("In photoDetails. idx1: " + str(idx1) + "; " + str(idx2) + "; " + str(idx3), level=Qgis.Info) if cameraNr is not None: TOMsMessageLog.logMessage("Camera TRUE", level=Qgis.Info) takePhoto = True else: TOMsMessageLog.logMessage("Camera FALSE", level=Qgis.Info) takePhoto = False FIELD1 = self.demandDialog.findChild(QLabel, "Photo_Widget_01") FIELD2 = self.demandDialog.findChild(QLabel, "Photo_Widget_02") FIELD3 = self.demandDialog.findChild(QLabel, "Photo_Widget_03") if FIELD1: TOMsMessageLog.logMessage("In photoDetails. FIELD 1 exists", level=Qgis.Info) if self.currFeature[idx1]: newPhotoFileName1 = os.path.join(path_absolute, self.currFeature[idx1]) TOMsMessageLog.logMessage("In photoDetails. photo1: {}".format(newPhotoFileName1), level=Qgis.Info) else: newPhotoFileName1 = None pixmap1 = QPixmap(newPhotoFileName1) if pixmap1.isNull(): pass # FIELD1.setText('Picture could not be opened ({path})'.format(path=newPhotoFileName1)) else: tab = FIELD1.parentWidget() grid = FIELD1.parentWidget().layout() photo_Widget1 = imageLabel(tab) TOMsMessageLog.logMessage( "In photoDetails. FIELD 1 w: {}; h: {}".format(FIELD1.width(), FIELD1.height()), level=Qgis.Info) photo_Widget1.setObjectName("Photo_Widget_01") photo_Widget1.setText("No photo is here") #photo_Widget1 = imageLabel(tab) grid.addWidget(photo_Widget1, 0, 0, 1, 1) FIELD1.hide() FIELD1.setParent(None) FIELD1 = photo_Widget1 FIELD1.set_Pixmap(pixmap1) TOMsMessageLog.logMessage("In photoDetails. FIELD 1 Photo1: " + str(newPhotoFileName1), level=Qgis.Info) TOMsMessageLog.logMessage("In photoDetails.pixmap1 size: {}".format(pixmap1.size()), level=Qgis.Info) FIELD1.pixmapUpdated.connect(functools.partial(self.displayPixmapUpdated, FIELD1)) #ZOOM_IN_1 = self.demandDialog.findChild(QPushButton, "pb_zoomIn_01") #ZOOM_IN_1.clicked.connect(FIELD1._zoomInButton) #ZOOM_OUT_1 = self.demandDialog.findChild(QPushButton, "pb_zoomOut_01") #ZOOM_OUT_1.clicked.connect(FIELD1._zoomOutButton) if takePhoto: START_CAMERA_1 = self.demandDialog.findChild(QPushButton, "startCamera1") TAKE_PHOTO_1 = self.demandDialog.findChild(QPushButton, "getPhoto1") TAKE_PHOTO_1.setEnabled(False) self.camera1 = formCamera(path_absolute, newPhotoFileName1, cameraNr) START_CAMERA_1.clicked.connect( functools.partial(self.camera1.useCamera, START_CAMERA_1, TAKE_PHOTO_1, FIELD1)) self.camera1.notifyPhotoTaken.connect(functools.partial(self.savePhotoTaken, idx1)) if FIELD2: TOMsMessageLog.logMessage("In photoDetails. FIELD 2 exisits", level=Qgis.Info) if self.currFeature[idx2]: newPhotoFileName2 = os.path.join(path_absolute, self.currFeature[idx2]) TOMsMessageLog.logMessage("In photoDetails. Photo1: " + str(newPhotoFileName2), level=Qgis.Info) else: newPhotoFileName2 = None # newPhotoFileName2 = os.path.join(path_absolute, str(self.currFeature[idx2])) # newPhotoFileName2 = os.path.join(path_absolute, str(self.currFeature.attribute(fileName2))) # TOMsMessageLog.logMessage("In photoDetails. Photo2: " + str(newPhotoFileName2), level=Qgis.Info) pixmap2 = QPixmap(newPhotoFileName2) if pixmap2.isNull(): pass # FIELD1.setText('Picture could not be opened ({path})'.format(path=newPhotoFileName1)) else: tab = FIELD2.parentWidget() grid = FIELD2.parentWidget().layout() photo_Widget2 = imageLabel(tab) TOMsMessageLog.logMessage( "In photoDetails. FIELD 2 w: {}; h: {}".format(FIELD2.width(), FIELD2.height()), level=Qgis.Info) photo_Widget2.setObjectName("Photo_Widget_02") photo_Widget2.setText("No photo is here") #photo_Widget2 = imageLabel(tab) grid.addWidget(photo_Widget2, 0, 0, 1, 1) FIELD2.hide() FIELD2.setParent(None) FIELD2 = photo_Widget2 FIELD2.set_Pixmap(pixmap2) TOMsMessageLog.logMessage("In photoDetails. FIELD 2 Photo2: " + str(newPhotoFileName2), level=Qgis.Info) TOMsMessageLog.logMessage("In photoDetails.pixmap2 size: {}".format(pixmap2.size()), level=Qgis.Info) FIELD2.pixmapUpdated.connect(functools.partial(self.displayPixmapUpdated, FIELD2)) #ZOOM_IN_2 = self.demandDialog.findChild(QPushButton, "pb_zoomIn_02") #ZOOM_IN_2.clicked.connect(FIELD2._zoomInButton) #ZOOM_OUT_2 = self.demandDialog.findChild(QPushButton, "pb_zoomOut_02") #ZOOM_OUT_2.clicked.connect(FIELD2._zoomOutButton) """ FIELD2.setPixmap(pixmap2) FIELD2.setScaledContents(True) TOMsMessageLog.logMessage("In photoDetails. Photo2: " + str(newPhotoFileName2), level=Qgis.Info)""" if takePhoto: START_CAMERA_2 = self.demandDialog.findChild(QPushButton, "startCamera2") TAKE_PHOTO_2 = self.demandDialog.findChild(QPushButton, "getPhoto2") TAKE_PHOTO_2.setEnabled(False) self.camera2 = formCamera(path_absolute, newPhotoFileName2, cameraNr) START_CAMERA_2.clicked.connect( functools.partial(self.camera2.useCamera, START_CAMERA_2, TAKE_PHOTO_2, FIELD2)) self.camera2.notifyPhotoTaken.connect(functools.partial(self.savePhotoTaken, idx2)) if FIELD3: TOMsMessageLog.logMessage("In photoDetails. FIELD 3 exisits", level=Qgis.Info) if self.currFeature[idx3]: newPhotoFileName3 = os.path.join(path_absolute, self.currFeature[idx3]) TOMsMessageLog.logMessage("In photoDetails. Photo1: " + str(newPhotoFileName3), level=Qgis.Info) else: newPhotoFileName3 = None # newPhotoFileName3 = os.path.join(path_absolute, str(self.currFeature[idx3])) # newPhotoFileName3 = os.path.join(path_absolute, # str(self.currFeature.attribute(fileName3))) # newPhotoFileName3 = os.path.join(path_absolute, str(layerName + "_Photos_03")) # TOMsMessageLog.logMessage("In photoDetails. Photo3: " + str(newPhotoFileName3), level=Qgis.Info) pixmap3 = QPixmap(newPhotoFileName3) if pixmap3.isNull(): pass # FIELD1.setText('Picture could not be opened ({path})'.format(path=newPhotoFileName1)) else: tab = FIELD3.parentWidget() grid = FIELD3.parentWidget().layout() photo_Widget3 = imageLabel(tab) TOMsMessageLog.logMessage( "In photoDetails. FIELD 3 w: {}; h: {}".format(FIELD3.width(), FIELD3.height()), level=Qgis.Info) photo_Widget3.setObjectName("Photo_Widget_03") photo_Widget3.setText("No photo is here") #photo_Widget3 = imageLabel(tab) grid.addWidget(photo_Widget3, 0, 0, 1, 1) FIELD3.hide() FIELD3.setParent(None) FIELD3 = photo_Widget3 FIELD3.set_Pixmap(pixmap3) TOMsMessageLog.logMessage("In photoDetails. FIELD 3 Photo3: " + str(newPhotoFileName3), level=Qgis.Info) TOMsMessageLog.logMessage("In photoDetails.pixmap3 size: {}".format(pixmap3.size()), level=Qgis.Info) FIELD3.pixmapUpdated.connect(functools.partial(self.displayPixmapUpdated, FIELD3)) #ZOOM_IN_3 = self.demandDialog.findChild(QPushButton, "pb_zoomIn_03") #ZOOM_IN_3.clicked.connect(FIELD3._zoomInButton) #ZOOM_OUT_3 = self.demandDialog.findChild(QPushButton, "pb_zoomOut_03") #ZOOM_OUT_3.clicked.connect(FIELD3._zoomOutButton) """FIELD3.setPixmap(pixmap3) FIELD3.setScaledContents(True) TOMsMessageLog.logMessage("In photoDetails. Photo3: " + str(newPhotoFileName3), level=Qgis.Info)""" if takePhoto: START_CAMERA_3 = self.demandDialog.findChild(QPushButton, "startCamera3") TAKE_PHOTO_3 = self.demandDialog.findChild(QPushButton, "getPhoto3") TAKE_PHOTO_3.setEnabled(False) self.camera3 = formCamera(path_absolute, newPhotoFileName3, cameraNr) START_CAMERA_3.clicked.connect( functools.partial(self.camera3.useCamera, START_CAMERA_3, TAKE_PHOTO_3, FIELD3)) self.camera3.notifyPhotoTaken.connect(functools.partial(self.savePhotoTaken, idx3)) pass
def test_MatchesReturnsTrueForComplexMatch(self): style = QgsConditionalStyle("@value > 10 and @value = 20") context = QgsExpressionContextUtils.createFeatureBasedContext(QgsFeature(), QgsFields()) assert style.matches(20, context)
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ # Get input vector layer vlayer = self.parameterAsVectorLayer(parameters, self.INPUT_LAYER, context) self.layer = vlayer symbol_level = parameters[self.SYMBOL_LEVEL] self.symbol_level = symbol_level color_field = parameters[self.COLOR_FIELD] virtual_color_field = parameters[self.VIRTUAL_COLOR_FIELD] label_field = parameters[self.LABEL_FIELD] virtual_label_field = parameters[self.VIRTUAL_LABEL_FIELD] # Send some information to the user feedback.pushInfo('Layer is {}'.format(vlayer.name())) # Compute symbol based expressions color_expression = self.getColorExpressionFromSymbology() label_expression = self.getLabelExpressionFromSymbology() if not color_expression or not label_expression: raise QgsProcessingException( self. tr('Color expression or label expression cannot be generated')) # Modify features # Start an undo block if color_field and label_field: # Compute the number of steps to display within the progress bar and # get features from source total = 100.0 / vlayer.featureCount() if vlayer.featureCount( ) else 0 # prepare expression exp_context = QgsExpressionContext() exp_context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes(vlayer)) # color_expression = "concat('hop ', pcolor)" color_exp = QgsExpression(color_expression) color_idx = vlayer.fields().indexFromName(color_field) label_exp = QgsExpression(label_expression) label_idx = vlayer.fields().indexFromName(label_field) vlayer.beginEditCommand('Translating all features') features = vlayer.getFeatures(QgsFeatureRequest().setFlags( QgsFeatureRequest.NoGeometry)) # .setSubsetOfAttributes([color_idx, label_idx]) ) for current, feature in enumerate(features): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break # Edit feature exp_context.setFeature(feature) # color color_val = color_exp.evaluate(exp_context) print(color_val) vlayer.changeAttributeValue(feature.id(), color_idx, color_val) # label label_val = label_exp.evaluate(exp_context) print(label_val) vlayer.changeAttributeValue(feature.id(), label_idx, label_val) # Update the progress bar feedback.setProgress(int(current * total)) if virtual_color_field: self.createOrUpdateLayerExpressionField(virtual_color_field, color_expression) if virtual_label_field: self.createOrUpdateLayerExpressionField(virtual_label_field, label_expression) # End the undo block vlayer.endEditCommand() return {self.OUTPUT: 'ok'}
def populateByExpression(self, adding=False): """ Populates the panel using an expression """ context = dataobjects.createContext() expression_context = context.expressionContext() # use the first row parameter values as a preview during expression creation params, ok = self.panel.parametersForRow(0, warnOnInvalid=False) alg_scope = QgsExpressionContextUtils.processingAlgorithmScope( self.panel.alg, params, context) # create explicit variables corresponding to every parameter for k, v in params.items(): alg_scope.setVariable(k, v, True) # add batchCount in the alg scope to be used in the expressions. 0 is only an example value alg_scope.setVariable('row_number', 0, False) expression_context.appendScope(alg_scope) # mark the parameter variables as highlighted for discoverability highlighted_vars = expression_context.highlightedVariables() highlighted_vars.extend(list(params.keys())) highlighted_vars.append('row_number') expression_context.setHighlightedVariables(highlighted_vars) dlg = QgsExpressionBuilderDialog(layer=None, context=context.expressionContext()) if adding: dlg.setExpectedOutputFormat( self.tr( 'An array of values corresponding to each new row to add')) if not dlg.exec_(): return if adding: exp = QgsExpression(dlg.expressionText()) res = exp.evaluate(expression_context) if type(res) is not list: res = [res] first_row = self.panel.batchRowCount( ) if self.panel.batchRowCount() > 1 else 0 for row, value in enumerate(res): self.setRowValue(row + first_row, value, context) else: for row in range(self.panel.batchRowCount()): params, ok = self.panel.parametersForRow(row, warnOnInvalid=False) # remove previous algorithm scope -- we need to rebuild this completely, using the # other parameter values from the current row expression_context.popScope() alg_scope = QgsExpressionContextUtils.processingAlgorithmScope( self.panel.alg, params, context) for k, v in params.items(): alg_scope.setVariable(k, v, True) # add batch row number as evaluable variable in algorithm scope alg_scope.setVariable('row_number', row, False) expression_context.appendScope(alg_scope) # rebuild a new expression every time -- we don't want the expression compiler to replace # variables with precompiled values exp = QgsExpression(dlg.expressionText()) value = exp.evaluate(expression_context) self.setRowValue(row, value, context)
def processAlgorithm(self, feedback): layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_LAYER)) fieldName = self.getParameterValue(self.FIELD_NAME) fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)] width = self.getParameterValue(self.FIELD_LENGTH) precision = self.getParameterValue(self.FIELD_PRECISION) newField = self.getParameterValue(self.NEW_FIELD) formula = self.getParameterValue(self.FORMULA) output = self.getOutputFromName(self.OUTPUT_LAYER) fields = layer.fields() if newField: fields.append(QgsField(fieldName, fieldType, '', width, precision)) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs()) exp = QgsExpression(formula) da = QgsDistanceArea() da.setSourceCrs(layer.crs()) da.setEllipsoidalMode(True) da.setEllipsoid(QgsProject.instance().ellipsoid()) exp.setGeomCalculator(da) exp.setDistanceUnits(QgsProject.instance().distanceUnits()) exp.setAreaUnits(QgsProject.instance().areaUnits()) exp_context = QgsExpressionContext(QgsExpressionContextUtils.globalProjectLayerScopes(layer)) if not exp.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: {0}').format(exp.evalErrorString())) outFeature = QgsFeature() outFeature.initAttributes(len(fields)) outFeature.setFields(fields) error = '' calculationSuccess = True features = vector.features(layer) total = 100.0 / len(features) rownum = 1 for current, f in enumerate(features): rownum = current + 1 exp_context.setFeature(f) exp_context.lastScope().setVariable("row_number", rownum) value = exp.evaluate(exp_context) if exp.hasEvalError(): calculationSuccess = False error = exp.evalErrorString() break else: outFeature.setGeometry(f.geometry()) for fld in f.fields(): outFeature[fld.name()] = f[fld.name()] outFeature[fieldName] = value writer.addFeature(outFeature) feedback.setProgress(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation ' 'string:\n{0}').format(error))
def processAlgorithm(self, parameters, context, feedback): # parameters connection_name = QgsExpressionContextUtils.globalScope().variable('gobs_connection_name') delete_series = self.parameterAsBool(parameters, self.DELETE_SERIES, context) # Get series id from first combo box serie = self.SERIES[parameters[self.SERIE]] id_serie = int(self.SERIES_DICT[serie]) # Override series is from second number input serie_id = self.parameterAsInt(parameters, self.SERIE_ID, context) if serie_id in self.SERIES_DICT.values(): id_serie = serie_id sql = ''' DELETE FROM gobs.observation WHERE fk_id_series = {0}; SELECT setval( pg_get_serial_sequence('gobs.observation', 'id'), coalesce(max(id),0) + 1, false ) FROM gobs.observation; DELETE FROM gobs.import WHERE fk_id_series = {0}; SELECT setval( pg_get_serial_sequence('gobs.import', 'id'), coalesce(max(id),0) + 1, false ) FROM gobs.import; '''.format( id_serie ) if delete_series: sql+= ''' DELETE FROM gobs.series WHERE id = {0}; SELECT setval( pg_get_serial_sequence('gobs.series', 'id'), coalesce(max(id),0) + 1, false ) FROM gobs.series; '''.format( id_serie ) [header, data, rowCount, ok, message] = fetchDataFromSqlQuery( connection_name, sql ) if ok: message = tr('Data has been deleted for the chosen series') if delete_series: message+= '. '+ tr('The series has also been deleted') feedback.pushInfo( message ) else: raise QgsProcessingException(message) status = 1 return { self.OUTPUT_STATUS: status, self.OUTPUT_STRING: message }
def initAlgorithm(self, config): # INPUTS connection_name = QgsExpressionContextUtils.globalScope().variable('gobs_connection_name') get_data = QgsExpressionContextUtils.globalScope().variable('gobs_get_database_data') # List of series sql = ''' SELECT s.id, concat( id_label, ' (', p.pr_label, ')', ' / Source: ', a_label, ' / Layer: ', sl_label ) AS label FROM gobs.series s INNER JOIN gobs.protocol p ON p.id = s.fk_id_protocol INNER JOIN gobs.actor a ON a.id = s.fk_id_actor INNER JOIN gobs.indicator i ON i.id = s.fk_id_indicator INNER JOIN gobs.spatial_layer sl ON sl.id = s.fk_id_spatial_layer ORDER BY label ''' dbpluginclass = createDbPlugin('postgis') connections = [c.connectionName() for c in dbpluginclass.connections()] data = [] if get_data == 'yes' and connection_name in connections: [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery( connection_name, sql ) self.SERIES = ['%s' % a[1] for a in data] self.SERIES_DICT = {a[1]: a[0] for a in data} self.addParameter( QgsProcessingParameterEnum( self.SERIE, tr('Series of observations'), options=self.SERIES, optional=False ) ) # Id of series, to get the series directly # mainly used from other processing algs p = QgsProcessingParameterNumber( self.SERIE_ID, tr('Series ID. If given, it overrides previous choice'), optional=True, defaultValue=-1 ) p.setFlags(QgsProcessingParameterDefinition.FlagHidden) self.addParameter(p) # Confirmation self.addParameter( QgsProcessingParameterBoolean( self.RUN_DELETE, tr('Check this box to delete. No action will be done otherwise'), defaultValue=False, ) ) # Delete the series self.addParameter( QgsProcessingParameterBoolean( self.DELETE_SERIES, tr('Also delete the series item'), defaultValue=False, ) ) # OUTPUTS # Add output for status (integer) self.addOutput( QgsProcessingOutputNumber( self.OUTPUT_STATUS, tr('Output status') ) ) # Add output for message self.addOutput( QgsProcessingOutputString( self.OUTPUT_STRING, tr('Output message') ) )
def replaceExpressionText(self, params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Replace expression texts against layer or features In parameters: LAYER=wms-layer-name STRING=A string with expression between [% and %] or STRINGS=["first string with expression", "second string with expression"] or STRINGS={"key1": "first string with expression", "key2": "second string with expression"} // optionals FEATURE={"type": "Feature", "geometry": {}, "properties": {}} or FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {}, "properties": {}}] FORM_SCOPE=boolean to add formScope based on provided features """ layername = params.get('LAYER', '') if not layername: raise ExpressionServiceError( "Bad request error", "Invalid 'ReplaceExpressionText' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = findVectorLayer(layername, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'ReplaceExpressionText': {} provided" .format(layername), 400) # get strings strings = params.get('STRINGS', '') if not strings: the_string = params.get('STRING', '') if not the_string: raise ExpressionServiceError( "Bad request error", "Invalid 'ReplaceExpressionText' REQUEST: STRING or STRINGS parameter is mandatory", 400) strings = '["{}"]'.format(the_string) # try to load expressions list or dict str_json = None try: str_json = json.loads(strings) except Exception: QgsMessageLog.logMessage( "JSON loads strings '{}' exception:\n{}".format( strings, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'ReplaceExpressionText' REQUEST: STRINGS '{}' are not well formed" .format(strings), 400) # get features features = params.get('FEATURES', '') if not features: feature = params.get('FEATURE', '') if feature: features = '[' + feature + ']' # create expression context exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope( QgsExpressionContextUtils.projectScope(project)) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) # create distance area context da = QgsDistanceArea() da.setSourceCrs(layer.crs(), project.transformContext()) da.setEllipsoid(project.ellipsoid()) # organized strings str_map = {} str_items = [] if isinstance(str_json, list): str_items = enumerate(str_json) elif isinstance(str_json, dict): str_items = str_json.items() for k, s in str_items: str_map[k] = s # create the body body = { 'status': 'success', 'results': [], 'errors': [], 'features': 0 } # without features just replace expression string with layer context if not features: result = {} for k, s in str_map.items(): value = QgsExpression.replaceExpressionText(s, exp_context, da) result[k] = json.loads(QgsJsonUtils.encodeValue(value)) body['results'].append(result) write_json_response(body, response) return # Check features try: geojson = json.loads(features) except Exception: QgsMessageLog.logMessage( "JSON loads features '{}' exception:\n{}".format( features, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed" .format(features), 400) if not geojson or not isinstance(geojson, list) or len(geojson) == 0: raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed" .format(features), 400) if ('type' not in geojson[0]) or geojson[0]['type'] != 'Feature': raise ExpressionServiceError( "Bad request error", "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not Feature." .format(features), 400) # try to load features # read fields feature_fields = QgsJsonUtils.stringToFields( '{ "type": "FeatureCollection","features":' + features + '}', QTextCodec.codecForName("UTF-8")) # read features feature_list = QgsJsonUtils.stringToFeatureList( '{ "type": "FeatureCollection","features":' + features + '}', feature_fields, QTextCodec.codecForName("UTF-8")) # features not well formed if not feature_list: raise ExpressionServiceError( "Bad request error", "Invalid FEATURES for 'ReplaceExpressionText': not GeoJSON features array provided\n{}" .format(features), 400) # Extend layer fields with this provided in GeoJSON Features feat_fields = QgsFields(layer.fields()) feat_fields.extend(feature_fields) # form scope addFormScope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't'] # loop through provided features to replace expression strings for f in feature_list: # clone the features with all attributes # those defined in layer + fields from GeoJSON Features feat = QgsFeature(feat_fields) feat.setGeometry(f.geometry()) for field in f.fields(): fname = field.name() if feat_fields.indexOf(fname) != -1: feat.setAttribute(fname, f[fname]) # Add form scope to expression context if addFormScope: exp_context.appendScope( QgsExpressionContextUtils.formScope(feat)) exp_context.setFeature(feat) exp_context.setFields(feat.fields()) # replace expression strings with the new feature result = {} for k, s in str_map.items(): value = QgsExpression.replaceExpressionText(s, exp_context, da) result[k] = json.loads(QgsJsonUtils.encodeValue(value)) body['results'].append(result) write_json_response(body, response) return
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ connection_name = QgsExpressionContextUtils.globalScope().variable( 'raepa_connection_name') feedback.pushInfo('Connection name = %s' % connection_name) # Ouvrages feedback.pushInfo('Import ouvrages') processing.run("qgis:importintopostgis", { 'INPUT': parameters[self.OUVRAGES], 'DATABASE': connection_name, 'SCHEMA': 'imports', 'TABLENAME': 'gabarit_ouvrages', 'PRIMARY_KEY': None, 'GEOMETRY_COLUMN': 'geom', 'ENCODING': 'UTF-8', 'OVERWRITE': True, 'CREATEINDEX': True, 'LOWERCASE_NAMES': True, 'DROP_STRING_LENGTH': True, 'FORCE_SINGLEPART': True }, context=context, feedback=feedback) feedback.pushInfo('Import ouvrages - OK') # Appareils feedback.pushInfo('Import appareils') processing.run("qgis:importintopostgis", { 'INPUT': parameters[self.APPAREILS], 'DATABASE': connection_name, 'SCHEMA': 'imports', 'TABLENAME': 'gabarit_appareils', 'PRIMARY_KEY': None, 'GEOMETRY_COLUMN': 'geom', 'ENCODING': 'UTF-8', 'OVERWRITE': True, 'CREATEINDEX': True, 'LOWERCASE_NAMES': True, 'DROP_STRING_LENGTH': True, 'FORCE_SINGLEPART': True }, context=context, feedback=feedback) feedback.pushInfo('Import appareils - OK') # Canalisations feedback.pushInfo('Import canalisations') processing.run("qgis:importintopostgis", { 'INPUT': parameters[self.CANALISATIONS], 'DATABASE': connection_name, 'SCHEMA': 'imports', 'TABLENAME': 'gabarit_canalisations', 'PRIMARY_KEY': None, 'GEOMETRY_COLUMN': 'geom', 'ENCODING': 'UTF-8', 'OVERWRITE': True, 'CREATEINDEX': True, 'LOWERCASE_NAMES': True, 'DROP_STRING_LENGTH': True, 'FORCE_SINGLEPART': True }, context=context, feedback=feedback) feedback.pushInfo('Import canalisations - OK') return {self.OUTPUT_STATUS: 1, self.OUTPUT_STRING: 'Import OK'}
def initAlgorithm(self, config): # INPUTS project = QgsProject.instance() connection_name = QgsExpressionContextUtils.projectScope( project).variable('gobs_connection_name') get_data = QgsExpressionContextUtils.globalScope().variable( 'gobs_get_database_data') # Add spatial layer choice # List of spatial_layer sql = ''' SELECT id, sl_label FROM gobs.spatial_layer ORDER BY sl_label ''' data = [] if get_data == 'yes' and connection_name in getPostgisConnectionList(): [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery(connection_name, sql) self.SPATIALLAYERS = ['%s - %s' % (a[1], a[0]) for a in data] self.SPATIALLAYERS_DICT = {a[0]: a[1] for a in data} self.addParameter( QgsProcessingParameterEnum(self.SPATIALLAYER, tr('Spatial layer'), options=self.SPATIALLAYERS, optional=False)) # Id of spatial layer, to get the layer directly # mainly used from other processing algs p = QgsProcessingParameterNumber( self.SPATIALLAYER_ID, tr('Spatial layer ID. If given, it overrides previous choice'), optional=True, defaultValue=-1) p.setFlags(QgsProcessingParameterDefinition.FlagHidden) self.addParameter(p) # Confirmation self.addParameter( QgsProcessingParameterBoolean( self.RUN_DELETE, tr('Check this box to delete. No action will be done otherwise' ), defaultValue=False, )) # Delete the series self.addParameter( QgsProcessingParameterBoolean( self.DELETE_SPATIAL_LAYER, tr('Also delete the spatial layer item'), defaultValue=False, )) # OUTPUTS # Add output for status (integer) self.addOutput( QgsProcessingOutputNumber(self.OUTPUT_STATUS, tr('Output status'))) # Add output for message self.addOutput( QgsProcessingOutputString(self.OUTPUT_STRING, tr('Output message')))
def virtualFields(self, params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None: """ Get virtual fields for features In parameters: LAYER=wms-layer-name VIRTUALS={"key1": "first expression", "key2": "second expression"} // optionals FILTER=An expression to filter layer FIELDS=list of requested field separated by comma WITH_GEOMETRY=False """ layername = params.get('LAYER', '') if not layername: raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = findVectorLayer(layername, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'VirtualFields': {} provided". format(layername), 400) # get virtuals virtuals = params.get('VIRTUALS', '') if not virtuals: raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: VIRTUALS parameter is mandatory", 400) # try to load virtuals dict vir_json = None try: vir_json = json.loads(virtuals) except Exception: QgsMessageLog.logMessage( "JSON loads virtuals '{}' exception:\n{}".format( virtuals, traceback.format_exc()), "lizmap", Qgis.Critical) raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed" .format(virtuals), 400) if not isinstance(vir_json, dict): raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed" .format(virtuals), 400) # create expression context exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope( QgsExpressionContextUtils.projectScope(project)) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) # create distance area context da = QgsDistanceArea() da.setSourceCrs(layer.crs(), project.transformContext()) da.setEllipsoid(project.ellipsoid()) # parse virtuals exp_map = {} exp_parser_errors = [] for k, e in vir_json.items(): exp = QgsExpression(e) exp.setGeomCalculator(da) exp.setDistanceUnits(project.distanceUnits()) exp.setAreaUnits(project.areaUnits()) if exp.hasParserError(): exp_parser_errors.append('Error "{}": {}'.format( e, exp.parserErrorString())) continue if not exp.isValid(): exp_parser_errors.append('Expression not valid "{}"'.format(e)) continue exp.prepare(exp_context) exp_map[k] = exp # expression parser errors found if exp_parser_errors: raise ExpressionServiceError( "Bad request error", "Invalid VIRTUALS for 'VirtualFields':\n{}".format( '\n'.join(exp_parser_errors)), 400) req = QgsFeatureRequest() # get filter req_filter = params.get('FILTER', '') if req_filter: req_exp = QgsExpression(req_filter) req_exp.setGeomCalculator(da) req_exp.setDistanceUnits(project.distanceUnits()) req_exp.setAreaUnits(project.areaUnits()) if req_exp.hasParserError(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'VirtualFields' Error \"{}\": {}". format(req_filter, req_exp.parserErrorString()), 400) if not req_exp.isValid(): raise ExpressionServiceError( "Bad request error", "Invalid FILTER for 'VirtualFields' Expression not valid \"{}\"" .format(req_filter), 400) req_exp.prepare(exp_context) req = QgsFeatureRequest(req_exp, exp_context) # With geometry withGeom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't'] if not withGeom: req.setFlags(QgsFeatureRequest.NoGeometry) # Fields pkAttributes = layer.primaryKeyAttributes() attributeList = [i for i in pkAttributes] fields = layer.fields() r_fields = [ f.strip() for f in params.get('FIELDS', '').split(',') if f ] for f in r_fields: attributeList.append(fields.indexOf(f)) # response response.setStatusCode(200) response.setHeader("Content-Type", "application/json") response.write('{ "type": "FeatureCollection","features":[') response.flush() jsonExporter = QgsJsonExporter(layer) if attributeList: jsonExporter.setAttributes(attributeList) separator = '' for feat in layer.getFeatures(req): fid = layername + '.' + getServerFid(feat, pkAttributes) extra = {} # Update context exp_context.setFeature(feat) exp_context.setFields(feat.fields()) # Evaluate expressions for virtual fields errors = {} for k, exp in exp_map.items(): value = exp.evaluate(exp_context) if exp.hasEvalError(): extra[k] = None errors[k] = exp.evalErrorString() else: extra[k] = json.loads(QgsJsonUtils.encodeValue(value)) errors[k] = exp.expression() response.write(separator + jsonExporter.exportFeature(feat, extra, fid)) response.flush() separator = ',\n' response.write(']}') return
def processAlgorithm(self, parameters, context, model_feedback): # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the # overall progress through the model feedback = QgsProcessingMultiStepFeedback(4, model_feedback) results = {} outputs = {} #config connection to db pg_service = QgsExpressionContextUtils.globalScope().variable("dominode_db_connection_name") connection = psycopg2.connect(service=pg_service) # Convert expression to string alg_params = { 'INPUT': parameters['dbconnectionnameexpression'] } outputs['ConvertExpressionToString'] = processing.run('script:expressiontostringconverter', alg_params, context=context, feedback=feedback, is_child_algorithm=True) feedback.setCurrentStep(1) if feedback.isCanceled(): return {} # Validate resource name alg_params = { 'INPUT_LAYER': '', 'INPUT_NAME': parameters['layername'] } outputs['ValidateResourceName'] = processing.run('script:resourcenamevalidator', alg_params, context=context, feedback=feedback, is_child_algorithm=True) feedback.setCurrentStep(2) if feedback.isCanceled(): return {} # Export to PostgreSQL (available connections) alg_params = { 'ADDFIELDS': False, 'APPEND': False, 'A_SRS': None, 'CLIP': False, 'DATABASE': pg_service, 'DIM': 0, 'GEOCOLUMN': 'geom', 'GT': '', 'GTYPE': 0, 'INDEX': False, 'INPUT': parameters['inputlayer'], 'LAUNDER': True, 'OPTIONS': '', 'OVERWRITE': True, 'PK': 'id', 'PRECISION': True, 'PRIMARY_KEY': '', 'PROMOTETOMULTI': False, 'SCHEMA': outputs['ValidateResourceName']['OUTPUT_DB_STAGING_SCHEMA_NAME'], 'SEGMENTIZE': '', 'SHAPE_ENCODING': '', 'SIMPLIFY': '', 'SKIPFAILURES': False, 'SPAT': None, 'S_SRS': None, 'TABLE': outputs['ValidateResourceName']['OUTPUT_DATASET_NAME'], 'T_SRS': None, 'WHERE': '' } outputs['ExportToPostgresqlAvailableConnections'] = processing.run('gdal:importvectorintopostgisdatabaseavailableconnections', alg_params, context=context, feedback=feedback, is_child_algorithm=True) feedback.setCurrentStep(3) if feedback.isCanceled(): return {} # set staging permissions schema_out = outputs['ValidateResourceName']['OUTPUT_DB_STAGING_SCHEMA_NAME'] table_out = outputs['ValidateResourceName']['OUTPUT_DATASET_NAME'] cursor = connection.cursor() cursor.execute("SELECT DomiNodeSetStagingPermissions('{}.{}');".format(schema_out,table_out)) record = cursor.fetchone() cursor.close() return results
def variablesChanged(self): QgsExpressionContextUtils.setCompositionVariables( self.mComposition, self.mVariableEditor.variablesInActiveScope() )
def processAlgorithm(self, parameters, context, feedback): connection_name = QgsExpressionContextUtils.globalScope().variable( 'raepa_connection_name') # Drop schema if needed runit = self.parameterAsBool(parameters, self.RUNIT, context) if not runit: status = 0 msg = 'Vous devez cocher cette case à cocher pour faire la mise à jour !' # raise Exception(msg) return {self.OUTPUT_STATUS: status, self.OUTPUT_STRING: msg} # get database version sql = ''' SELECT version FROM raepa.sys_structure_metadonnee ORDER BY date_ajout DESC LIMIT 1; ''' [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery(connection_name, sql) if not ok: feedback.pushInfo(error_message) status = 0 raise Exception(error_message) db_version = None for a in data: db_version = a[0] if not db_version: error_message = 'Pas de version installée dans la base de données !' raise Exception(error_message) feedback.pushInfo( 'Version de la structure de la base de données : {}'.format( db_version)) # get plugin version alg_dir = os.path.dirname(__file__) plugin_dir = os.path.join(alg_dir, '../../') config = configparser.ConfigParser() config.read(os.path.join(plugin_dir, 'metadata.txt')) plugin_version = config['general']['version'] feedback.pushInfo('Version du plugin : {}'.format(plugin_version)) # Return if nothing to do if db_version == plugin_version: return { self.OUTPUT_STATUS: 1, self.OUTPUT_STRING: ('La version de la base de données correspond déjà à la version du plugin. Pas de mise à jour ' 'nécessaire.') } # Get input srid crs = parameters[self.SRID] srid = crs.authid().replace('EPSG:', '') feedback.pushInfo('SRID = {}'.format(srid)) # Get all the upgrade SQL files between db versions and plugin version upgrade_dir = os.path.join(plugin_dir, 'install/sql/upgrade/') get_files = [ f for f in os.listdir(upgrade_dir) if os.path.isfile(os.path.join(upgrade_dir, f)) ] files = [] db_version_integer = getVersionInteger(db_version) for f in get_files: k = getVersionInteger( f.replace('upgrade_to_', '').replace('.sql', '').strip()) if k > db_version_integer: files.append([k, f]) def getKey(item): return item[0] sfiles = sorted(files, key=getKey) sql_files = [s[1] for s in sfiles] msg = '' # Loop sql files and run SQL code for sf in sql_files: sql_file = os.path.join(plugin_dir, 'install/sql/upgrade/%s' % sf) with open(sql_file, 'r') as f: sql = f.read() if len(sql.strip()) == 0: feedback.pushInfo('* ' + sf + ' -- SKIPPED (EMPTY FILE)') continue # Replace 2154 by given srid sql = sql.replace('2154', srid) # Add SQL database version in raepa.metadata new_db_version = sf.replace('upgrade_to_', '').replace('.sql', '').strip() feedback.pushInfo('* NEW DB VERSION' + new_db_version) sql += ''' UPDATE raepa.sys_structure_metadonnee SET (version, date_ajout) = ( '%s', now()::timestamp(0) ); ''' % new_db_version [header, data, rowCount, ok, error_message] = fetchDataFromSqlQuery(connection_name, sql) if ok: feedback.pushInfo('* ' + sf + ' -- SUCCESS !') else: feedback.pushInfo(error_message) status = 0 raise Exception(error_message) # return { # self.OUTPUT_STATUS: status, # self.OUTPUT_STRING: error_message # } return { self.OUTPUT_STATUS: 1, self.OUTPUT_STRING: '*** STRUCTURE RAEPA MISE À JOUR AVEC SUCCÈS ***' }
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_LAYER)) fieldName = self.getParameterValue(self.FIELD_NAME) fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)] width = self.getParameterValue(self.FIELD_LENGTH) precision = self.getParameterValue(self.FIELD_PRECISION) newField = self.getParameterValue(self.NEW_FIELD) formula = self.getParameterValue(self.FORMULA) output = self.getOutputFromName(self.OUTPUT_LAYER) if output.value == '': ext = output.getDefaultFileExtension(self) output.value = system.getTempFilenameInTempFolder(output.name + '.' + ext) fields = layer.fields() if newField: fields.append(QgsField(fieldName, fieldType, '', width, precision)) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs()) exp = QgsExpression(formula) da = QgsDistanceArea() da.setSourceCrs(layer.crs().srsid()) da.setEllipsoidalMode( iface.mapCanvas().mapSettings().hasCrsTransformEnabled()) da.setEllipsoid(QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE)[0]) exp.setGeomCalculator(da) exp.setDistanceUnits(QgsProject.instance().distanceUnits()) exp.setAreaUnits(QgsProject.instance().areaUnits()) exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) if not exp.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: %s' % exp.evalErrorString())) outFeature = QgsFeature() outFeature.initAttributes(len(fields)) outFeature.setFields(fields) error = '' calculationSuccess = True features = vector.features(layer) total = 100.0 / len(features) if len(features) > 0 else 1 rownum = 1 for current, f in enumerate(features): rownum = current + 1 exp_context.setFeature(f) exp_context.lastScope().setVariable("row_number", rownum) value = exp.evaluate(exp_context) if exp.hasEvalError(): calculationSuccess = False error = exp.evalErrorString() break else: outFeature.setGeometry(f.geometry()) for fld in f.fields(): outFeature[fld.name()] = f[fld.name()] outFeature[fieldName] = value writer.addFeature(outFeature) progress.setPercentage(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation ' 'string:\n%s' % error))
def exportJSONLayer(layer, eachPopup, precision, tmpFileName, exp_crs, layerFileName, safeLayerName, minify, canvas, restrictToExtent, iface, extent): cleanedLayer = writeTmpLayer(layer, eachPopup, restrictToExtent, iface, extent) if is25d(layer, canvas, restrictToExtent, extent): provider = cleanedLayer.dataProvider() provider.addAttributes([ QgsField("height", QVariant.Double), QgsField("wallColor", QVariant.String), QgsField("roofColor", QVariant.String) ]) cleanedLayer.updateFields() fields = cleanedLayer.pendingFields() renderer = layer.rendererV2() renderContext = QgsRenderContext.fromMapSettings(canvas.mapSettings()) feats = layer.getFeatures() context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression = QgsExpression('eval(@qgis_25d_height)') heightField = fields.indexFromName("height") wallField = fields.indexFromName("wallColor") roofField = fields.indexFromName("roofColor") renderer.startRender(renderContext, fields) cleanedLayer.startEditing() for feat in feats: context.setFeature(feat) height = expression.evaluate(context) if isinstance(renderer, QgsCategorizedSymbolRendererV2): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) catIndex = renderer.categoryIndexForValue(attrValue) categories = renderer.categories() symbol = categories[catIndex].symbol() elif isinstance(renderer, QgsGraduatedSymbolRendererV2): classAttribute = renderer.classAttribute() attrValue = feat.attribute(classAttribute) ranges = renderer.ranges() for range in ranges: if (attrValue >= range.lowerValue() and attrValue <= range.upperValue()): symbol = range.symbol().clone() else: symbol = renderer.symbolForFeature2(feat, renderContext) wallColor = symbol.symbolLayer(1).subSymbol().color().name() roofColor = symbol.symbolLayer(2).subSymbol().color().name() cleanedLayer.changeAttributeValue(feat.id() + 1, heightField, height) cleanedLayer.changeAttributeValue(feat.id() + 1, wallField, wallColor) cleanedLayer.changeAttributeValue(feat.id() + 1, roofField, roofColor) cleanedLayer.commitChanges() renderer.stopRender(renderContext) writer = QgsVectorFileWriter options = [] if precision != "maintain": options.append("COORDINATE_PRECISION=" + unicode(precision)) writer.writeAsVectorFormat(cleanedLayer, tmpFileName, 'utf-8', exp_crs, 'GeoJson', 0, layerOptions=options) with open(layerFileName, "w") as f2: f2.write("var json_" + unicode(safeLayerName) + "=") with open(tmpFileName, "r") as tmpFile: for line in tmpFile: if minify: line = line.strip("\n\t ") line = removeSpaces(line) f2.write(line) os.remove(tmpFileName) fields = layer.pendingFields() for field in fields: exportImages(layer, field.name(), layerFileName)
def processAlgorithm(self, parameters, context, feedback): expr_context = self.createExpressionContext(parameters, context, self.source) self.group_by_expr.prepare(expr_context) # Group features in memory layers source = self.source count = self.source.featureCount() if count: progress_step = 50.0 / count current = 0 groups = {} keys = [] # We need deterministic order for the tests feature = QgsFeature() for feature in self.source.getFeatures(): expr_context.setFeature(feature) group_by_value = self.evaluateExpression(self.group_by_expr, expr_context) # Get an hashable key for the dict key = group_by_value if isinstance(key, list): key = tuple(key) group = groups.get(key, None) if group is None: sink, id = QgsProcessingUtils.createFeatureSink( 'memory:', context, source.fields(), source.wkbType(), source.sourceCrs()) layer = QgsProcessingUtils.mapLayerFromString(id, context) group = { 'sink': sink, 'layer': layer, 'feature': feature } groups[key] = group keys.append(key) group['sink'].addFeature(feature, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * progress_step)) if feedback.isCanceled(): return (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, self.fields, QgsWkbTypes.multiType(source.wkbType()), source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) # Calculate aggregates on memory layers if len(keys): progress_step = 50.0 / len(keys) for current, key in enumerate(keys): group = groups[key] expr_context = self.createExpressionContext(parameters, context) expr_context.appendScope(QgsExpressionContextUtils.layerScope(group['layer'])) expr_context.setFeature(group['feature']) geometry = self.evaluateExpression(self.geometry_expr, expr_context) if geometry is not None and not geometry.isEmpty(): geometry = QgsGeometry.unaryUnion(geometry.asGeometryCollection()) if geometry.isEmpty(): raise QgsProcessingException( 'Impossible to combine geometries for {} = {}' .format(self.group_by, group_by_value)) attrs = [] for fields_expr in self.fields_expr: attrs.append(self.evaluateExpression(fields_expr, expr_context)) # Write output feature outFeat = QgsFeature() if geometry is not None: outFeat.setGeometry(geometry) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(50 + int(current * progress_step)) if feedback.isCanceled(): return return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): connection_name = QgsExpressionContextUtils.globalScope().variable( 'gobs_connection_name') # Drop schema if needed override = self.parameterAsBool(parameters, self.OVERRIDE, context) if override: feedback.pushInfo(tr("Trying to drop schema {}…").format(SCHEMA)) sql = "DROP SCHEMA IF EXISTS {} CASCADE;".format(SCHEMA) _, _, _, ok, error_message = fetch_data_from_sql_query( connection_name, sql) if ok: feedback.pushInfo( tr("Schema {} has been dropped.").format(SCHEMA)) else: raise QgsProcessingException(error_message) # Create full structure sql_files = [ "00_initialize_database.sql", "{}/10_FUNCTION.sql".format(SCHEMA), "{}/20_TABLE_SEQUENCE_DEFAULT.sql".format(SCHEMA), "{}/30_VIEW.sql".format(SCHEMA), "{}/40_INDEX.sql".format(SCHEMA), "{}/50_TRIGGER.sql".format(SCHEMA), "{}/60_CONSTRAINT.sql".format(SCHEMA), "{}/70_COMMENT.sql".format(SCHEMA), "{}/90_GLOSSARY.sql".format(SCHEMA), "99_finalize_database.sql", ] # Add test data add_test_data = self.parameterAsBool(parameters, self.ADD_TEST_DATA, context) if add_test_data: sql_files.append("99_test_data.sql") plugin_dir = plugin_path() plugin_version = version() dev_version = False run_migration = os.environ.get("TEST_DATABASE_INSTALL_{}".format( SCHEMA.capitalize())) if plugin_version in ["master", "dev"] and not run_migration: feedback.reportError( "Be careful, running the install on a development branch!") dev_version = True if run_migration: plugin_dir = plugin_test_data_path() feedback.reportError( "Be careful, running migrations on an empty database using {} " "instead of {}".format(run_migration, plugin_version)) plugin_version = run_migration # Loop sql files and run SQL code for sf in sql_files: feedback.pushInfo(sf) sql_file = os.path.join(plugin_dir, "install/sql/{}".format(sf)) with open(sql_file, "r") as f: sql = f.read() if len(sql.strip()) == 0: feedback.pushInfo(" Skipped (empty file)") continue _, _, _, ok, error_message = fetch_data_from_sql_query( connection_name, sql) if ok: feedback.pushInfo(" Success !") else: raise QgsProcessingException(error_message) # Add version if run_migration or not dev_version: metadata_version = plugin_version else: migrations = available_migrations(000000) last_migration = migrations[-1] metadata_version = (last_migration.replace("upgrade_to_", "").replace(".sql", "").strip()) feedback.reportError( "Latest migration is {}".format(metadata_version)) sql = """ INSERT INTO {}.metadata (me_version, me_version_date, me_status) VALUES ( '{}', now()::timestamp(0), 1 )""".format(SCHEMA, metadata_version) fetch_data_from_sql_query(connection_name, sql) feedback.pushInfo("Database version '{}'.".format(metadata_version)) return { self.OUTPUT_STATUS: 1, self.OUTPUT_STRING: tr("*** THE STRUCTURE {} HAS BEEN CREATED WITH VERSION '{}'***". format(SCHEMA, metadata_version)), }
def initContext(self): exp_context = self.builder.expressionContext() exp_context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(self.layer)) exp_context.lastScope().setVariable("row_number", 1) exp_context.setHighlightedVariables(["row_number"]) self.builder.setExpressionContext(exp_context)
def processAlgorithm(self, parameters, context, feedback): path = self.parameterAsFile(parameters, self.INPUT, context) t_file = self.parameterAsVectorLayer( parameters, self.FILE_TABLE, context ) t_troncon = self.parameterAsVectorLayer( parameters, self.SEGMENT_TABLE, context ) t_obs = self.parameterAsVectorLayer( parameters, self.OBSERVATIONS_TABLE, context ) t_regard = self.parameterAsVectorLayer( parameters, self.MANHOLES_TABLE, context ) paths = path.split(';') if len(paths) != 1: raise QgsProcessingException( tr('* ERREUR: 1 fichier a la fois {}.').format(path) ) if not os.path.exists(path): raise QgsProcessingException( tr('* ERREUR: {} n\'existe pas.').format(path) ) # add date fields to itv file table # relation_aggregate( # 'itv_tronco_id_file_itv_file20_id', # 'max', # "abf" # ) if 'date_debut' not in t_file.dataProvider().fields().names(): with edit(t_file): res = t_file.dataProvider().addAttributes([ QgsField("date_debut", QVariant.String), QgsField("date_fin", QVariant.String) ]) if res: t_file.updateFields() feat_file = None with open(path, 'rb') as f: basename = os.path.basename(path) md = hashlib.md5() md.update(f.read()) hashcontent = md.hexdigest() feat_file = QgsVectorLayerUtils.createFeature(t_file) feat_file.setAttribute('basename', basename) feat_file.setAttribute('hashcontent', hashcontent) if not feat_file: raise QgsProcessingException( tr( '* ERREUR: le fichier {} n\'a pas été lu ' 'correctement.' ).format(path) ) exp_context = QgsExpressionContext() exp_context.appendScope( QgsExpressionContextUtils.globalScope() ) exp_context.appendScope( QgsExpressionContextUtils.projectScope(context.project()) ) exp_context.appendScope( QgsExpressionContextUtils.layerScope(t_file) ) exp_str = QgsExpression.createFieldEqualityExpression( 'basename', feat_file['basename'] ) + ' AND ' + QgsExpression.createFieldEqualityExpression( 'hashcontent', feat_file['hashcontent'] ) exp = QgsExpression(exp_str) exp.prepare(exp_context) if exp.hasEvalError(): raise QgsProcessingException( tr('* ERROR: Expression {} has eval error: {}').format( exp.expression(), exp.evalErrorString() ) ) request = QgsFeatureRequest(exp, exp_context) request.setLimit(1) for _t in t_file.getFeatures(request): raise QgsProcessingException( tr('* ERREUR: le fichier {} a deja ete lu').format( path ) ) exp_str = QgsExpression.createFieldEqualityExpression( 'hashcontent', feat_file['hashcontent'] ) exp = QgsExpression(exp_str) exp.prepare(exp_context) if exp.hasEvalError(): raise QgsProcessingException( tr('* ERROR: Expression {} has eval error: {}').format( (exp.expression(), exp.evalErrorString()) ) ) request = QgsFeatureRequest(exp, exp_context) request.setLimit(1) for _t in t_file.getFeatures(request): raise QgsProcessingException( tr( '* ERREUR: le fichier {} semble deja avoir ete lu' ).format(path) ) exp_context = QgsExpressionContext() exp_context.appendScope( QgsExpressionContextUtils.globalScope() ) exp_context.appendScope( QgsExpressionContextUtils.projectScope(context.project()) ) exp_context.appendScope( QgsExpressionContextUtils.layerScope(t_troncon) ) exp_str = 'maximum("id")' exp = QgsExpression(exp_str) exp.prepare(exp_context) if exp.hasEvalError(): raise QgsProcessingException( tr( '* ERROR: Expression {} has eval error: {}' ).format(exp.expression(), exp.evalErrorString()) ) last_t_id = exp.evaluate(exp_context) if not last_t_id: last_t_id = 0 exp_context = QgsExpressionContext() exp_context.appendScope( QgsExpressionContextUtils.globalScope() ) exp_context.appendScope( QgsExpressionContextUtils.projectScope(context.project()) ) exp_context.appendScope( QgsExpressionContextUtils.layerScope(t_regard) ) exp_str = 'maximum("id")' exp = QgsExpression(exp_str) exp.prepare(exp_context) if exp.hasEvalError(): raise QgsProcessingException( tr( '* ERROR: Expression {} has eval error: {}' ).format(exp.expression(), exp.evalErrorString()) ) last_r_id = exp.evaluate(exp_context) if not last_r_id: last_r_id = 0 # lecture des entetes ENCODING = 'ISO-8859-1' LANG = 'fr' DELIMITER = ',' DECIMAL = '.' QUOTECHAR = '"' VERSION = '' with open(path, 'rb') as f: for line in f: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} try: line = line.decode() except UnicodeDecodeError: raise QgsProcessingException( 'Error while reading {}'.format(path) ) # remove break line line = line.replace('\n', '').replace('\r', '') if line.startswith('#'): if line.startswith('#A'): if line.startswith('#A1'): ENCODING = line[4:] if ENCODING.find(':') != -1: ENCODING = ENCODING[:ENCODING.find(':')] elif line.startswith('#A2'): LANG = line[4:] elif line.startswith('#A3'): DELIMITER = line[4:] elif line.startswith('#A4'): DECIMAL = line[4:] elif line.startswith('#A5'): QUOTECHAR = line[4:] else: break # Dialect CSV pour la lecture des tableaux de valeurs du # fichier d'ITV class itvDialect(csv.Dialect): strict = True skipinitialspace = True quoting = csv.QUOTE_MINIMAL delimiter = DELIMITER quotechar = QUOTECHAR lineterminator = '\r\n' # Liste des troncons, observations et regards troncons = [] regards = [] observations = [] # Lectures des donnees with open(path, 'rb') as f: # Identifiant de départ t_id = last_t_id r_id = last_r_id # Entête header = [] # Nom du tableau array = '' # Observations de troncons ? obs_for_troncon = False # initialisation du Dialect CSV pour ITV dia = itvDialect() # Lecture ligne à ligne du fichier for line in f: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} # Decoding line en utilisant l'encoding du fichier line = line.decode(ENCODING) # remove break line line = line.replace('\n', '').replace('\r', '') # Ligne commençant par un # est une ligne d'entête if line.startswith('#'): # Entête de troncon ou regard if line.startswith('#B'): l_b = io.StringIO(line[5:]) for l_r in csv.reader(l_b, dia): header = l_r break array = line[1:4] continue # Entête d'observation elif line.startswith('#C'): if header[0].startswith('A'): obs_for_troncon = True else: obs_for_troncon = False l_b = io.StringIO(line[3:]) for l_r in csv.reader(l_b, dia): header = l_r break array = line[1:2] continue # Fin d'observation elif line.startswith('#Z'): header = [] array = '' obs_for_troncon = False continue # La ligne contient des donnees else: if not header: # an error in the file structure continue l_b = io.StringIO(line) for l_r in csv.reader(l_b, dia): data = l_r row = list( zip( [h.lower() for h in header], [t for t in data] ) ) # observation if array == 'C': if obs_for_troncon: observations.append( row + [('id_troncon', t_id)] ) # Premiere ligne de description d'un troncon ou regard elif array == 'B01': if header[0].startswith('A'): t_id += 1 troncons.append([('id', t_id)] + row) elif header[0].startswith('C'): r_id += 1 regards.append([('id', r_id)] + row) # Ligne complémentaire de description else: if header[0].startswith('A'): troncons[-1] += row elif header[0].startswith('C'): regards[-1] += row # Recuperation des references de noeuds et dates itv_dates = [] regard_node_refs = [] regard_ref_id = {} max_r_id = last_r_id for reg in regards: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} d_rg = dict(reg) if d_rg['caa'] and d_rg['caa'] not in regard_node_refs: regard_node_refs.append(d_rg['caa']) regard_ref_id[d_rg['caa']] = d_rg['id'] if d_rg['id'] and d_rg['id'] > max_r_id: max_r_id = d_rg['id'] if 'cbf' in d_rg and d_rg['cbf'] not in itv_dates: itv_dates.append(d_rg['cbf']) node_refs = [] for tro in troncons: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} d_tr = dict(tro) # The nodes ref are stored in AAB, AAD, AAF and AAT if 'aab' in d_tr and \ d_tr['aab'] and \ d_tr['aab'] not in regard_node_refs and \ d_tr['aab'] not in node_refs: node_refs.append(d_tr['aab']) if 'aad' in d_tr and \ d_tr['aad'] and \ d_tr['aad'] not in regard_node_refs and \ d_tr['aad'] not in node_refs: node_refs.append(d_tr['aad']) if 'aaf' in d_tr and \ d_tr['aaf'] and \ d_tr['aaf'] not in regard_node_refs and \ d_tr['aaf'] not in node_refs: node_refs.append(d_tr['aaf']) if 'aat' in d_tr and \ d_tr['aat'] and \ d_tr['aat'] not in regard_node_refs and \ d_tr['aat'] not in node_refs: node_refs.append(d_tr['aat']) if 'abf' in d_tr and d_tr['abf'] not in itv_dates: itv_dates.append(d_tr['abf']) # Ajout des regards manquant for n_ref in node_refs: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} max_r_id += 1 regards.append([('id', max_r_id), ('caa', n_ref)]) regard_ref_id[n_ref] = max_r_id # Ajout des identifiants de regards aux tronçons regard_refs = regard_ref_id.keys() for tro in troncons: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} d_tr = dict(tro) # If AAD is not defined then it is equal to AAB if 'aad' not in d: d['aad'] = d['aab'] if d_tr['aad'] and \ d_tr['aad'] in regard_refs: tro += [('id_regard1', regard_ref_id[d_tr['aad']])] if d_tr['aaf'] and \ d_tr['aaf'] in regard_refs: tro += [('id_regard2', regard_ref_id[d_tr['aaf']])] if 'aat' in d_tr.keys() and \ d_tr['aat'] and \ d_tr['aat'] in regard_refs: tro += [('id_regard3', regard_ref_id[d_tr['aat']])] # Verification des champs fields = provider_fields(t_troncon.fields()) for tro in troncons: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} for key, val in tro: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} if fields.indexOf(key) == -1: raise QgsProcessingException( tr( '* ERREUR dans le fichier : ' 'le champs de tronçon "{}" est inconnue' ).format(key) ) field = fields.field(key) if isinstance(val, str) and field.isNumeric(): if val: try: float(val.replace(DECIMAL, '.')) except BaseException: raise QgsProcessingException( tr( '* ERREUR dans le fichier : ' 'le champs de tronçon "{}" est ' 'numérique mais pas la valeur "{}"' ).format(key, val) ) fields = provider_fields(t_obs.fields()) for obs in observations: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} for key, val in obs: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} if fields.indexOf(key) == -1: raise QgsProcessingException( tr( '* ERREUR dans le fichier : ' 'le champs d\'observation "{}" est ' 'inconnue' ).format(key) ) field = fields.field(key) if isinstance(val, str) and field.isNumeric(): if val: try: float(val.replace(DECIMAL, '.')) except BaseException: raise QgsProcessingException( tr( '* ERREUR dans le fichier : ' 'le champs d\'observation "{}" est ' 'numérique mais pas la valeur "{}"' ).format(key, val) ) fields = provider_fields(t_regard.fields()) for reg in regards: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} for key, val in reg: # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} if fields.indexOf(key) == -1: raise QgsProcessingException( tr( '* ERREUR dans le fichier : ' 'le champs de regard "{}" est inconnue' ).format(key) ) field = fields.field(key) if isinstance(val, str) and field.isNumeric(): if val: try: float(val.replace(DECIMAL, '.')) except BaseException: raise QgsProcessingException( tr( '* ERREUR dans le fichier : ' 'le champs de regard "{}" est ' 'numérique mais pas la valeur "{}"' ).format(key, val) ) # Finalisation objet fichier feat_file.setAttribute('encoding', ENCODING) feat_file.setAttribute('lang', LANG) if VERSION: feat_file.setAttribute('version', VERSION) if itv_dates: feat_file.setAttribute('date_debut', min(itv_dates)) feat_file.setAttribute('date_fin', max(itv_dates)) # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.SUCCESS: 0} # Ajout de l'objet fichier t_file.startEditing() (res, outFeats) = t_file.dataProvider().addFeatures([feat_file]) if not res or not outFeats: raise QgsProcessingException( tr( '* ERREUR: lors de l\'enregistrement du fichier {}' ).format(', '.join(t_file.dataProvider().errors())) ) if not t_file.commitChanges(): raise QgsProcessingException( tr('* ERROR: Commit {}.').format(t_file.commitErrors()) ) # Mise a jour de l'identifiant de l'objet fichier feat_file.setAttribute('id', outFeats[0]['id']) # Creation des objets troncons features = [] fields = provider_fields(t_troncon.fields()) for tro in troncons: feat_t = QgsVectorLayerUtils.createFeature(t_troncon) feat_t.setAttribute('id_file', feat_file['id']) for key, val in tro: field = fields.field(key) if isinstance(val, str) and field.isNumeric(): if val: feat_t.setAttribute( key, float(val.replace(DECIMAL, '.')) ) else: feat_t.setAttribute(key, val) features.append(feat_t) # Ajout des objets troncons if features: t_troncon.startEditing() (res, outFeats) = t_troncon.dataProvider().addFeatures(features) if not res or not outFeats: raise QgsProcessingException( tr( '* ERREUR: lors de l\'enregistrement ' 'des troncon {}' ).format( ', '.join(t_troncon.dataProvider().errors()) ) ) if not t_troncon.commitChanges(): raise QgsProcessingException( tr('* ERROR: Commit {}.').format( t_troncon.commitErrors() ) ) # Creation des objets observations features = [] fields = provider_fields(t_obs.fields()) for obs in observations: feat_o = QgsVectorLayerUtils.createFeature(t_obs) feat_o.setAttribute('id_file', feat_file['id']) for key, val in obs: field = fields.field(key) if isinstance(val, str) and field.isNumeric(): if val: feat_o.setAttribute( key, float(val.replace(DECIMAL, '.')) ) else: feat_o.setAttribute(key, val) features.append(feat_o) # Ajout des objets observations if features: t_obs.startEditing() (res, outFeats) = t_obs.dataProvider().addFeatures(features) if not res or not outFeats: raise QgsProcessingException( tr( '* ERREUR: lors de l\'enregistrement ' 'des observations {}' ).format( ', '.join(t_obs.dataProvider().errors()) ) ) if not t_obs.commitChanges(): raise QgsProcessingException( tr('* ERROR: Commit {}.').format( t_obs.commitErrors() ) ) # Creation des objets regards features = [] fields = provider_fields(t_regard.fields()) for reg in regards: feat_r = QgsVectorLayerUtils.createFeature(t_regard) feat_r.setAttribute('id_file', feat_file['id']) for key, val in reg: field = fields.field(key) if isinstance(val, str) and field.isNumeric(): if val: feat_r.setAttribute( key, float(val.replace(DECIMAL, '.')) ) else: feat_r.setAttribute(key, val) features.append(feat_r) # Ajout des objets regards if features: t_regard.startEditing() (res, outFeats) = t_regard.dataProvider().addFeatures( features ) if not res or not outFeats: raise QgsProcessingException( tr( '* ERREUR: lors de l\'enregistrement ' 'des regards {}' ).format( ', '.join(t_regard.dataProvider().errors()) ) ) if not t_regard.commitChanges(): raise QgsProcessingException( tr('* ERROR: Commit %s.').format( t_regard.commitErrors() ) ) # Returns empty dict if no outputs return {self.SUCCESS: 1}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) field_name = self.parameterAsString(parameters, self.FIELD_NAME, context) field_type = self.TYPES[self.parameterAsEnum(parameters, self.FIELD_TYPE, context)] width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context) precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context) new_field = self.parameterAsBool(parameters, self.NEW_FIELD, context) formula = self.parameterAsString(parameters, self.FORMULA, context) expression = QgsExpression(formula) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) expression.setGeomCalculator(da) expression.setDistanceUnits(context.project().distanceUnits()) expression.setAreaUnits(context.project().areaUnits()) fields = source.fields() field_index = fields.lookupField(field_name) if new_field or field_index < 0: fields.append(QgsField(field_name, field_type, '', width, precision)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) exp_context = self.createExpressionContext(parameters, context) if layer is not None: exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(exp_context) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break rownum = current + 1 exp_context.setFeature(f) exp_context.lastScope().setVariable("row_number", rownum) value = expression.evaluate(exp_context) if expression.hasEvalError(): feedback.reportError(expression.evalErrorString()) else: attrs = f.attributes() if new_field or field_index < 0: attrs.append(value) else: attrs[field_index] = value f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testHomePath(self): p = QgsProject() path_changed_spy = QSignalSpy(p.homePathChanged) self.assertFalse(p.homePath()) self.assertFalse(p.presetHomePath()) # simulate save file tmp_dir = QTemporaryDir() tmp_file = "{}/project.qgs".format(tmp_dir.path()) with open(tmp_file, 'w') as f: pass p.setFileName(tmp_file) # home path should be file path self.assertEqual(p.homePath(), tmp_dir.path()) self.assertFalse(p.presetHomePath()) self.assertEqual(len(path_changed_spy), 1) # manually override home path p.setPresetHomePath('/tmp/my_path') self.assertEqual(p.homePath(), '/tmp/my_path') self.assertEqual(p.presetHomePath(), '/tmp/my_path') self.assertEqual(len(path_changed_spy), 2) # check project scope scope = QgsExpressionContextUtils.projectScope(p) self.assertEqual(scope.variable('project_home'), '/tmp/my_path') # no extra signal if path is unchanged p.setPresetHomePath('/tmp/my_path') self.assertEqual(p.homePath(), '/tmp/my_path') self.assertEqual(p.presetHomePath(), '/tmp/my_path') self.assertEqual(len(path_changed_spy), 2) # setting file name should not affect home path is manually set tmp_file_2 = "{}/project/project2.qgs".format(tmp_dir.path()) os.mkdir(tmp_dir.path() + '/project') with open(tmp_file_2, 'w') as f: pass p.setFileName(tmp_file_2) self.assertEqual(p.homePath(), '/tmp/my_path') self.assertEqual(p.presetHomePath(), '/tmp/my_path') self.assertEqual(len(path_changed_spy), 2) scope = QgsExpressionContextUtils.projectScope(p) self.assertEqual(scope.variable('project_home'), '/tmp/my_path') # clear manual path p.setPresetHomePath('') self.assertEqual(p.homePath(), tmp_dir.path() + '/project') self.assertFalse(p.presetHomePath()) self.assertEqual(len(path_changed_spy), 3) scope = QgsExpressionContextUtils.projectScope(p) self.assertEqual(scope.variable('project_home'), tmp_dir.path() + '/project') # relative path p.setPresetHomePath('../home') self.assertEqual(p.homePath(), tmp_dir.path() + '/home') self.assertEqual(p.presetHomePath(), '../home') self.assertEqual(len(path_changed_spy), 4) scope = QgsExpressionContextUtils.projectScope(p) self.assertEqual(scope.variable('project_home'), tmp_dir.path() + '/home') # relative path, no filename p.setFileName('') self.assertEqual(p.homePath(), '../home') self.assertEqual(p.presetHomePath(), '../home') scope = QgsExpressionContextUtils.projectScope(p) self.assertEqual(scope.variable('project_home'), '../home')
def processAlgorithm(self, progress): layer = self.getParameterValue(self.INPUT_LAYER) mapping = self.getParameterValue(self.FIELDS_MAPPING) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = dataobjects.getObjectFromUri(layer) fields = [] expressions = [] da = QgsDistanceArea() da.setSourceCrs(layer.crs().srsid()) da.setEllipsoidalMode( iface.mapCanvas().mapSettings().hasCrsTransformEnabled()) da.setEllipsoid(QgsProject.instance().readEntry( 'Measure', '/Ellipsoid', GEO_NONE)[0]) exp_context = QgsExpressionContext() exp_context.appendScope(QgsExpressionContextUtils.globalScope()) exp_context.appendScope(QgsExpressionContextUtils.projectScope()) exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer)) for field_def in mapping: fields.append( QgsField(name=field_def['name'], type=field_def['type'], len=field_def['length'], prec=field_def['precision'])) expression = QgsExpression(field_def['expression']) expression.setGeomCalculator(da) expression.setDistanceUnits(QgsProject.instance().distanceUnits()) expression.setAreaUnits(QgsProject.instance().areaUnits()) if expression.hasParserError(): raise GeoAlgorithmExecutionException( self.tr(u'Parser error in expression "{}": {}').format( str(field_def['expression']), str(expression.parserErrorString()))) expression.prepare(exp_context) if expression.hasEvalError(): raise GeoAlgorithmExecutionException( self.tr(u'Evaluation error in expression "{}": {}').format( str(field_def['expression']), str(expression.evalErrorString()))) expressions.append(expression) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs()) # Create output vector layer with new attributes error = '' calculationSuccess = True inFeat = QgsFeature() outFeat = QgsFeature() features = vector.features(layer) total = 100.0 / len(features) for current, inFeat in enumerate(features): rownum = current + 1 geometry = inFeat.geometry() outFeat.setGeometry(geometry) attrs = [] for i in range(0, len(mapping)): field_def = mapping[i] expression = expressions[i] exp_context.setFeature(inFeat) exp_context.lastScope().setVariable("row_number", rownum) value = expression.evaluate(exp_context) if expression.hasEvalError(): calculationSuccess = False error = expression.evalErrorString() break attrs.append(value) outFeat.setAttributes(attrs) writer.addFeature(outFeat) progress.setPercentage(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation' ' string:\n') + error)