def set_feature_ids(self, layer: Optional[QgsVectorLayer], fids: List[int]): """ Sets the feature ids to show in the model """ self.beginRemoveRows(QModelIndex(), 0, len(self.features)) self.features = [] self.display_expressions = [] self.endRemoveRows() if layer is None: self.fids = [] return context = QgsExpressionContext() context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(layer)) display_expression = QgsExpression(layer.displayExpression()) display_expression.prepare(context) request = QgsFeatureRequest().setFilterFids(fids) request.setSubsetOfAttributes(display_expression.referencedColumns(), layer.fields()) pending_features = list(layer.getFeatures(request)) self.beginInsertRows(QModelIndex(), 0, len(pending_features)) self.features = pending_features self.fids = fids for f in self.features: context.setFeature(f) self.display_expressions.append(display_expression.evaluate(context)) self.endInsertRows()
def add_source_field_or_expression(field_or_expression): field_index = self.source_layer.fields().lookupField( field_or_expression) if field_index == -1: expression = QgsExpression(field_or_expression) if not expression.hasParserError(): expression.prepare(context) return expression, expression.needsGeometry( ), expression.referencedColumns() return None, False, {field_or_expression}
def _buildfromlayer(self, widget, layerconfig): layername = layerconfig['layer'] keyfield = layerconfig['key'] valuefield = layerconfig['value'] filterexp = layerconfig.get('filter', None) try: layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0] except IndexError: roam.utils.warning("Can't find layer {} in project".format(layername)) return keyfieldindex = layer.fieldNameIndex(keyfield) valuefieldindex = layer.fieldNameIndex(valuefield) if keyfieldindex == -1 or valuefieldindex == -1: roam.utils.warning("Can't find key or value column") return if not filterexp and valuefieldindex == keyfieldindex: values = layer.uniqueValues(keyfieldindex) for value in values: widget.addItem(value, value) return attributes = {keyfieldindex, valuefieldindex} flags = QgsFeatureRequest.NoGeometry expression = None if filterexp: expression = QgsExpression(filterexp) expression.prepare(layer.pendingFields()) if expression.hasParserError(): roam.utils.warning("Expression has parser error: {}".format(expression.parserErrorString())) return if expression.needsGeometry(): flags = QgsFeatureRequest.NoFlags for field in expression.referencedColumns(): index = layer.fieldNameIndex(field) attributes.add(index) values = set() request = QgsFeatureRequest().setFlags(flags).setSubsetOfAttributes(list(attributes)) for feature in layer.getFeatures(request): if expression and not expression.evaluate(feature): continue widget.addItem(feature[keyfieldindex], feature[valuefield]) if self.allownulls: widget.insertItem(0, '(no selection)', None)
def input_form(self): input_form = super(QgisEditTypeValueRelation, self).input_form if self.FilterExpression != '': exp = QgsExpression(self.FilterExpression) filter_expression = { 'expression': exp.expression(), 'referenced_columns': list(exp.referencedColumns()), 'referenced_functions': list(exp.referencedFunctions()) } # For current_values function in filter expression get parameter field fo it if "current_value" in filter_expression['referenced_functions']: groups = re.findall(r'current_value\(\'(.*?)\'\)|(\w+=\w+)', filter_expression['expression']) filter_expression['referencing_fields'] = [ g[0] for g in groups ] else: filter_expression = None # add params for get value input_form['input']['options'].update({ 'key': self.Value, 'value': self.Key, 'usecompleter': True if self.UseCompleter == '1' or self.UseCompleter == 'true' else False, 'layer_id': self.Layer, 'loading': { 'state': None }, 'filter_expression': filter_expression, }) return input_form
def testReferencedColumnsSet(self): QgsExpression.registerFunction(self.referenced_columns_set) exp = QgsExpression('referenced_columns_set()') self.assertEqual(set(exp.referencedColumns()), set(['a', 'b']))
def testReferencedColumnsNoSet(self): QgsExpression.registerFunction(self.no_referenced_columns_set) exp = QgsExpression('no_referenced_columns_set()') self.assertEqual(exp.referencedColumns(), {QgsFeatureRequest.ALL_ATTRIBUTES})
def testReferencedColumnsNoSet(self): success = QgsExpression.registerFunction( self.no_referenced_columns_set) exp = QgsExpression('no_referenced_columns_set()') self.assertEqual(exp.referencedColumns(), {QgsFeatureRequest.AllAttributes})
def _buildfromlayer(self, widget, layerconfig): layername = layerconfig['layer'] keyfield = layerconfig['key'] valuefield = layerconfig['value'] filterexp = layerconfig.get('filter', None) try: layer = QgsMapLayerRegistry.instance().mapLayersByName( layername)[0] except IndexError: roam.utils.warning( "Can't find layer {} in project".format(layername)) return keyfieldindex = layer.fieldNameIndex(keyfield) valuefieldindex = layer.fieldNameIndex(valuefield) if keyfieldindex == -1 or valuefieldindex == -1: roam.utils.warning("Can't find key or value column") return if self.allownulls: item = QStandardItem('(no selection)') item.setData(None, Qt.UserRole) self.listmodel.appendRow(item) attributes = {keyfieldindex, valuefieldindex} iconfieldindex = layer.fieldNameIndex('icon') if iconfieldindex > -1: attributes.add(iconfieldindex) if not filterexp and valuefieldindex == keyfieldindex and iconfieldindex == -1: values = layer.uniqueValues(keyfieldindex) for value in values: value = nullconvert(value) item = QStandardItem(value) item.setData(value, Qt.UserRole) self.listmodel.appendRow(item) return flags = QgsFeatureRequest.NoGeometry expression = None if filterexp: expression = QgsExpression(filterexp) expression.prepare(layer.pendingFields()) if expression.hasParserError(): roam.utils.warning("Expression has parser error: {}".format( expression.parserErrorString())) return if expression.needsGeometry(): flags = QgsFeatureRequest.NoFlags for field in expression.referencedColumns(): index = layer.fieldNameIndex(field) attributes.add(index) request = QgsFeatureRequest().setFlags(flags).setSubsetOfAttributes( list(attributes)) for feature in layer.getFeatures(request): if expression and not expression.evaluate(feature): continue keyvalue = nullconvert(feature[keyfieldindex]) valuvalue = nullconvert(feature[valuefield]) try: path = feature[iconfieldindex] icon = QIcon(path) except KeyError: icon = QIcon() item = QStandardItem(unicode(keyvalue)) item.setData(unicode(valuvalue), Qt.UserRole) item.setIcon(icon) self.listmodel.appendRow(item)
def testReferencedColumnsSet(self): success = QgsExpression.registerFunction(self.referenced_columns_set) exp = QgsExpression("referenced_columns_set()") self.assertEqual(set(exp.referencedColumns()), set(["a", "b"]))
def testReferencedColumnsNoSet(self): success = QgsExpression.registerFunction(self.no_referenced_columns_set) exp = QgsExpression("no_referenced_columns_set()") self.assertEqual(exp.referencedColumns(), {QgsFeatureRequest.AllAttributes})
def _buildfromlayer(self, widget, layerconfig): layername = layerconfig['layer'] keyfield = layerconfig['key'] valuefield = layerconfig['value'] filterexp = layerconfig.get('filter', None) try: layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0] except IndexError: roam.utils.warning("Can't find layer {} in project".format(layername)) return keyfieldindex = layer.fieldNameIndex(keyfield) valuefieldindex = layer.fieldNameIndex(valuefield) if keyfieldindex == -1 or valuefieldindex == -1: roam.utils.warning("Can't find key or value column") return if self.allownulls: item = QStandardItem('(no selection)') item.setData(None, Qt.UserRole) self.listmodel.appendRow(item) attributes = {keyfieldindex, valuefieldindex} iconfieldindex = layer.fieldNameIndex('icon') if iconfieldindex > -1: attributes.add(iconfieldindex) if not filterexp and valuefieldindex == keyfieldindex and iconfieldindex == -1: values = layer.uniqueValues(keyfieldindex) for value in values: value = nullconvert(value) item = QStandardItem(value) item.setData(value, Qt.UserRole) self.listmodel.appendRow(item) return flags = QgsFeatureRequest.NoGeometry expression = None if filterexp: expression = QgsExpression(filterexp) expression.prepare(layer.pendingFields()) if expression.hasParserError(): roam.utils.warning("Expression has parser error: {}".format(expression.parserErrorString())) return if expression.needsGeometry(): flags = QgsFeatureRequest.NoFlags for field in expression.referencedColumns(): index = layer.fieldNameIndex(field) attributes.add(index) request = QgsFeatureRequest().setFlags(flags).setSubsetOfAttributes(list(attributes)) for feature in layer.getFeatures(request): if expression and not expression.evaluate(feature): continue keyvalue = nullconvert(feature[keyfieldindex]) valuvalue = nullconvert(feature[valuefield]) try: path = feature[iconfieldindex] icon = QIcon(path) except KeyError: icon = QIcon() item = QStandardItem(unicode(keyvalue)) item.setData(unicode(valuvalue), Qt.UserRole) item.setIcon(icon) self.listmodel.appendRow(item)
def read_config(self, key=""): # the following code reads the configuration file which setups the plugin to search in the correct database, # table and method settings = QgsSettings() settings.beginGroup("/Discovery") connection = settings.value(key + "connection", "", type=str) self.data_type = settings.value(key + "data_type", "", type=str) self.file = settings.value(key + "file", "", type=str) self.postgisschema = settings.value(key + "schema", "", type=str) self.postgistable = settings.value(key + "table", "", type=str) self.postgissearchcolumn = settings.value(key + "search_column", "", type=str) self.echosearchcolumn = settings.value(key + "echo_search_column", True, type=bool) self.postgisdisplaycolumn = settings.value(key + "display_columns", "", type=str) self.postgisgeomcolumn = settings.value(key + "geom_column", "", type=str) if settings.value("marker_time_enabled", True, type=bool): self.display_time = settings.value("marker_time", 5000, type=int) else: self.display_time = -1 if settings.value("bar_info_time_enabled", True, type=bool): self.bar_info_time = settings.value("bar_info_time", 30, type=int) else: self.bar_info_time = 0 self.info_to_clipboard = settings.value("info_to_clipboard", True, type=bool) scale_expr = settings.value(key + "scale_expr", "", type=str) bbox_expr = settings.value(key + "bbox_expr", "", type=str) if self.is_displayed: self.hide_marker() self.hide_rubber_band() self.is_displayed = False self.make_enabled(False) # assume the config is invalid first self.db_conn = None if self.data_type == "postgres": self.conn_info = dbutils.get_postgres_conn_info(connection) self.layer = None if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \ len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0: return if len(self.conn_info) == 0: iface.messageBar().pushMessage( "Discovery", "The database connection '%s' does not exist!" % connection, level=Qgis.Critical) return if self.data_type == "mssql": self.conn_info = mssql_utils.get_mssql_conn_info(connection) self.layer = None if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \ len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0: return if len(self.conn_info) == 0: iface.messageBar().pushMessage( "Discovery", "The database connection '%s' does not exist!" % connection, level=Qgis.Critical) return elif self.data_type == "gpkg": self.layer = QgsVectorLayer( self.file + '|layername=' + self.postgistable, self.postgistable, 'ogr') self.conn_info = None self.extra_expr_columns = [] self.scale_expr = None self.bbox_expr = None self.make_enabled(True) # optional scale expression when zooming in to results if len(scale_expr) != 0: expr = QgsExpression(scale_expr) if expr.hasParserError(): iface.messageBar().pushMessage("Discovery", "Invalid scale expression: " + expr.parserErrorString(), level=Qgis.Warning) else: self.scale_expr = scale_expr self.extra_expr_columns += expr.referencedColumns() # optional bbox expression when zooming in to results if len(bbox_expr) != 0: expr = QgsExpression(bbox_expr) if expr.hasParserError(): iface.messageBar().pushMessage("Discovery", "Invalid bbox expression: " + expr.parserErrorString(), level=Qgis.Warning) else: self.bbox_expr = bbox_expr self.extra_expr_columns += expr.referencedColumns()
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) close_path = self.parameterAsBool(parameters, self.CLOSE_PATH, context) group_field_name = self.parameterAsString(parameters, self.GROUP_FIELD, context) order_field_name = self.parameterAsString(parameters, self.ORDER_FIELD, context) order_expression = self.parameterAsString(parameters, self.ORDER_EXPRESSION, context) date_format = self.parameterAsString(parameters, self.DATE_FORMAT, context) text_dir = self.parameterAsString(parameters, self.OUTPUT_TEXT_DIR, context) group_field_index = source.fields().lookupField(group_field_name) if group_field_index >= 0: group_field_def = source.fields().at(group_field_index) else: group_field_def = None if order_field_name: order_expression = QgsExpression.quotedColumnRef(order_field_name) if not order_expression: raise QgsProcessingException( self.tr('ORDER_EXPRESSION parameter is missing.')) expression_context = self.createExpressionContext( parameters, context, source) expression = QgsExpression(order_expression) if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) expression.prepare(expression_context) order_field_type = QVariant.String if expression.isField(): field_name = next(iter(expression.referencedColumns())) order_field_type = source.fields().field(field_name).type() fields = QgsFields() if group_field_def is not None: fields.append(group_field_def) begin_field = QgsField('begin', order_field_type) fields.append(begin_field) end_field = QgsField('end', order_field_type) fields.append(end_field) output_wkb = QgsWkbTypes.LineString if QgsWkbTypes.hasM(source.wkbType()): output_wkb = QgsWkbTypes.addM(output_wkb) if QgsWkbTypes.hasZ(source.wkbType()): output_wkb = QgsWkbTypes.addZ(output_wkb) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, output_wkb, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) if text_dir and not (os.path.exists(text_dir)): raise QgsProcessingException( self.tr("The text output directory does not exist")) points = dict() required_fields = expression.referencedColumns() required_fields.add(group_field_name) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(required_fields, source.fields()), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().constGet().clone() if group_field_index >= 0: group = f[group_field_index] else: group = 1 expression_context.setFeature(f) order = expression.evaluate(expression_context) if date_format != '': order = datetime.strptime(str(order), date_format) if group in points: points[group].append((order, point)) else: points[group] = [(order, point)] feedback.setProgress(int(current * total)) feedback.setProgress(0) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.ellipsoid()) current = 0 total = 100.0 / len(points) if points else 1 for group, vertices in points.items(): if feedback.isCanceled(): break vertices.sort(key=lambda x: (x[0] is None, x[0])) f = QgsFeature() attributes = [] if group_field_index >= 0: attributes.append(group) attributes.extend([vertices[0][0], vertices[-1][0]]) f.setAttributes(attributes) line = [node[1] for node in vertices] if close_path is True: if line[0] != line[-1]: line.append(line[0]) if text_dir: fileName = os.path.join(text_dir, '%s.txt' % group) with open(fileName, 'w') as fl: fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') for i in range(len(line)): if i == 0: fl.write('startAt=%f;%f;90\n' % (line[i].x(), line[i].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(QgsPointXY(line[i - 1]), QgsPointXY(line[i])) fl.write('%f;%f;90\n' % (angle, distance)) f.setGeometry(QgsGeometry(QgsLineString(line))) sink.addFeature(f, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def data( self ): if self._dataLoaded: return self._x, self._y, self._z self._dataLoaded=True self._x = None self._y = None self._z = None self._gridShape=None self._gridTested=False self._dataLoaded=True source=self._source zField=self._zField if source is None or zField is None or zField == '': return self._x, self._y, self._z discardTolerance=self._discardTolerance feedback=self._feedback total = source.featureCount() percent = 100.0 / total if total > 0 else 0 count = 0 x = list() y = list() z = list() try: if source.fields().lookupField(zField) >= 0: zField='"'+zField.replace('"','""')+'"' expression=QgsExpression(zField) if expression.hasParserError(): raise ContourError(tr("Cannot parse")+" "+zField) fields=source.fields() context=QgsExpressionContext() context.setFields(fields) if not expression.prepare(context): raise ContourError(tr("Cannot evaluate value")+ " "+zField) request = QgsFeatureRequest() request.setSubsetOfAttributes( expression.referencedColumns(),fields) if self._sourceFids is not None: request.setFilterFids(self._sourceFids) for current,feat in enumerate(source.getFeatures( request )): try: if feedback.isCanceled(): raise ContourError('Cancelled by user') feedback.setProgress(int(current * percent)) context.setFeature(feat) zval=expression.evaluate(context) try: zval=float(zval) except ValueError: raise ContourError(tr("Z value {0} is not number") .format(zval)) if zval is not None: fgeom = feat.geometry() if QgsWkbTypes.flatType(fgeom.wkbType()) != QgsWkbTypes.Point: raise ContourError(tr("Invalid geometry type for contouring - must be point geometry")) geom=fgeom.asPoint() x.append(geom.x()) y.append(geom.y()) z.append(zval) except Exception as ex: raise count = count + 1 npt=len(x) if npt > 0: x=np.array(x) y=np.array(y) z=np.array(z) if discardTolerance > 0: index=ContourUtils.discardDuplicatePoints( x,y,discardTolerance,self.crs().isGeographic()) npt1=len(index) if npt1 < npt: x=x[index] y=y[index] z=z[index] feedback.pushInfo(tr("{0} near duplicate points discarded - tolerance {1}") .format(npt-npt1,discardTolerance)) except ContourError as ce: feedback.reportError(ce.message()) feedback.setProgress(0) return self._x,self._y,self._z finally: feedback.setProgress(0) if len(x) < 3: feedback.reportError(tr("Too few points to contour")) return self._x, self._y, self._z self._x=x self._y=y self._z=z return self._x, self._y, self._z
def data( self ): if self._dataLoaded: return self._x, self._y, self._z self._dataLoaded=True self._x = None self._y = None self._z = None self._gridShape=None self._gridTested=False self._dataLoaded=True source=self._source zField=self._zField if source is None or zField is None or zField == '': return self._x, self._y, self._z discardTolerance=self._discardTolerance feedback=self._feedback total = source.featureCount() percent = 100.0 / total if total > 0 else 0 count = 0 x = list() y = list() z = list() try: if source.fields().lookupField(zField) >= 0: zField='"'+zField.replace('"','""')+'"' expression=QgsExpression(zField) if expression.hasParserError(): raise ContourError(tr("Cannot parse")+" "+zField) fields=source.fields() context=QgsExpressionContext() context.setFields(fields) if not expression.prepare(context): raise ContourError(tr("Cannot evaluate value")+ " "+zField) request = QgsFeatureRequest() request.setSubsetOfAttributes( expression.referencedColumns(),fields) if self._sourceFids is not None: request.setFilterFids(self._sourceFids) for current,feat in enumerate(source.getFeatures( request )): try: if feedback.isCanceled(): raise ContourError('Cancelled by user') feedback.setProgress(int(current * percent)) context.setFeature(feat) zval=expression.evaluate(context) if zval is None or (isinstance(zval,QVariant) and zval.isNull()): continue try: zval=float(zval) except ValueError: raise ContourError(tr("Z value {0} is not number") .format(zval)) if zval is not None: fgeom = feat.geometry() if QgsWkbTypes.flatType(fgeom.wkbType()) != QgsWkbTypes.Point: raise ContourError(tr("Invalid geometry type for contouring - must be point geometry")) geom=fgeom.asPoint() x.append(geom.x()) y.append(geom.y()) z.append(zval) except Exception as ex: raise count = count + 1 npt=len(x) if npt > 0: x=np.array(x) y=np.array(y) z=np.array(z) if discardTolerance > 0: index=ContourUtils.discardDuplicatePoints( x,y,discardTolerance,self.crs().isGeographic()) npt1=len(index) if npt1 < npt: x=x[index] y=y[index] z=z[index] feedback.pushInfo(tr("{0} near duplicate points discarded - tolerance {1}") .format(npt-npt1,discardTolerance)) except ContourError as ce: feedback.reportError(ce.message()) feedback.setProgress(0) return self._x,self._y,self._z finally: feedback.setProgress(0) if len(x) < 3: feedback.reportError(tr("Too few points to contour")) return self._x, self._y, self._z self._x=x self._y=y self._z=z return self._x, self._y, self._z