def default_value(defaultconfig, feature, layer): if isinstance(defaultconfig, dict): try: defaulttype = defaultconfig['type'] except KeyError: raise DefaultError("No type key set for default config") try: defaultprovider = defaultproviders[defaulttype] except KeyError as ex: log(ex) raise DefaultError("No default provider found for {}".format(defaulttype)) try: value = defaultprovider(feature, layer, defaultconfig) except DefaultError as ex: roam.utils.log(ex) value = None else: # Pass it though a series of filters to get the default value if it's just pain text value = os.path.expandvars(defaultconfig) if '[%' in defaultconfig and '%]' in defaultconfig: # TODO Use regex value = QgsExpression.replaceExpressionText(value, feature, layer) roam.utils.debug("Default value: {}".format(value)) return value
def stamp_image(image, expression_str, position, feature): painter = QPainter(image) data = QgsExpression.replaceExpressionText(expression_str, feature, None) if not data: return image data = data.replace(r"\n", "<br>") style = """ body { color: yellow; } """ doc = QTextDocument() doc.setDefaultStyleSheet(style) data = "<body>{}</body>".format(data) doc.setHtml(data) point = QPointF(20, 20) # Wrap the text so we don't go crazy if doc.size().width() > 300: doc.setTextWidth(300) if position == "top-left": point = QPointF(20, 20) elif position == "top-right": x = image.width() - 20 - doc.size().width() point = QPointF(x, 20) elif position == "bottom-left": point = QPointF(20, image.height() - 20 - doc.size().height()) elif position == "bottom-right": x = image.width() - 20 - doc.size().width() y = image.height() - 20 - doc.size().height() point = QPointF(x, y) painter.translate(point) doc.drawContents(painter) return image
def default_value(defaultconfig, feature, layer): if isinstance(defaultconfig, dict): try: defaulttype = defaultconfig['type'] except KeyError: raise DefaultError("No type key set for default config") try: defaultprovider = defaultproviders[defaulttype] except KeyError as ex: log(ex) raise DefaultError( "No default provider found for {}".format(defaulttype)) try: value = defaultprovider(feature, layer, defaultconfig) except DefaultError as ex: roam.utils.log(ex) value = None else: # Pass it though a series of filters to get the default value if it's just pain text value = os.path.expandvars(defaultconfig) if '[%' in defaultconfig and '%]' in defaultconfig: # TODO Use regex value = QgsExpression.replaceExpressionText(value, feature, layer) roam.utils.debug("Default value: {}".format(value)) return value
def getdefaults(widgets, feature, layer, canvas): defaults = {} for field, config in widgets: default = config.get("default", None) if default is None: continue if isinstance(default, dict): defaultconfig = default try: defaulttype = defaultconfig['type'] defaultprovider = defaultproviders[defaulttype] value = defaultprovider(feature, layer, field, defaultconfig, canvas) except KeyError as ex: log(ex) continue except DefaultError as ex: log(ex) value = None else: # Pass it though a series of filters to get the default value if '[%' in default and '%]' in default: # TODO Use regex default = QgsExpression.replaceExpressionText(default, feature, layer ) value = os.path.expandvars(default) log("Default value: {}".format(value)) defaults[field] = value defaults.update(featureform.loadsavedvalues(layer)) return defaults
def getdefaults(widgets, feature, layer, canvas): defaults = {} for field, config in widgets: default = config.get("default", None) if default is None: continue if isinstance(default, dict): defaultconfig = default try: defaulttype = defaultconfig['type'] defaultprovider = defaultproviders[defaulttype] value = defaultprovider(feature, layer, field, defaultconfig, canvas) except KeyError as ex: log(ex) continue except DefaultError as ex: log(ex) value = None else: # Pass it though a series of filters to get the default value if '[%' in default and '%]' in default: # TODO Use regex default = QgsExpression.replaceExpressionText(default, feature, layer ) value = os.path.expandvars(default) log("Default value: {}".format(value)) defaults[field] = value defaults.update(featureform.loadsavedvalues(layer)) return defaults
def replace_expression_placeholders(text:str, feature: QgsFeature): """ Replace any QGIS expression placeholders in the given string. :param text: The text to replace the expression values in. :param feature: The feature to pull any expression values from. :return: A string with expression values replaced. """ return QgsExpression.replaceExpressionText(text, expression_context_for_feature(feature))
def evaluate_expression(self, value): """ Computes the resulting value based on the current expression context. :param value: Value to evaluate. :type value: str :return: Returns the value based on the current expression context. :rtype: str """ exp_ctx = self.createExpressionContext() return QgsExpression.replaceExpressionText(value, exp_ctx)
def data(self, role): if role == Qt.DisplayRole: layer = self.form.QGISLayer displayfield = self.form.settings.get('display', '') if not displayfield: # If there is no display field set then just grab the first field display = self.feature[0] else: # If there is then run it as an expression display = QgsExpression.replaceExpressionText(layer.displayField(), self.feature, layer ) return "{0} ({1})".format(display, self.form.label) elif role == Qt.DecorationRole: icon = QIcon(self.form.icon)
def processAlgorithm(self, parameters, context, feedback): layers = self.parameterAsLayerList(parameters, self.INPUT_DATASOURCES, context) query = self.parameterAsString(parameters, self.INPUT_QUERY, context) uid_field = self.parameterAsString(parameters, self.INPUT_UID_FIELD, context) geometry_field = self.parameterAsString(parameters, self.INPUT_GEOMETRY_FIELD, context) geometry_type = self.parameterAsEnum(parameters, self.INPUT_GEOMETRY_TYPE, context) geometry_crs = self.parameterAsCrs(parameters, self.INPUT_GEOMETRY_CRS, context) df = QgsVirtualLayerDefinition() for layerIdx, layer in enumerate(layers): df.addSource('input{}'.format(layerIdx + 1), layer.id()) if query == '': raise QgsProcessingException( self.tr('Empty SQL. Please enter valid SQL expression and try again.')) else: localContext = self.createExpressionContext(parameters, context) expandedQuery = QgsExpression.replaceExpressionText(query, localContext) df.setQuery(expandedQuery) if uid_field: df.setUid(uid_field) if geometry_type == 1: # no geometry df.setGeometryWkbType(QgsWkbTypes.NoGeometry) else: if geometry_field: df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) if geometry_crs.isValid(): df.setGeometrySrid(geometry_crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): raise QgsProcessingException(vLayer.dataProvider().error().message()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) features = vLayer.getFeatures() total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break sink.addFeature(inFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): layers = self.parameterAsLayerList(parameters, self.INPUT_DATASOURCES, context) query = self.parameterAsString(parameters, self.INPUT_QUERY, context) uid_field = self.parameterAsString(parameters, self.INPUT_UID_FIELD, context) geometry_field = self.parameterAsString(parameters, self.INPUT_GEOMETRY_FIELD, context) geometry_type = self.parameterAsEnum(parameters, self.INPUT_GEOMETRY_TYPE, context) geometry_crs = self.parameterAsCrs(parameters, self.INPUT_GEOMETRY_CRS, context) df = QgsVirtualLayerDefinition() for layerIdx, layer in enumerate(layers): df.addSource('input{}'.format(layerIdx + 1), layer.id()) if query == '': raise QgsProcessingException( self.tr('Empty SQL. Please enter valid SQL expression and try again.')) else: localContext = self.createExpressionContext(parameters, context) expandedQuery = QgsExpression.replaceExpressionText(query, localContext) df.setQuery(expandedQuery) if uid_field: df.setUid(uid_field) if geometry_type == 1: # no geometry df.setGeometryWkbType(QgsWkbTypes.NoGeometry) else: if geometry_field: df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) if geometry_crs.isValid(): df.setGeometrySrid(geometry_crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): raise QgsProcessingException(vLayer.dataProvider().error().message()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) features = vLayer.getFeatures() total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break sink.addFeature(inFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def results_from_query(self, infoblockdef, layer, feature, mapkey, lastresults=None): def get_key(): try: keycolumn = infoblockdef['mapping']['mapkey'] if keycolumn == 'from_info1': if 'mapkey' in lastresults: return lastresults['mapkey'] else: return [] else: return feature[keycolumn] except KeyError: return mapkey def get_layer(): connection = infoblockdef.get('connection', "from_layer") if isinstance(connection, dict): return layer_by_name(connection['layer']) elif connection == "from_layer": return layer else: raise NotImplementedError( "{} is not a supported connection type".format(connection)) if not lastresults: lastresults = {} sql = infoblockdef['query'] layer = get_layer() db = database.Database.fromLayer(layer) mapkey = get_key() attributes = values_from_feature(feature, safe_names=True) attributes['mapkey'] = mapkey # Run the SQL text though the QGIS expression engine first. sql = QgsExpression.replaceExpressionText(sql, feature, layer) results = db.query(sql, **attributes) results = list(results) return results
def update(self, cursor): global image images = {} feature = cursor.feature fields = [field.name() for field in feature.fields()] data = OrderedDict() items = [] for field, value in zip(fields, feature.attributes()): data[field] = value item = "<tr><th>{0}</th> <td>${{{0}}}</td></tr>".format(field) items.append(item) rowtemple = Template("".join(items)) rowshtml = updateTemplate(data, rowtemple) form = cursor.form layer = cursor.layer if form: name = "{}".format(layer.name(), form.label) else: name = layer.name() info = dict(TITLE=name, ROWS=rowshtml) html = updateTemplate(info, template) base = os.path.dirname(os.path.abspath(__file__)) baseurl = QUrl.fromLocalFile(base + "\\") if form: displaytext = form.settings.get("display", None) display = QgsExpression.replaceExpressionText(displaytext, cursor.feature, layer) else: display = str(feature[0]) self.countlabel.setText(str(cursor)) self.displaylabel.setText(display) self.attributesView.setHtml(html, baseurl) self.editButton.setVisible(not form is None) self.featureupdated.emit(layer, cursor.feature, cursor.features)
def replace_expression_text(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 = find_vector_layer(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 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 add_form_scope = 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(): field_name = field.name() if feat_fields.indexOf(field_name) != -1: feat.setAttribute(field_name, f[field_name]) # Add form scope to expression context if add_form_scope: exp_context.appendScope(QgsExpressionContextUtils.formScope(feat)) exp_context.setFeature(feat) exp_context.setFields(feat.fields()) # 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): layers = self.parameterAsLayerList(parameters, self.INPUT_DATASOURCES, context) query = self.parameterAsString(parameters, self.INPUT_QUERY, context) uid_field = self.parameterAsString(parameters, self.INPUT_UID_FIELD, context) geometry_field = self.parameterAsString(parameters, self.INPUT_GEOMETRY_FIELD, context) geometry_type = self.parameterAsEnum(parameters, self.INPUT_GEOMETRY_TYPE, context) geometry_crs = self.parameterAsCrs(parameters, self.INPUT_GEOMETRY_CRS, context) df = QgsVirtualLayerDefinition() for layerIdx, layer in enumerate(layers): # Issue https://github.com/qgis/QGIS/issues/24041 # When using this algorithm from the graphic modeler, it may try to # access (thanks the QgsVirtualLayerProvider) to memory layer that # belongs to temporary QgsMapLayerStore, not project. # So, we write them to disk is this is the case. if context.project() and not context.project().mapLayer( layer.id()): basename = "memorylayer." + QgsVectorFileWriter.supportedFormatExtensions( )[0] tmp_path = QgsProcessingUtils.generateTempFilename(basename) QgsVectorFileWriter.writeAsVectorFormat( layer, tmp_path, layer.dataProvider().encoding()) df.addSource('input{}'.format(layerIdx + 1), tmp_path, "ogr") else: df.addSource('input{}'.format(layerIdx + 1), layer.id()) if query == '': raise QgsProcessingException( self. tr('Empty SQL. Please enter valid SQL expression and try again.' )) localContext = self.createExpressionContext(parameters, context) expandedQuery = QgsExpression.replaceExpressionText( query, localContext) df.setQuery(expandedQuery) if uid_field: df.setUid(uid_field) if geometry_type == 1: # no geometry df.setGeometryWkbType(QgsWkbTypes.NoGeometry) else: if geometry_field: df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) if geometry_crs.isValid(): df.setGeometrySrid(geometry_crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): raise QgsProcessingException( vLayer.dataProvider().error().message()) if vLayer.wkbType() == QgsWkbTypes.Unknown: raise QgsProcessingException(self.tr("Cannot find geometry field")) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) features = vLayer.getFeatures() total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break sink.addFeature(inFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}