def updateGui(self, feature): # The important part: get the feature iterator with an expression request = QgsFeatureRequest().setFilterExpression ( u'"pand_id" = \'' + unicode(feature['gebouwnummer']) + '\'' ) request.setFlags( QgsFeatureRequest.NoGeometry ) features = self.geomLayer.getFeatures( request ) atr = None fields = self.geomLayer.pendingFields() field_names = [field.name() for field in fields] for elem in features: self.editFeature = elem atr = dict(zip(field_names, elem.attributes())) if atr is not None: atr = {i:j for i,j in atr.items() if j } self.newGroupBox.hide() self.editGroupBox.show() self.verwijderWidget.show() self.showValues(feature, atr) self.actionButtonBox.setDisabled(True) else: self.newGroupBox.show() self.editGroupBox.hide() self.verwijderWidget.hide() self.editFeature = None self.statusNieuwLabel.setText(unicode(feature['status'])) self.pandidLabel.setText(unicode(feature['gebouwnummer']))
def testGetFeaturesFilterRectTests(self): extent = QgsRectangle(-70, 67, -60, 80) request = QgsFeatureRequest().setFilterRect(extent) features = [f['pk'] for f in self.source.getFeatures(request)] all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) assert set(features) == set([2, 4]), 'Got {} instead'.format(features) self.assertTrue(all_valid) # test that results match QgsFeatureRequest.acceptFeature for f in self.source.getFeatures(): self.assertEqual(request.acceptFeature(f), f['pk'] in set([2, 4])) # test with an empty rectangle extent = QgsRectangle() request = QgsFeatureRequest().setFilterRect(extent) features = [f['pk'] for f in self.source.getFeatures(request)] all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) assert set(features) == set([1, 2, 3, 4, 5]), 'Got {} instead'.format(features) self.assertTrue(all_valid) # ExactIntersection flag set, but no filter rect set. Should be ignored. request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.ExactIntersect) features = [f['pk'] for f in self.source.getFeatures(request)] all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) assert set(features) == set([1, 2, 3, 4, 5]), 'Got {} instead'.format(features) self.assertTrue(all_valid)
def findFeaturesAt(mapPoint, layerConfig, mapTool): """ To find features from a given position in a given layer :param mapPoint: the map position :param layerConfig: the layer in which we are looking for features :param mapTool: a QgsMapTool instance :return: features found in layer """ if layerConfig is None: return None tolerance = layerConfig.tolerance if layerConfig.unit == QgsTolerance.Pixels: layTolerance = Finder.calcCanvasTolerance(mapTool.toCanvasCoordinates(mapPoint), layerConfig.layer, mapTool, tolerance) elif layerConfig.unit == QgsTolerance.ProjectUnits: layTolerance = Finder.calcMapTolerance(mapPoint, layerConfig.layer, mapTool, tolerance) else: layTolerance = tolerance layPoint = mapTool.toLayerCoordinates(layerConfig.layer, mapPoint) searchRect = QgsRectangle(layPoint.x() - layTolerance, layPoint.y() - layTolerance, layPoint.x() + layTolerance, layPoint.y() + layTolerance) request = QgsFeatureRequest() request.setFilterRect(searchRect) request.setFlags(QgsFeatureRequest.ExactIntersect) features = [] for feature in layerConfig.layer.getFeatures(request): if layerConfig.layer.geometryType() == QGis.Polygon: dist, nearest, vertex = feature.geometry().closestSegmentWithContext(mapPoint) if QgsGeometry.fromPoint(nearest).intersects(searchRect): features.append(QgsFeature(feature)) else: features.append(QgsFeature(feature)) return features
def writeTmpLayer(layer, restrictToExtent, iface, extent): fields = layer.fields() usedFields = [] for count, field in enumerate(fields): fieldIndex = fields.indexFromName(unicode(field.name())) editorWidget = layer.editorWidgetSetup(fieldIndex).type() addField = False try: if layer.renderer().classAttribute() == field.name(): addField = True except: pass if layer.customProperty("labeling/fieldName") == field.name(): addField = True if (editorWidget != 'Hidden'): addField = True if addField: usedFields.append(count) uri = TYPE_MAP[layer.wkbType()] crs = layer.crs() if crs.isValid(): uri += '?crs=' + crs.authid() for field in usedFields: fieldIndex = layer.fields().indexFromName(unicode( layer.fields().field(field).name())) editorWidget = layer.editorWidgetSetup(fieldIndex).type() fieldType = layer.fields().field(field).type() fieldName = layer.fields().field(field).name() if (editorWidget == 'Hidden'): fieldName = "q2wHide_" + fieldName fieldType = "double" if (fieldType == QVariant.Double or fieldType == QVariant.Int) else ("string") uri += '&field=' + unicode(fieldName) + ":" + fieldType newlayer = QgsVectorLayer(uri, layer.name(), 'memory') writer = newlayer.dataProvider() outFeat = QgsFeature() if restrictToExtent and extent == "Canvas extent": canvas = iface.mapCanvas() extent = canvas.extent() canvasCRS = canvas.mapSettings().destinationCrs() layerCRS = layer.crs() try: transform = QgsCoordinateTransform(canvasCRS, layerCRS, QgsProject.instance()) except: transform = QgsCoordinateTransform(canvasCRS, layerCRS) projectedExtent = transform.transformBoundingBox(extent) request = QgsFeatureRequest(projectedExtent) request.setFlags(QgsFeatureRequest.ExactIntersect) features = layer.getFeatures(request) else: features = layer.getFeatures() for feature in features: if feature.geometry() is not None: outFeat.setGeometry(feature.geometry()) attrs = [feature[f] for f in usedFields] if attrs: outFeat.setAttributes(attrs) writer.addFeatures([outFeat]) return newlayer
def clone_layer(layer, keep_selection=True): """Duplicate the layer by taking the same source and copying keywords. :param keep_selection: If we should keep the selection. Default to true. :type keep_selection: bool :param layer: Layer to be duplicated. :type layer: QgsMapLayer :return: The new QgsMapLayer object. :rtype: QgsMapLayer """ if is_vector_layer(layer): new_layer = QgsVectorLayer( layer.source(), layer.name(), layer.providerType()) if keep_selection and layer.selectedFeatureCount() > 0: request = QgsFeatureRequest() request.setFilterFids(layer.selectedFeatureIds()) request.setFlags(QgsFeatureRequest.NoGeometry) iterator = layer.getFeatures(request) new_layer.setSelectedFeatures([k.id() for k in iterator]) else: new_layer = QgsRasterLayer( layer.source(), layer.name(), layer.providerType()) new_layer.keywords = copy_layer_keywords(layer.keywords) return layer
def featureCount(self): if not self.subsetString(): return len(self._features) else: req = QgsFeatureRequest() req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) return len([f for f in self.getFeatures(req)])
def uniqueValues(self, fieldIndex, limit=1): results = set() if fieldIndex >= 0 and fieldIndex < self.fields().count(): req = QgsFeatureRequest() req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([fieldIndex]) for f in self.getFeatures(req): results.add(f.attributes()[fieldIndex]) return results
def getLayerFeature(self): layerFeature = QgsFeature() featReq = QgsFeatureRequest().setFilterFid(self.layerFeatureId) if not self.featureLayer.hasGeometryType(): featReq.setFlags(QgsFeatureRequest.NoGeometry) if self.featureLayer.getFeatures(featReq).nextFeature(layerFeature): return layerFeature else: return None
def test_sql2(self): l2 = QgsVectorLayer(os.path.join(self.testDataDir, "france_parts.shp"), "france_parts", "ogr", False) self.assertEqual(l2.isValid(), True) QgsProject.instance().addMapLayer(l2) query = toPercent("SELECT * FROM france_parts") l4 = QgsVectorLayer("?query=%s&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().wkbType(), 3) self.assertEqual(l4.dataProvider().crs().postgisSrid(), 4326) n = 0 r = QgsFeatureRequest(QgsRectangle(-1.677, 49.624, -0.816, 49.086)) for f in l4.getFeatures(r): self.assertEqual(f.geometry() is not None, True) self.assertEqual(f.attributes()[0], 2661) n += 1 self.assertEqual(n, 1) # use uid query = toPercent("SELECT * FROM france_parts") l5 = QgsVectorLayer("?query=%s&geometry=geometry:polygon:4326&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l5.isValid(), True) idSum = sum(f.id() for f in l5.getFeatures()) self.assertEqual(idSum, 10659) r = QgsFeatureRequest(2661) idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) r = QgsFeatureRequest() r.setFilterFids([2661, 2664]) self.assertEqual(sum(f.id() for f in l5.getFeatures(r)), 2661 + 2664) # test attribute subset r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.SubsetOfAttributes) r.setSubsetOfAttributes([1]) s = [(f.id(), f.attributes()[1]) for f in l5.getFeatures(r)] self.assertEqual(sum([x[0] for x in s]), 10659) self.assertEqual(sum([x[1] for x in s]), 3064.0) # test NoGeometry # by request flag r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.NoGeometry) self.assertEqual(all([not f.hasGeometry() for f in l5.getFeatures(r)]), True) # test subset self.assertEqual(l5.dataProvider().featureCount(), 4) l5.setSubsetString("ObjectId = 2661") idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) self.assertEqual(l5.dataProvider().featureCount(), 1)
def layerData(self, layer, request={}, offset=0): # Retrieve the data for a layer first = True data = {} fields = [] fieldTypes = [] fr = QgsFeatureRequest() if request: if 'exact' in request and request['exact']: fr.setFlags(QgsFeatureRequest.ExactIntersect) if 'nogeom' in request and request['nogeom']: fr.setFlags(QgsFeatureRequest.NoGeometry) if 'fid' in request: fr.setFilterFid(request['fid']) elif 'extents' in request: fr.setFilterRect(QgsRectangle(*request['extents'])) if 'attributes' in request: fr.setSubsetOfAttributes(request['attributes']) # IMPORTANT - we do not use `for f in layer.getFeatures(fr):` as we need # to verify that existing attributes and geometry are correctly cleared # from the feature when calling nextFeature() it = layer.getFeatures(fr) f = QgsFeature() while it.nextFeature(f): if first: first = False for field in f.fields(): fields.append(str(field.name())) fieldTypes.append(str(field.typeName())) if sys.version_info.major == 2: fielddata = dict((name, str(f[name])) for name in fields) else: fielddata = dict((name, str(f[name])) for name in fields) g = f.geometry() if not g.isEmpty(): fielddata[geomkey] = str(g.exportToWkt()) else: fielddata[geomkey] = "None" fielddata[fidkey] = f.id() id = fielddata[fields[0]] description = fielddata[fields[1]] fielddata['id'] = id fielddata['description'] = description data[f.id() + offset] = fielddata if 'id' not in fields: fields.insert(0, 'id') if 'description' not in fields: fields.insert(1, 'description') fields.append(fidkey) fields.append(geomkey) return fields, fieldTypes, data
def search(self): project = QgsProject.instance() layerId, ok = project.readEntry(self.pluginName, 'layerId') fieldName, ok = project.readEntry(self.pluginName, 'fieldName') pattern = self.searchText.text() self.layer = QgsMapLayerRegistry.instance().mapLayer(layerId) if self.layer is None: self.iface.messageBar().pushMessage(self.name, self.tr("Choose a layer first in settings dialog."), QgsMessageBar.WARNING, 3) self.showSettings() return if fieldName == "": self.iface.messageBar().pushMessage(self.name, self.tr("Choose a field first in settings dialog."), QgsMessageBar.WARNING, 3) self.showSettings() return fields = self.layer.dataProvider().fields() fieldIndex = fields.indexFromName(fieldName) # create feature request featReq = QgsFeatureRequest() featReq.setFlags(QgsFeatureRequest.NoGeometry) featReq.setSubsetOfAttributes([fieldIndex]) iterator = self.layer.getFeatures(featReq) # process search f = QgsFeature() results = [] self.continueSearch = True while iterator.nextFeature(f) and self.continueSearch: if self.evaluate(f[fieldName], pattern): results.append(f.id()) QCoreApplication.processEvents() # process results if self.continueSearch: msg = self.tr("{} features found").format(len(results)) legend = self.iface.legendInterface() if (not legend.isLayerVisible(self.layer)): msg += self.tr(' - The layer named {} is not visible').format(self.layer.name()) self.iface.messageBar().pushMessage(self.name, msg, QgsMessageBar.INFO, 2) self.processResults(results)
def getFeatIdList(self, currentLayer): #getting all features ids if self.mFieldExpressionWidget.currentText() == '': featIdList = currentLayer.allFeatureIds() elif not self.mFieldExpressionWidget.isValidExpression(): self.iface.messageBar().pushMessage(self.tr('Warning!'), self.tr('Invalid attribute filter!'), level=QgsMessageBar.WARNING, duration=2) return [] else: request = QgsFeatureRequest().setFilterExpression(self.mFieldExpressionWidget.asExpression()) request.setFlags(QgsFeatureRequest.NoGeometry) featIdList = [i.id() for i in currentLayer.getFeatures(request)] #sort is faster than sorted (but sort is just available for lists) featIdList.sort() return featIdList
def removeLastFeatures(self): self.isIniEditable = self.layerPolygon.isEditable() if not self.isIniEditable: self.layerPolygon.startEditing() exp = QgsExpression( '"id_add" = {}'.format( self.getMaximumValueAdd() ) ) request = QgsFeatureRequest( exp ) request.setFlags( QgsFeatureRequest.NoGeometry ) it = self.layerPolygon.getFeatures( request ) for feat in it: self.layerPolygon.deleteFeature( feat.id() ) self.layerPolygon.commitChanges() if self.isIniEditable: self.layerPolygon.startEditing() self.layerPolygon.updateExtents() self.dockWidgetGui.btnRemoveLastFeatures.setEnabled( False )
def draw (self, resetStyle = False): xField = self.xProperty.currentText() yField = self.yProperty.currentText() #X-Axis limits self.xMinTime.setVisible (xField == 'Time') self.xMaxTime.setVisible (xField == 'Time') self.xMinFloat.setVisible(xField != 'Time') self.xMaxFloat.setVisible(xField != 'Time') self.xMin = self.xMinFloat if xField != 'Time' else self.xMinTime self.xMax = self.xMaxFloat if xField != 'Time' else self.xMaxTime #Y-Axis limits self.yMinTime.setVisible (yField == 'Time') self.yMaxTime.setVisible (yField == 'Time') self.yMinFloat.setVisible(yField != 'Time') self.yMaxFloat.setVisible(yField != 'Time') self.yMin = self.yMinFloat if yField != 'Time' else self.yMinTime self.yMax = self.yMaxFloat if yField != 'Time' else self.yMaxTime ySorted = self.ySorted.isChecked() self.plotWidget.clean (xField = xField, yField = yField, resetStyle = resetStyle) request = QgsFeatureRequest () request.setFlags(QgsFeatureRequest.NoGeometry) for foi in self.foiList: request.setFilterExpression("foi = '%s'" % foi) if ySorted: dataSerie = [(f.attribute(yField),f.attribute(xField)) for f in self.layer.getFeatures(request)] else: dataSerie = [(f.attribute(xField),f.attribute(yField)) for f in self.layer.getFeatures(request)] dataSerie.sort() if len (dataSerie): if ySorted: y,x = map(list, zip(*dataSerie)) else: x,y = map(list, zip(*dataSerie)) self.plotWidget.plot(x,y, f.attribute('name')) self.plotWidget.applyTimeFormat (self.timeFormat.text()) self.plotWidget.draw () styles = self.StylesTable (self.plotWidget.ax.get_lines()) self.stylesTable.setModel (styles) styles.dataChanged.connect(self.plotWidget.draw)
def is25d(layer, canvas, restrictToExtent, extent): if layer.type() != layer.VectorLayer: return False if layer.geometryType() != QgsWkbTypes.PolygonGeometry: return False vts = layer.customProperty("VectorTilesReader/vector_tile_source") if vts is not None: return False renderer = layer.renderer() if isinstance(renderer, Qgs25DRenderer): return True symbols = [] if isinstance(renderer, QgsCategorizedSymbolRenderer): categories = renderer.categories() for category in categories: symbols.append(category.symbol()) elif isinstance(renderer, QgsGraduatedSymbolRenderer): ranges = renderer.ranges() for range in ranges: symbols.append(range.symbol()) elif isinstance(renderer, QgsRuleBasedRenderer): rules = renderer.rootRule().children() for rule in rules: symbols.append(rule.symbol()) else: renderContext = QgsRenderContext.fromMapSettings(canvas.mapSettings()) fields = layer.fields() if restrictToExtent and extent == "Canvas extent": request = QgsFeatureRequest(canvas.extent()) request.setFlags(QgsFeatureRequest.ExactIntersect) features = layer.getFeatures(request) else: features = layer.getFeatures() renderer.startRender(renderContext, fields) for feature in features: symbol = renderer.symbolForFeature(feature, renderContext) symbols.append(symbol) renderer.stopRender(renderContext) for sym in symbols: sl1 = sym.symbolLayer(1) sl2 = sym.symbolLayer(2) if (isinstance(sl1, QgsGeometryGeneratorSymbolLayer) and isinstance(sl2, QgsGeometryGeneratorSymbolLayer)): return True return False
def layerData(layer, request={}, offset=0): first = True data = {} fields = [] fieldTypes = [] fr = QgsFeatureRequest() if request: if 'exact' in request and request['exact']: fr.setFlags(QgsFeatureRequest.ExactIntersect) if 'nogeom' in request and request['nogeom']: fr.setFlags(QgsFeatureRequest.NoGeometry) if 'fid' in request: fr.setFilterFid(request['fid']) elif 'extents' in request: fr.setFilterRect(QgsRectangle(*request['extents'])) if 'attributes' in request: fr.setSubsetOfAttributes(request['attributes']) for f in layer.getFeatures(fr): if first: first = False for field in f.fields(): fields.append(str(field.name())) fieldTypes.append(str(field.typeName())) fielddata = dict((name, unicode(f[name])) for name in fields) g = f.geometry() if g: fielddata[geomkey] = str(g.exportToWkt()) else: fielddata[geomkey] = "None" fielddata[fidkey] = f.id() id = fielddata[fields[0]] description = fielddata[fields[1]] fielddata['id'] = id fielddata['description'] = description data[f.id() + offset] = fielddata if 'id' not in fields: fields.insert(0, 'id') if 'description' not in fields: fields.insert(1, 'description') fields.append(fidkey) fields.append(geomkey) return fields, fieldTypes, data
def findNearestPointL(self, searchPt, lineLayer, tolerance): # given a point, find the nearest point (within the tolerance) within the line layer # returns QgsPoint QgsMessageLog.logMessage("In findNearestPointL. Checking lineLayer: " + lineLayer.name() + "; " + searchPt.asWkt(), tag="TOMs panel") searchRect = QgsRectangle(searchPt.x() - tolerance, searchPt.y() - tolerance, searchPt.x() + tolerance, searchPt.y() + tolerance) request = QgsFeatureRequest() request.setFilterRect(searchRect) request.setFlags(QgsFeatureRequest.ExactIntersect) shortestDistance = float("inf") #nearestPoint = QgsFeature() # Loop through all features in the layer to find the closest feature for f in lineLayer.getFeatures(request): # Add any features that are found should be added to a list closestPtOnFeature = f.geometry().nearestPoint( QgsGeometry.fromPointXY(searchPt)) dist = f.geometry().distance(QgsGeometry.fromPointXY(searchPt)) if dist < shortestDistance: shortestDistance = dist closestPoint = closestPtOnFeature QgsMessageLog.logMessage("In findNearestPointL: shortestDistance: " + str(shortestDistance), tag="TOMs panel") del request del searchRect if shortestDistance < float("inf"): #nearestPoint = QgsFeature() # add the geometry to the feature, #nearestPoint.setGeometry(QgsGeometry(closestPtOnFeature)) #QgsMessageLog.logMessage("findNearestPointL: nearestPoint geom type: " + str(nearestPoint.wkbType()), tag="TOMs panel") return closestPoint # returns a geometry else: return None
def assign_district(self, target_ids, new_district): """ Queue up changes :param target_ids: :param new_district: :return: """ staged_log_entries = [] # first, record the previous districts, before they get changed by the super method request = QgsFeatureRequest().setFilterFids(target_ids) request.setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes( [self.target_layer.fields().lookupField(self.target_field), self.meshblock_number_idx]) for f in self.target_layer.getFeatures(request): district = f[self.target_field] if district == NULL: continue if district == new_district: target_ids = [t for t in target_ids if t != f.id()] continue meshblock_number = f[self.meshblock_number_idx] if district not in self.pending_affected_districts: self.pending_affected_districts[district] = {'ADD': [], 'REMOVE': []} self.pending_affected_districts[district]['REMOVE'].append(f.id()) staged_log_entries.append(self.create_log_entry(meshblock_number=meshblock_number, old_district=district, new_district=new_district)) if not super().assign_district(target_ids, new_district): return False self.pending_log_entries.extend(staged_log_entries) # if assign was successful, then record all districts affected by this operation # (that includes the new district and all old districts) if new_district not in self.pending_affected_districts: self.pending_affected_districts[new_district] = {'ADD': [], 'REMOVE': []} self.pending_affected_districts[new_district]['ADD'].extend(target_ids) return True
def findNearestPointOnLink(self, pos): # def findFeatureAt(self, pos, excludeFeature=None): # http://www.lutraconsulting.co.uk/blog/2014/10/17/getting-started-writing-qgis-python-plugins/ - generates "closest feature" function """ Find the feature close to the given position. 'pos' is the position to check, in canvas coordinates. if 'excludeFeature' is specified, we ignore this feature when finding the clicked-on feature. If no feature is close to the given coordinate, we return None. """ mapPt = self.transformCoordinates(pos) tolerance = 1.0 searchRect = QgsRectangle(mapPt.x() - tolerance, mapPt.y() - tolerance, mapPt.x() + tolerance, mapPt.y() + tolerance) request = QgsFeatureRequest() request.setFilterRect(searchRect) request.setFlags(QgsFeatureRequest.ExactIntersect) shortestDistance = float("inf") closestFeature = None # Loop through all features in the layer to find the closest feature for f in self.linkLayer.getFeatures(request): dist = f.geometry().distance(QgsGeometry.fromPointXY(mapPt)) if dist < shortestDistance: shortestDistance = dist closestFeature = f if closestFeature: # Find distance from start of feature distanceAlongLink = closestFeature.geometry().lineLocatePoint( QgsGeometry.fromPointXY(mapPt)) nearestPt = closestFeature.geometry().nearestPoint( QgsGeometry.fromPointXY(mapPt)) return nearestPt, closestFeature, distanceAlongLink return mapPt, None, None
def findPointFeatures(self, aNodeLayer, topoPoints): ''' identifies a list of point features on aNodeLayer by a list of ids ''' # save start time start = time.time() qFeaturesList = {} qNodeFeaturesList = [] for aTopoPoint in topoPoints: pointLayerName = aTopoPoint.getTableName() pointLayer = None start_layer = time.time() for aLayer in QgsMapLayerRegistry.instance().mapLayers().values(): if aLayer.shortName() == pointLayerName: pointLayer = aLayer if pointLayerName not in qFeaturesList: qFeaturesList[pointLayerName] = [] break # DEBUG: #self.showTimeMessage('findPointFeatures - Point Layer', time.time() - start_layer) if pointLayer: start_point = time.time() primaryKey = self.topologyConnector.primaryKey(aTopoPoint.getFullTableName()) request = QgsFeatureRequest().setFilterExpression(primaryKey + ' = ' + str(aTopoPoint.getSystemId())) #u'"system_id" = ' request.setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes(['id'], pointLayer.fields()) for aQsFeature in pointLayer.getFeatures( request ): if aQsFeature.id() not in qFeaturesList[pointLayerName]: qFeaturesList[pointLayerName].append(aQsFeature.id()) # DEBUG #self.showTimeMessage('findPointFeatures - Point Feature', time.time() - start_point) if aNodeLayer: start_node = time.time() request = QgsFeatureRequest().setFilterExpression(u'"node_id" = ' + str(aTopoPoint.getNodeId())) request.setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes(['id'], aNodeLayer.fields()) for aQsNodeFeature in aNodeLayer.getFeatures( request ): qNodeFeaturesList.append(aQsNodeFeature.id()) #DEBUG #self.showTimeMessage('findPointFeatures - Node Feature', time.time() - start_node) self.showTimeMessage('findPointFeatures', time.time() - start) return {'pointFeaturesList': qFeaturesList, 'nodeFeaturesList': qNodeFeaturesList}
def district_list(self): """ Returns a complete list of districts available for redistricting to """ field_index = self.source_layer.fields().lookupField(self.source_field) request = QgsFeatureRequest() self.modify_district_request(request) request.setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes([field_index]) districts = [f[field_index] for f in self.source_layer.getFeatures(request) if f[field_index] != NULL] # we want an ordered list of unique values! d = OrderedDict() for x in districts: d[x] = True return list(d.keys())
def getFeatIdList(self, currentLayer): #getting all features ids if self.mFieldExpressionWidget.currentText() == '': featIdList = currentLayer.allFeatureIds() elif not self.mFieldExpressionWidget.isValidExpression(): self.iface.messageBar().pushMessage( self.tr('Warning!'), self.tr('Invalid attribute filter!'), level=QgsMessageBar.WARNING, duration=2) return [] else: request = QgsFeatureRequest().setFilterExpression( self.mFieldExpressionWidget.asExpression()) request.setFlags(QgsFeatureRequest.NoGeometry) featIdList = [i.id() for i in currentLayer.getFeatures(request)] #sort is faster than sorted (but sort is just available for lists) featIdList.sort() return featIdList
def is25d(layer, canvas, restrictToExtent, extent): if layer.geometryType() != QGis.Polygon: return False try: renderer = layer.rendererV2() if isinstance(renderer, Qgs25DRenderer): return True symbols = [] if isinstance(renderer, QgsCategorizedSymbolRendererV2): categories = renderer.categories() for category in categories: symbols.append(category.symbol()) elif isinstance(renderer, QgsGraduatedSymbolRendererV2): ranges = renderer.ranges() for range in ranges: symbols.append(range.symbol()) else: renderContext = QgsRenderContext.fromMapSettings( canvas.mapSettings()) fields = layer.pendingFields() if restrictToExtent and extent == "Canvas extent": request = QgsFeatureRequest(iface.mapCanvas().extent()) request.setFlags(QgsFeatureRequest.ExactIntersect) features = layer.getFeatures(request) else: features = layer.getFeatures() renderer.startRender(renderContext, fields) for feature in features: symbol = renderer.symbolForFeature2(feature, renderContext) symbols.append(symbol) renderer.stopRender(renderContext) for sym in symbols: sl1 = sym.symbolLayer(1) sl2 = sym.symbolLayer(2) if (isinstance(sl1, QgsGeometryGeneratorSymbolLayerV2) and isinstance(sl2, QgsGeometryGeneratorSymbolLayerV2)): return True return False except: QgsMessageLog.logMessage(traceback.format_exc(), "qgis2web", level=QgsMessageLog.CRITICAL) return False
def update_stats_nz_values(self, electorate_id, results: dict): """ Updates an electorate's stored Statistics NZ api calculations :param electorate_id: electorate to update :param results: results dictionary from stats API """ # get feature ID request = QgsFeatureRequest() request.setFilterExpression(QgsExpression.createFieldEqualityExpression(self.source_field, electorate_id)) request.setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes([]) request.setLimit(1) f = next(self.source_layer.getFeatures(request)) self.source_layer.dataProvider().changeAttributeValues( {f.id(): {self.stats_nz_pop_field_index: results['currentPopulation'], self.stats_nz_var_20_field_index: results['varianceYear1'], self.stats_nz_var_23_field_index: results['varianceYear2']}})
def get_features_by_expression(layer, expression=None, with_attributes=False, with_geometry=False): # TODO: It should be possible to pass a list of attributes to retrieve if expression is None: request = QgsFeatureRequest() else: request = QgsFeatureRequest(expression) if not with_geometry: request.setFlags(QgsFeatureRequest.NoGeometry) if not with_attributes: field_idx = layer.fields().indexFromName(ID_FIELD) request.setSubsetOfAttributes([field_idx ]) # Note: this adds a new flag return [feature for feature in layer.getFeatures(request)]
def findFeaturesAt(mapPoint, layerConfig, mapTool, closest=False): """ To find features from a given position in a given layer :param mapPoint: the map position :param layerConfig: the layer in which we are looking for features :param mapTool: a QgsMapTool instance :param closest: if we only want the one closest feature :return: features found in layer """ if layerConfig is None: return None tolerance = layerConfig.tolerance if layerConfig.unit == QgsTolerance.Pixels: layTolerance = Finder.calcCanvasTolerance(mapTool.toCanvasCoordinates(mapPoint), layerConfig.layer, mapTool, tolerance) elif layerConfig.unit == QgsTolerance.ProjectUnits: layTolerance = Finder.calcMapTolerance(mapPoint, layerConfig.layer, mapTool, tolerance) else: layTolerance = tolerance layPoint = mapTool.toLayerCoordinates(layerConfig.layer, mapPoint) searchRect = QgsRectangle(layPoint.x() - layTolerance, layPoint.y() - layTolerance, layPoint.x() + layTolerance, layPoint.y() + layTolerance) request = QgsFeatureRequest() request.setFilterRect(searchRect) request.setFlags(QgsFeatureRequest.ExactIntersect) if closest: for feature in layerConfig.layer.getFeatures(request): if layerConfig.layer.geometryType() == QGis.Polygon: dist, nearest, vertex = feature.geometry().closestSegmentWithContext(mapPoint) if not QgsGeometry.fromPoint(nearest).intersects(searchRect): return None return QgsFeature(feature) else: features = [] for feature in layerConfig.layer.getFeatures(request): if layerConfig.layer.geometryType() == QGis.Polygon: dist, nearest, vertex = feature.geometry().closestSegmentWithContext(mapPoint) if QgsGeometry.fromPoint(nearest).intersects(searchRect): features.append(QgsFeature(feature)) else: features.append(QgsFeature(feature)) return features
def fill_table(self): self.tbl_changes_parcels.clearContents() self.tbl_changes_parcels.setRowCount(len(self.parcels_t_ids)) request = QgsFeatureRequest(QgsExpression('"{}" in ({})'.format(self.utils._db.names.T_ID_F, ",".join(str(t_id) for t_id in self.parcels_t_ids)))) request.setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes([self.utils._db.names.T_ID_F, self.utils._db.names.LC_PARCEL_T_NUPRE_F, self.utils._db.names.LC_PARCEL_T_FMI_F, self.utils._db.names.LC_PARCEL_T_PARCEL_NUMBER_F], self.utils._layers[self.utils._db.names.LC_PARCEL_T].fields()) # NOTE: this adds a new flag parcels = self.utils._layers[self.utils._db.names.LC_PARCEL_T].getFeatures(request) if parcels: row = 0 for parcel in parcels: self.tbl_changes_parcels.setItem(row, 0, QTableWidgetItem(str(parcel[self.utils._db.names.T_ID_F]))) self.tbl_changes_parcels.setItem(row, 1, QTableWidgetItem(str(parcel[self.utils._db.names.LC_PARCEL_T_NUPRE_F]))) self.tbl_changes_parcels.setItem(row, 2, QTableWidgetItem(str(parcel[self.utils._db.names.LC_PARCEL_T_FMI_F]))) self.tbl_changes_parcels.setItem(row, 3, QTableWidgetItem(str(parcel[self.utils._db.names.LC_PARCEL_T_PARCEL_NUMBER_F]))) row += 1
def findNearestLine(self, searchPt, lineLayer, tolerance): # given a point, find the nearest point (within the tolerance) within the line layer # returns QgsPoint QgsMessageLog.logMessage("In findNearestLine - lineLayer", tag="TOMs panel") searchRect = QgsRectangle(searchPt.x() - tolerance, searchPt.y() - tolerance, searchPt.x() + tolerance, searchPt.y() + tolerance) request = QgsFeatureRequest() request.setFilterRect(searchRect) request.setFlags(QgsFeatureRequest.ExactIntersect) shortestDistance = float("inf") # Loop through all features in the layer to find the closest feature for f in lineLayer.getFeatures(request): # Add any features that are found should be added to a list # closestPtOnFeature = f.geometry().nearestPoint(QgsGeometry.fromPoint(searchPt)) dist = f.geometry().distance(QgsGeometry.fromPointXY(searchPt)) if dist < shortestDistance: shortestDistance = dist closestLine = f QgsMessageLog.logMessage("In findNearestLine: shortestDistance: " + str(shortestDistance), tag="TOMs panel") del request del searchRect if shortestDistance < float("inf"): """QgsMessageLog.logMessage("In findNearestLine: closestLine {}".format(closestLine.exportToWkt()), tag="TOMs panel")""" return closestLine # returns a geometry else: return None
def __select(self): """ To select objects in multiples layers inside a selection rectangle """ searchRect = QgsRectangle(self.first, self.last) for layer in self.canvas().layers(): if not self.identified or layer.id() not in self.disabled(): if layer.type( ) == QgsMapLayer.VectorLayer and layer.geometryType( ) in self.types: renderer = layer.rendererV2() context = QgsRenderContext() if renderer: renderer.startRender(context, layer.pendingFields()) request = QgsFeatureRequest() request.setFilterRect(searchRect) request.setFlags(QgsFeatureRequest.ExactIntersect) fIds = [] for feature in layer.getFeatures(request): try: will = renderer.willRenderFeature( feature, context) except: try: will = renderer.willRenderFeature(feature) except: self.__iface.messageBar().pushMessage( QCoreApplication.translate( "VDLTools", "Error"), "will renderer still not working", level=QgsMessageBar.CRITICAL, duration=0) return if will: fIds.append(feature.id()) renderer.stopRender(context) layer.selectByIds(fIds) self.selectedSignal.emit()
def redraw(self, handler): """ Forces a redraw of the cached image """ self.image = None if not self.original_populations: # first run, get initial estimates request = QgsFeatureRequest() request.setFilterExpression(QgsExpression.createFieldEqualityExpression('type', self.task)) request.setFlags(QgsFeatureRequest.NoGeometry) for f in self.electorate_layer.getFeatures(request): estimated_pop = f.attribute(handler.stats_nz_pop_field_index) if estimated_pop is None or estimated_pop == NULL: # otherwise just use existing estimated pop as starting point estimated_pop = f.attribute(handler.estimated_pop_idx) self.original_populations[f.id()] = estimated_pop # step 1: get all electorate features corresponding to affected electorates electorate_features = {f[handler.electorate_layer_field]: f for f in handler.get_affected_districts( [handler.electorate_layer_field, handler.stats_nz_pop_field, 'estimated_pop'], needs_geometry=False)} self.new_populations = {} for district in handler.pending_affected_districts.keys(): # pylint: disable=consider-iterating-dictionary # use stats nz pop as initial estimate, if available estimated_pop = electorate_features[district].attribute(handler.stats_nz_pop_field_index) if estimated_pop is None or estimated_pop == NULL: # otherwise just use existing estimated pop as starting point estimated_pop = electorate_features[district].attribute(handler.estimated_pop_idx) # add new bits estimated_pop = handler.grow_population_with_added_meshblocks(district, estimated_pop) # minus lost bits estimated_pop = handler.shrink_population_by_removed_meshblocks(district, estimated_pop) self.new_populations[electorate_features[district].id()] = estimated_pop
def _filter_plots_ids_from_expresion(db, parcel_layer, ue_baunit_layer, expression_select_parcels): """ Get plots ids depending of a defined filter expression """ # Only required field in parcel layer fields_idx = list() for field in [ db.names.T_ID_F, db.names.LC_PARCEL_T_FMI_F, db.names.LC_PARCEL_T_PARCEL_NUMBER_F, db.names.LC_PARCEL_T_PREVIOUS_PARCEL_NUMBER_F ]: fields_idx.append(parcel_layer.fields().indexFromName(field)) request = QgsFeatureRequest(expression_select_parcels) request.setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes(fields_idx) # Note: this adds a new flag parcels = parcel_layer.getFeatures(request) parcel_ids = [ select_parcel[db.names.T_ID_F] for select_parcel in parcels ] # Only required field in ue baunit layer field_idx = parcel_layer.fields().indexFromName( db.names.COL_UE_BAUNIT_T_LC_PLOT_F) expression_select_plots = QgsExpression( '{} in ({}) AND {} IS NOT NULL'.format( db.names.COL_UE_BAUNIT_T_PARCEL_F, ','.join([str(parcel_id) for parcel_id in parcel_ids]), db.names.COL_UE_BAUNIT_T_LC_PLOT_F)) request = QgsFeatureRequest(expression_select_plots) request.setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes([field_idx ]) # Note: this adds a new flag plots = ue_baunit_layer.getFeatures(request) return [plot[db.names.COL_UE_BAUNIT_T_LC_PLOT_F] for plot in plots] # Plot ids
def read(self, feature_type, bbox = None, attributes = None, geometry=True, feature_filter=None): if not isinstance(feature_type, FeatureType): raise TypeError() lyr = self._connectlayer(feature_type) request = None if bbox or attributes is not None or not geometry or feature_filter: request = QgsFeatureRequest() if bbox: rect = QgsRectangle(*bbox) request.setFilterRect(rect) if attributes: request.setSubsetOfAttributes(attributes, lyr.pendingFields()) if not geometry: request.setFlags(QgsFeatureRequest.NoGeometry) if feature_filter: request.setFilterExpression(feature_filter) #lyr.setSubsetString(feature_filter) # return listoffeatures # filter is maybe a QgsFeatureRequest # http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/vector.html#iterating-over-a-subset-of-features return list(lyr.getFeatures(request) if request else lyr.getFeatures())
def _add_default_exposure_class(layer): """The layer doesn't have an exposure class, we need to add it. :param layer: The vector layer. :type layer: QgsVectorLayer """ layer.startEditing() field = create_field_from_definition(exposure_class_field) layer.keywords['inasafe_fields'][exposure_class_field['key']] = ( exposure_class_field['field_name']) layer.addAttribute(field) index = layer.fieldNameIndex(exposure_class_field['field_name']) exposure = layer.keywords['exposure'] request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) for feature in layer.getFeatures(request): layer.changeAttributeValue(feature.id(), index, exposure) layer.commitChanges() return
def zonal_stats(raster, vector): """Reclassify a continuous raster layer. Issue https://github.com/inasafe/inasafe/issues/3190 The algorithm will take care about projections. We don't want to reproject the raster layer. So if CRS are different, we reproject the vector layer and then we do a lookup from the reprojected layer to the original vector layer. :param raster: The raster layer. :type raster: QgsRasterLayer :param vector: The vector layer. :type vector: QgsVectorLayer :return: The output of the zonal stats. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = zonal_stats_steps['output_layer_name'] exposure = raster.keywords['exposure'] if raster.crs().authid() != vector.crs().authid(): layer = reproject(vector, raster.crs()) # We prepare the copy output_layer = create_memory_layer( output_layer_name, vector.geometryType(), vector.crs(), vector.fields() ) copy_layer(vector, output_layer) else: layer = create_memory_layer( output_layer_name, vector.geometryType(), vector.crs(), vector.fields() ) copy_layer(vector, layer) input_band = layer.keywords.get('active_band', 1) analysis = QgsZonalStatistics( layer, raster, 'exposure_', input_band, QgsZonalStatistics.Sum) result = analysis.calculateStatistics(None) LOGGER.debug(tr('Zonal stats on %s : %s' % (raster.source(), result))) output_field = exposure_count_field['field_name'] % exposure if raster.crs().authid() != vector.crs().authid(): output_layer.startEditing() field = create_field_from_definition( exposure_count_field, exposure) output_layer.addAttribute(field) new_index = output_layer.fields().lookupField(field.name()) old_index = layer.fields().lookupField('exposure_sum') for feature_input, feature_output in zip( layer.getFeatures(), output_layer.getFeatures()): output_layer.changeAttributeValue( feature_input.id(), new_index, feature_input[old_index]) output_layer.commitChanges() layer = output_layer else: fields_to_rename = { 'exposure_sum': output_field } if qgis_version() >= 21600: rename_fields(layer, fields_to_rename) else: copy_fields(layer, fields_to_rename) remove_fields(layer, list(fields_to_rename.keys())) layer.commitChanges() # The zonal stats is producing some None values. We need to fill these # with 0. See issue : #3778 # We should start a new editing session as previous fields need to be # committed first. layer.startEditing() request = QgsFeatureRequest() expression = '\"%s\" is None' % output_field request.setFilterExpression(expression) request.setFlags(QgsFeatureRequest.NoGeometry) index = layer.fields().lookupField(output_field) for feature in layer.getFeatures(): if feature[output_field] is None: layer.changeAttributeValue(feature.id(), index, 0) layer.commitChanges() layer.keywords = raster.keywords.copy() layer.keywords['inasafe_fields'] = vector.keywords['inasafe_fields'].copy() layer.keywords['inasafe_default_values'] = ( raster.keywords['inasafe_default_values'].copy()) key = exposure_count_field['key'] % raster.keywords['exposure'] # Special case here, one field is the exposure count and the total. layer.keywords['inasafe_fields'][key] = output_field layer.keywords['inasafe_fields'][total_field['key']] = output_field layer.keywords['exposure_keywords'] = raster.keywords.copy() layer.keywords['hazard_keywords'] = vector.keywords[ 'hazard_keywords'].copy() layer.keywords['aggregation_keywords'] = ( vector.keywords['aggregation_keywords']) layer.keywords['layer_purpose'] = ( layer_purpose_aggregate_hazard_impacted['key']) layer.keywords['title'] = output_layer_name check_layer(layer) return layer
def run(self): """Run any post processors requested by the impact function. """ try: requested_postprocessors = self.function_parameters[ 'postprocessors'] postprocessors = get_postprocessors( requested_postprocessors, self.aggregator.aoi_mode) except (TypeError, KeyError): # TypeError is for when function_parameters is none # KeyError is for when ['postprocessors'] is unavailable postprocessors = {} LOGGER.debug('Running this postprocessors: ' + str(postprocessors)) feature_names_attribute = self.aggregator.attributes[ self.aggregator.get_default_keyword('AGGR_ATTR_KEY')] if feature_names_attribute is None: self.attribute_title = self.tr('Aggregation unit') else: self.attribute_title = feature_names_attribute name_filed_index = self.aggregator.layer.fieldNameIndex( self.attribute_title) sum_field_index = self.aggregator.layer.fieldNameIndex( self._sum_field_name()) user_defined_female_ratio = False female_ratio_field_index = None female_ratio = None user_defined_age_ratios = False youth_ratio_field_index = None youth_ratio = None adult_ratio_field_index = None adult_ratio = None elderly_ratio_field_index = None elderly_ratio = None if 'Gender' in postprocessors: # look if we need to look for a variable female ratio in a layer try: female_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'FEMALE_RATIO_ATTR_KEY')] female_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(female_ratio_field) # something went wrong finding the female ratio field, # use defaults from below except block if female_ratio_field_index == -1: raise KeyError user_defined_female_ratio = True except KeyError: try: female_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'FEMALE_RATIO_KEY')) except KeywordNotFoundError: female_ratio = \ self.aggregator.get_default_keyword('FEMALE_RATIO') if 'Age' in postprocessors: # look if we need to look for a variable age ratio in a layer try: youth_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'YOUTH_RATIO_ATTR_KEY')] youth_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(youth_ratio_field) adult_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'ADULT_RATIO_ATTR_KEY')] adult_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(adult_ratio_field) elderly_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'ELDERLY_RATIO_ATTR_KEY')] elderly_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(elderly_ratio_field) # something went wrong finding the youth ratio field, # use defaults from below except block if (youth_ratio_field_index == -1 or adult_ratio_field_index == -1 or elderly_ratio_field_index == -1): raise KeyError user_defined_age_ratios = True except KeyError: try: youth_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'YOUTH_RATIO_KEY')) adult_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'ADULT_RATIO_KEY')) elderly_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'ELDERLY_RATIO_KEY')) except KeywordNotFoundError: youth_ratio = \ self.aggregator.get_default_keyword('YOUTH_RATIO') adult_ratio = \ self.aggregator.get_default_keyword('ADULT_RATIO') elderly_ratio = \ self.aggregator.get_default_keyword('ELDERLY_RATIO') if 'BuildingType' or 'RoadType' in postprocessors: try: key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, 'key_attribute') except KeywordNotFoundError: # use 'type' as default key_attribute = 'type' # iterate zone features request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) provider = self.aggregator.layer.dataProvider() # start data retrieval: fetch no geometry and all attributes for each # feature polygon_index = 0 for feature in provider.getFeatures(request): # if a feature has no field called if name_filed_index == -1: zone_name = str(feature.id()) else: zone_name = feature[name_filed_index] # create dictionary of attributes to pass to postprocessor general_params = { 'target_field': self.aggregator.target_field, 'function_params': self.function_parameters} if self.aggregator.statistics_type == 'class_count': general_params['impact_classes'] = ( self.aggregator.statistics_classes) elif self.aggregator.statistics_type == 'sum': impact_total = feature[sum_field_index] general_params['impact_total'] = impact_total try: general_params['impact_attrs'] = ( self.aggregator.impact_layer_attributes[polygon_index]) except IndexError: # rasters and attributeless vectors have no attributes general_params['impact_attrs'] = None for key, value in postprocessors.iteritems(): parameters = general_params try: # look if params are available for this postprocessor parameters.update( self.function_parameters[ 'postprocessors'][key]['params']) except KeyError: pass if key == 'Gender': if user_defined_female_ratio: female_ratio = feature[female_ratio_field_index] if female_ratio is None: female_ratio = self.aggregator.defaults[ 'FEMALE_RATIO'] LOGGER.warning('Data Driven Female ratio ' 'incomplete, using defaults for' ' aggregation unit' ' %s' % feature.id) parameters['female_ratio'] = female_ratio if key == 'Age': if user_defined_age_ratios: youth_ratio = feature[youth_ratio_field_index] adult_ratio = feature[adult_ratio_field_index] elderly_ratio = feature[elderly_ratio_field_index] if (youth_ratio is None or adult_ratio is None or elderly_ratio is None): youth_ratio = self.aggregator.defaults[ 'YOUTH_RATIO'] adult_ratio = self.aggregator.defaults[ 'ADULT_RATIO'] elderly_ratio = self.aggregator.defaults[ 'ELDERLY_RATIO'] LOGGER.warning('Data Driven Age ratios ' 'incomplete, using defaults for' ' aggregation unit' ' %s' % feature.id) parameters['youth_ratio'] = youth_ratio parameters['adult_ratio'] = adult_ratio parameters['elderly_ratio'] = elderly_ratio if key == 'BuildingType' or key == 'RoadType': # TODO: Fix this might be referenced before assignment parameters['key_attribute'] = key_attribute try: value.setup(parameters) value.process() results = value.results() value.clear() # LOGGER.debug(results) # this can raise a KeyError self.output[key].append( (zone_name, results)) except PostProcessorError as e: message = m.Message( m.Heading(self.tr('%s postprocessor problem' % key), **styles.DETAILS_STYLE), m.Paragraph(self.tr(str(e)))) self.error_message = message except KeyError: self.output[key] = [] # TODO: Fix this might be referenced before assignment self.output[key].append((zone_name, results)) # increment the index polygon_index += 1
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) request.setDestinationCrs(network.sourceCrs()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount( ) else 0 points = [] for current, f in enumerate(features): if feedback.isCanceled(): break points.append(f.geometry().asPoint()) feedback.setProgress(int(current * total)) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(self.tr('Calculating service areas...')) graph = builder.graph() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) vertices = [] upperBoundary = [] lowerBoundary = [] total = 100.0 / len(snappedPoints) if snappedPoints else 1 for i, p in enumerate(snappedPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) for j, v in enumerate(cost): if v > travelCost and tree[j] != -1: vertexId = graph.edge(tree[j]).outVertex() if cost[vertexId] <= travelCost: vertices.append(j) for j in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[j]).inVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[j]).outVertex()).point()) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = origPoint sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = origPoint sink.addFeature(feat, QgsFeatureSink.FastInsert) vertices[:] = [] upperBoundary[:] = [] lowerBoundary[:] = [] feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def analysis_summary(aggregate_hazard, analysis, callback=None): """Compute the summary from the aggregate hazard to analysis. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | analysis_id | Output layer : | analysis_id | count_hazard_class | affected_count | total | :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param analysis: The target vector layer where to write statistics. :type analysis: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new target layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_3_analysis_steps['output_layer_name'] processing_step = summary_3_analysis_steps['step_name'] source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = analysis.keywords['inasafe_fields'] target_compulsory_fields = [ analysis_id_field, analysis_name_field, ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, total_field ] check_inputs(source_compulsory_fields, source_fields) absolute_values = create_absolute_values_structure( aggregate_hazard, ['all']) hazard_class = source_fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] classification = hazard_keywords['classification'] total = source_fields[total_field['key']] flat_table = FlatTable('hazard_class') # First loop over the aggregate_hazard layer request = QgsFeatureRequest() request.setSubsetOfAttributes( [hazard_class, total], aggregate_hazard.fields()) request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregate_hazard.getFeatures(): hazard_value = area[hazard_class_index] value = area[total] if not value or isinstance(value, QPyNullVariant) or isnan(value): # For isnan, see ticket #3812 value = 0 if not hazard_value or isinstance(hazard_value, QPyNullVariant): hazard_value = 'NULL' flat_table.add_value( value, hazard_class=hazard_value ) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = area[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value( value, all='all' ) analysis.startEditing() shift = analysis.fields().count() counts = [ total_affected_field, total_not_affected_field, total_not_exposed_field, total_field] add_fields( analysis, absolute_values, counts, unique_hazard, hazard_count_field) affected_sum = 0 not_affected_sum = 0 not_exposed_sum = 0 for area in analysis.getFeatures(request): total = 0 for i, val in enumerate(unique_hazard): if not val or isinstance(val, QPyNullVariant): val = 'NULL' sum = flat_table.get_value(hazard_class=val) total += sum analysis.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( classification=classification, hazard_class=val) if affected == not_exposed_class['key']: not_exposed_sum += sum elif affected: affected_sum += sum else: not_affected_sum += sum # Affected field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard), affected_sum) # Not affected field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 1, not_affected_sum) # Not exposed field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 2, not_exposed_sum) # Total field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 3, total) # Any absolute postprocessors for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value( all='all' ) analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 4 + i, value) # Sanity check ± 1 to the result. Disabled for now as it seems ± 1 is not # enough. ET 13/02/17 # total_computed = ( # affected_sum + not_affected_sum + not_exposed_sum) # if not -1 < (total_computed - total) < 1: # raise ComputationError analysis.commitChanges() analysis.keywords['title'] = layer_purpose_analysis_impacted['name'] if qgis_version() >= 21600: analysis.setName(analysis.keywords['title']) else: analysis.setLayerName(analysis.keywords['title']) analysis.keywords['layer_purpose'] = layer_purpose_analysis_impacted['key'] check_layer(analysis) return analysis
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs()) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) request.setDestinationCrs(network.sourceCrs()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0 points = [endPoint] for current, f in enumerate(features): if feedback.isCanceled(): break points.append(f.geometry().asPoint()) feedback.setProgress(int(current * total)) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(self.tr('Calculating shortest paths...')) graph = builder.graph() idxEnd = graph.findVertex(snappedPoints[0]) route = [] nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, nPoints + 1): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: msg = self.tr('There is no route from start point ({}) to end point ({}).'.format(points[i].toString(), endPoint.toString())) feedback.setProgressText(msg) QgsMessageLog.logMessage(msg, self.tr('Processing'), QgsMessageLog.WARNING) continue cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append(graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[i]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = points[i].toString() feat['end'] = endPoint.toString() feat['cost'] = cost / multiplier sink.addFeature(feat, QgsFeatureSink.FastInsert) route[:] = [] feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :type context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :type feedback: QgsProcessingFeedback, optional :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback :type raise_exceptions: boo, default to False :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) # Only feature based algs have sourceFlags try: if alg.sourceFlags() & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks: context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) except AttributeError: pass active_layer = parameters['INPUT'] # Run some checks and prepare the layer for in-place execution by: # - getting the active layer and checking that it is a vector # - making the layer editable if it was not already # - selecting all features if none was selected # - checking in-place support for the active layer/alg/parameters # If one of the check fails and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: if active_layer is None: raise QgsProcessingException(tr("There is not active layer.")) if not isinstance(active_layer, QgsVectorLayer): raise QgsProcessingException(tr("Active layer is not a vector layer.")) if not active_layer.isEditable(): if not active_layer.startEditing(): raise QgsProcessingException(tr("Active layer is not editable (and editing could not be turned on).")) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) except QgsProcessingException as e: if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {} if not active_layer.selectedFeatureIds(): active_layer.selectAll() # Make sure we are working on selected features only parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(active_layer.id(), True) parameters['OUTPUT'] = 'memory:' req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Start the execution # If anything goes wrong and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException(tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) field_idxs = range(len(active_layer.fields())) iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds()) iterator_req.setInvalidGeometryCheck(context.invalidGeometryCheck()) feature_iterator = active_layer.getFeatures(iterator_req) step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1 for current, f in enumerate(feature_iterator): feedback.setProgress(current * step) if feedback.isCanceled(): break # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle # There is no way to know if some features have been skipped # due to invalid geometries if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid: selected_ids = active_layer.selectedFeatureIds() else: selected_ids = [] results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle new_features = [] # Check if there are any skipped features if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid: missing_ids = list(set(selected_ids) - set(result_layer.allFeatureIds())) if missing_ids: for f in active_layer.getFeatures(QgsFeatureRequest(missing_ids)): if not f.geometry().isGeosValid(): new_features.append(f) active_layer.deleteFeatures(active_layer.selectedFeatureIds()) for f in result_layer.getFeatures(): new_features.extend(QgsVectorLayerUtils. makeFeaturesCompatible([f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {}
def test_sql2(self): l2 = QgsVectorLayer(os.path.join(self.testDataDir, "france_parts.shp"), "france_parts", "ogr", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l2.isValid(), True) QgsProject.instance().addMapLayer(l2) query = toPercent("SELECT * FROM france_parts") l4 = QgsVectorLayer("?query=%s&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().wkbType(), 6) self.assertEqual(l4.dataProvider().crs().postgisSrid(), 4326) n = 0 r = QgsFeatureRequest(QgsRectangle(-1.677, 49.624, -0.816, 49.086)) for f in l4.getFeatures(r): self.assertEqual(f.geometry() is not None, True) self.assertEqual(f.attributes()[0], 2661) n += 1 self.assertEqual(n, 1) # use uid query = toPercent("SELECT * FROM france_parts") l5 = QgsVectorLayer( "?query=%s&geometry=geometry:polygon:4326&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l5.isValid(), True) idSum = sum(f.id() for f in l5.getFeatures()) self.assertEqual(idSum, 10659) r = QgsFeatureRequest(2661) idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) r = QgsFeatureRequest() r.setFilterFids([2661, 2664]) self.assertEqual(sum(f.id() for f in l5.getFeatures(r)), 2661 + 2664) # test attribute subset r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.SubsetOfAttributes) r.setSubsetOfAttributes([1]) s = [(f.id(), f.attributes()[1]) for f in l5.getFeatures(r)] self.assertEqual(sum([x[0] for x in s]), 10659) self.assertEqual(sum([x[1] for x in s]), 3064.0) # test NoGeometry # by request flag r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.NoGeometry) self.assertEqual(all([not f.hasGeometry() for f in l5.getFeatures(r)]), True) # test subset self.assertEqual(l5.dataProvider().featureCount(), 4) l5.setSubsetString("ObjectId = 2661") idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) self.assertEqual(l5.dataProvider().featureCount(), 1)
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) startPoint = self.getParameterValue(self.START_POINT) endPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.END_POINTS)) strategy = self.getParameterValue(self.STRATEGY) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter( fields.toList(), QgsWkbTypes.LineString, layer.crs()) tmp = startPoint.split(',') startPoint = QgsPoint(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(iface.mapCanvas().mapSettings().destinationCrs(), iface.mapCanvas().hasCrsTransformEnabled(), tolerance) progress.setInfo(self.tr('Loading end points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = vector.features(endPoints, request) count = len(features) points = [startPoint] for f in features: points.append(f.geometry().asPoint()) progress.setInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) progress.setInfo(self.tr('Calculating shortest paths...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) route = [] total = 100.0 / count for i in range(1, count + 1): idxEnd = graph.findVertex(snappedPoints[i]) if tree[idxEnd] == -1: msg = self.tr('There is no route from start point ({}) to end point ({}).'.format(startPoint.toString(), points[i].toString())) progress.setText(msg) ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, msg) continue cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append(graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[0]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = startPoint.toString() feat['end'] = points[i].toString() feat['cost'] = cost / multiplier writer.addFeature(feat) route[:] = [] progress.setPercentage(int(i * total)) del writer
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) request.setDestinationCrs(network.sourceCrs()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0 points = [] for current, f in enumerate(features): if feedback.isCanceled(): break points.append(f.geometry().asPoint()) feedback.setProgress(int(current * total)) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(self.tr('Calculating service areas...')) graph = builder.graph() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) vertices = [] upperBoundary = [] lowerBoundary = [] total = 100.0 / len(snappedPoints) if snappedPoints else 1 for i, p in enumerate(snappedPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) for j, v in enumerate(cost): if v > travelCost and tree[j] != -1: vertexId = graph.edge(tree[j]).outVertex() if cost[vertexId] <= travelCost: vertices.append(j) for j in vertices: upperBoundary.append(graph.vertex(graph.edge(tree[j]).inVertex()).point()) lowerBoundary.append(graph.vertex(graph.edge(tree[j]).outVertex()).point()) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = origPoint sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = origPoint sink.addFeature(feat, QgsFeatureSink.FastInsert) vertices[:] = [] upperBoundary[:] = [] lowerBoundary[:] = [] feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :type context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :type feedback: QgsProcessingFeedback, optional :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback :type raise_exceptions: boo, default to False :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) # Only feature based algs have sourceFlags try: if alg.sourceFlags( ) & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks: context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) except AttributeError: pass active_layer = parameters['INPUT'] # Run some checks and prepare the layer for in-place execution by: # - getting the active layer and checking that it is a vector # - making the layer editable if it was not already # - selecting all features if none was selected # - checking in-place support for the active layer/alg/parameters # If one of the check fails and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: if active_layer is None: raise QgsProcessingException(tr("There is not active layer.")) if not isinstance(active_layer, QgsVectorLayer): raise QgsProcessingException( tr("Active layer is not a vector layer.")) if not active_layer.isEditable(): if not active_layer.startEditing(): raise QgsProcessingException( tr("Active layer is not editable (and editing could not be turned on)." )) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) except QgsProcessingException as e: if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {} if not active_layer.selectedFeatureIds(): active_layer.selectAll() # Make sure we are working on selected features only parameters['INPUT'] = QgsProcessingFeatureSourceDefinition( active_layer.id(), True) parameters['OUTPUT'] = 'memory:' req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Start the execution # If anything goes wrong and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException( tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) field_idxs = range(len(active_layer.fields())) iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds()) iterator_req.setInvalidGeometryCheck( context.invalidGeometryCheck()) feature_iterator = active_layer.getFeatures(iterator_req) step = 100 / len(active_layer.selectedFeatureIds() ) if active_layer.selectedFeatureIds() else 1 for current, f in enumerate(feature_iterator): feedback.setProgress(current * step) if feedback.isCanceled(): break # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible( new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues( f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set( [f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set( [f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle # There is no way to know if some features have been skipped # due to invalid geometries if context.invalidGeometryCheck( ) == QgsFeatureRequest.GeometrySkipInvalid: selected_ids = active_layer.selectedFeatureIds() else: selected_ids = [] results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString( results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle new_features = [] # Check if there are any skipped features if context.invalidGeometryCheck( ) == QgsFeatureRequest.GeometrySkipInvalid: missing_ids = list( set(selected_ids) - set(result_layer.allFeatureIds())) if missing_ids: for f in active_layer.getFeatures( QgsFeatureRequest(missing_ids)): if not f.geometry().isGeosValid(): new_features.append(f) active_layer.deleteFeatures(active_layer.selectedFeatureIds()) for f in result_layer.getFeatures(): new_features.extend( QgsVectorLayerUtils.makeFeaturesCompatible( [f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {}
def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. The input layer must be editable or an exception is raised. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param active_layer: the editable layer :type active_layer: QgsVectoLayer :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :param context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :param feedback: QgsProcessingFeedback, optional :raises QgsProcessingException: raised when the layer is not editable or the layer cannot be found in the current project :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) if active_layer is None or not active_layer.isEditable(): raise QgsProcessingException(tr("Layer is not editable or layer is None.")) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) parameters['OUTPUT'] = 'memory:' try: new_feature_ids = [] active_layer.beginEditCommand(alg.name()) req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException(tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications.")) field_idxs = range(len(active_layer.fields())) feature_iterator = active_layer.getFeatures(QgsFeatureRequest(active_layer.selectedFeatureIds())) if parameters['INPUT'].selectedFeaturesOnly else active_layer.getFeatures() for f in feature_iterator: new_features = alg.processFeature(f, context, feedback) new_features = make_features_compatible(new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle active_layer.deleteFeatures(active_layer.selectedFeatureIds()) new_features = [] for f in result_layer.getFeatures(): new_features.extend(make_features_compatible([f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException(tr("Error adding processed features back into the layer.")) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e))) return False, {}
def virtualFields(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 """ layer_name = params.get('LAYER', '') if not layer_name: raise ExpressionServiceError( "Bad request error", "Invalid 'VirtualFields' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = find_vector_layer(layer_name, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'VirtualFields': {} provided".format(layer_name), 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 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 with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't'] if not with_geom: req.setFlags(QgsFeatureRequest.NoGeometry) # Fields pk_attributes = layer.primaryKeyAttributes() attribute_list = [i for i in pk_attributes] fields = layer.fields() r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f] for f in r_fields: attribute_list.append(fields.indexOf(f)) # response response.setStatusCode(200) response.setHeader("Content-Type", "application/json") response.write('{ "type": "FeatureCollection","features":[') response.flush() json_exporter = QgsJsonExporter(layer) if attribute_list: json_exporter.setAttributes(attribute_list) separator = '' for feat in layer.getFeatures(req): fid = layer_name + '.' + get_server_fid(feat, pk_attributes) 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 + json_exporter.exportFeature(feat, extra, fid)) response.flush() separator = ',\n' response.write(']}') return
def get_feature_with_form_scope(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 """ layer_name = params.get('LAYER', '') if not layer_name: raise ExpressionServiceError( "Bad request error", "Invalid 'GetFeatureWithFormScope' REQUEST: LAYER parameter is mandatory", 400) # get layer layer = find_vector_layer(layer_name, project) # layer not found if not layer: raise ExpressionServiceError( "Bad request error", "Invalid LAYER parameter for 'VirtualField': {} provided".format(layer_name), 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 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 with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't'] if not with_geom: req.setFlags(QgsFeatureRequest.NoGeometry) # Fields pk_attributes = layer.primaryKeyAttributes() attribute_list = [i for i in pk_attributes] fields = layer.fields() r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f] for f in r_fields: attribute_list.append(fields.indexOf(f)) # response response.setStatusCode(200) response.setHeader("Content-Type", "application/json") response.write('{ "type": "FeatureCollection","features":[') response.flush() json_exporter = QgsJsonExporter(layer) if attribute_list: json_exporter.setAttributes(attribute_list) separator = '' for feat in layer.getFeatures(req): fid = layer_name + '.' + get_server_fid(feat, pk_attributes) response.write(separator + json_exporter.exportFeature(feat, {}, fid)) response.flush() separator = ',\n' response.write(']}') return
def processAlgorithm(self, feedback): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) startPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.START_POINTS)) strategy = self.getParameterValue(self.STRATEGY) travelCost = self.getParameterValue(self.TRAVEL_COST) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) writerPoints = self.getOutputFromName( self.OUTPUT_POINTS).getVectorWriter(fields, QgsWkbTypes.MultiPoint, layer.crs()) writerPolygons = self.getOutputFromName( self.OUTPUT_POLYGON).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs()) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder( iface.mapCanvas().mapSettings().destinationCrs(), iface.mapCanvas().hasCrsTransformEnabled(), tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = vector.features(startPoints, request) points = [] for f in features: points.append(f.geometry().asPoint()) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) feedback.pushInfo(self.tr('Calculating service areas...')) graph = builder.graph() vertices = [] upperBoundary = [] lowerBoundary = [] total = 100.0 / len(snappedPoints) for i, p in enumerate(snappedPoints): idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) for j, v in enumerate(cost): if v > travelCost and tree[j] != -1: vertexId = graph.edge(tree[j]).outVertex() if cost[vertexId] <= travelCost: vertices.append(j) for j in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[j]).inVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[j]).outVertex()).point()) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = origPoint writerPoints.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = origPoint writerPoints.addFeature(feat) upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = origPoint writerPolygons.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = origPoint writerPolygons.addFeature(feat) vertices[:] = [] upperBoundary[:] = [] lowerBoundary[:] = [] feedback.setProgress(int(i * total)) del writerPoints del writerPolygons
def run(self): """Run any post processors requested by the impact function. """ try: requested_postprocessors = self.functionParams['postprocessors'] postprocessors = get_postprocessors(requested_postprocessors) except (TypeError, KeyError): # TypeError is for when function_parameters is none # KeyError is for when ['postprocessors'] is unavailable postprocessors = {} LOGGER.debug('Running this postprocessors: ' + str(postprocessors)) feature_names_attribute = self.aggregator.attributes[ self.aggregator.defaults['AGGR_ATTR_KEY']] if feature_names_attribute is None: self.attribute_title = self.tr('Aggregation unit') else: self.attribute_title = feature_names_attribute name_filed_index = self.aggregator.layer.fieldNameIndex( self.attribute_title) sum_field_index = self.aggregator.layer.fieldNameIndex( self._sum_field_name()) user_defined_female_ratio = False female_ratio_field_index = None female_ratio = None if 'Gender' in postprocessors: # look if we need to look for a variable female ratio in a layer try: female_ration_field = self.aggregator.attributes[ self.aggregator.defaults['FEM_RATIO_ATTR_KEY']] female_ratio_field_index = self.aggregator.layer.fieldNameIndex( female_ration_field) # something went wrong finding the female ratio field, # use defaults from below except block if female_ratio_field_index == -1: raise KeyError user_defined_female_ratio = True except KeyError: try: female_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.defaults['FEM_RATIO_KEY']) except KeywordNotFoundError: female_ratio = self.aggregator.defaults['FEM_RATIO'] # iterate zone features request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) provider = self.aggregator.layer.dataProvider() # start data retrieval: fetch no geometry and all attributes for each # feature polygon_index = 0 for feature in provider.getFeatures(request): # if a feature has no field called if name_filed_index == -1: zone_name = str(feature.id()) else: zone_name = feature[name_filed_index] # create dictionary of attributes to pass to postprocessor general_params = { 'target_field': self.aggregator.target_field, 'function_params': self.functionParams} if self.aggregator.statistics_type == 'class_count': general_params['impact_classes'] = ( self.aggregator.statistics_classes) elif self.aggregator.statistics_type == 'sum': impact_total = feature[sum_field_index] general_params['impact_total'] = impact_total try: general_params['impact_attrs'] = ( self.aggregator.impact_layer_attributes[polygon_index]) except IndexError: # rasters and attributeless vectors have no attributes general_params['impact_attrs'] = None for key, value in postprocessors.iteritems(): parameters = general_params try: # look if params are available for this postprocessor parameters.update( self.functionParams['postprocessors'][key]['params']) except KeyError: pass if key == 'Gender': if user_defined_female_ratio: female_ratio = feature[female_ratio_field_index] if female_ratio is None: female_ratio = self.aggregator.defaults[ 'FEM_RATIO'] LOGGER.debug(female_ratio) parameters['female_ratio'] = female_ratio value.setup(parameters) value.process() results = value.results() value.clear() # LOGGER.debug(results) try: self.output[key].append( (zone_name, results)) except KeyError: self.output[key] = [] self.output[key].append( (zone_name, results)) # increment the index polygon_index += 1
def _clip_vector_layer( layer, extent, extra_keywords=None, explode_flag=True, hard_clip_flag=False, explode_attribute=None): """Clip a Hazard or Exposure layer to the extents provided. The layer must be a vector layer or an exception will be thrown. The output layer will always be in WGS84/Geographic. :param layer: A valid QGIS vector or raster layer :type layer: :param extent: Either an array representing the exposure layer extents in the form [xmin, ymin, xmax, ymax]. It is assumed that the coordinates are in EPSG:4326 although currently no checks are made to enforce this. or: A QgsGeometry of type polygon. **Polygon clipping is currently only supported for vector datasets.** :type extent: list(float, float, float, float) :param extra_keywords: Optional keywords dictionary to be added to output layer. :type extra_keywords: dict :param explode_flag: A bool specifying whether multipart features should be 'exploded' into singleparts. **This parameter is ignored for raster layer clipping.** :type explode_flag: bool :param hard_clip_flag: A bool specifying whether line and polygon features that extend beyond the extents should be clipped such that they are reduced in size to the part of the geometry that intersects the extent only. Default is False. **This parameter is ignored for raster layer clipping.** :type hard_clip_flag: bool :param explode_attribute: A str specifying to which attribute #1, #2 and so on will be added in case of explode_flag being true. The attribute is modified only if there are at least 2 parts. :type explode_attribute: str :returns: Clipped layer (placed in the system temp dir). The output layer will be reprojected to EPSG:4326 if needed. :rtype: QgsVectorLayer """ if not layer or not extent: myMessage = tr('Layer or Extent passed to clip is None.') raise InvalidParameterError(myMessage) if layer.type() != QgsMapLayer.VectorLayer: myMessage = tr('Expected a vector layer but received a %s.' % str(layer.type())) raise InvalidParameterError(myMessage) #myHandle, myFilename = tempfile.mkstemp('.sqlite', 'clip_', # temp_dir()) myHandle, myFilename = tempfile.mkstemp('.shp', 'clip_', temp_dir()) # Ensure the file is deleted before we try to write to it # fixes windows specific issue where you get a message like this # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory. # This is because mkstemp creates the file handle and leaves # the file open. os.close(myHandle) os.remove(myFilename) # Get the clip extents in the layer's native CRS myGeoCrs = QgsCoordinateReferenceSystem() myGeoCrs.createFromSrid(4326) myXForm = QgsCoordinateTransform(myGeoCrs, layer.crs()) myAllowedClipTypes = [QGis.WKBPolygon, QGis.WKBPolygon25D] if type(extent) is list: myRect = QgsRectangle( extent[0], extent[1], extent[2], extent[3]) # noinspection PyCallByClass myClipPolygon = QgsGeometry.fromRect(myRect) elif (type(extent) is QgsGeometry and extent.wkbType in myAllowedClipTypes): myRect = extent.boundingBox().toRectF() myClipPolygon = extent else: raise InvalidClipGeometryError( tr( 'Clip geometry must be an extent or a single part' 'polygon based geometry.')) myProjectedExtent = myXForm.transformBoundingBox(myRect) # Get vector layer myProvider = layer.dataProvider() if myProvider is None: myMessage = tr('Could not obtain data provider from ' 'layer "%s"' % layer.source()) raise Exception(myMessage) # Get the layer field list, select by our extent then write to disk # .. todo:: FIXME - for different geometry types we should implement # different clipping behaviour e.g. reject polygons that # intersect the edge of the bbox. Tim myRequest = QgsFeatureRequest() if not myProjectedExtent.isEmpty(): myRequest.setFilterRect(myProjectedExtent) myRequest.setFlags(QgsFeatureRequest.ExactIntersect) myFieldList = myProvider.fields() myWriter = QgsVectorFileWriter( myFilename, 'UTF-8', myFieldList, layer.wkbType(), myGeoCrs, #'SQLite') # FIXME (Ole): This works but is far too slow 'ESRI Shapefile') if myWriter.hasError() != QgsVectorFileWriter.NoError: myMessage = tr('Error when creating shapefile: <br>Filename:' '%s<br>Error: %s' % (myFilename, myWriter.hasError())) raise Exception(myMessage) # Reverse the coordinate xform now so that we can convert # geometries from layer crs to geocrs. myXForm = QgsCoordinateTransform(layer.crs(), myGeoCrs) # Retrieve every feature with its geometry and attributes myCount = 0 myHasMultipart = False for myFeature in myProvider.getFeatures(myRequest): myGeometry = myFeature.geometry() # Loop through the parts adding them to the output file # we write out single part features unless explode_flag is False if explode_flag: myGeometryList = explode_multipart_geometry(myGeometry) else: myGeometryList = [myGeometry] for myPartIndex, myPart in enumerate(myGeometryList): myPart.transform(myXForm) if hard_clip_flag: # Remove any dangling bits so only intersecting area is # kept. myPart = clip_geometry(myClipPolygon, myPart) if myPart is None: continue myFeature.setGeometry(myPart) # There are multiple parts and we want to show it in the # explode_attribute if myPartIndex > 0 and explode_attribute is not None: myHasMultipart = True myWriter.addFeature(myFeature) myCount += 1 del myWriter # Flush to disk if myCount < 1: myMessage = tr( 'No features fall within the clip extents. Try panning / zooming ' 'to an area containing data and then try to run your analysis ' 'again. If hazard and exposure data doesn\'t overlap at all, it ' 'is not possible to do an analysis. Another possibility is that ' 'the layers do overlap but because they may have different ' 'spatial references, they appear to be disjointed. If this is the ' 'case, try to turn on reproject on-the-fly in QGIS.') raise NoFeaturesInExtentError(myMessage) myKeywordIO = KeywordIO() if extra_keywords is None: extra_keywords = {} extra_keywords['had multipart polygon'] = myHasMultipart myKeywordIO.copy_keywords( layer, myFilename, extra_keywords=extra_keywords) myBaseName = '%s clipped' % layer.name() myLayer = QgsVectorLayer(myFilename, myBaseName, 'ogr') return myLayer
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) startPoint = self.getParameterValue(self.START_POINT) endPoints = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.END_POINTS), context) strategy = self.getParameterValue(self.STRATEGY) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields, QgsWkbTypes.LineString, layer.crs(), context) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder( iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading end points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = QgsProcessingUtils.getFeatures(endPoints, context, request) count = QgsProcessingUtils.featureCount(endPoints, context) points = [startPoint] for f in features: points.append(f.geometry().asPoint()) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) feedback.pushInfo(self.tr('Calculating shortest paths...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) route = [] total = 100.0 / count for i in range(1, count + 1): idxEnd = graph.findVertex(snappedPoints[i]) if tree[idxEnd] == -1: msg = self.tr( 'There is no route from start point ({}) to end point ({}).' .format(startPoint.toString(), points[i].toString())) feedback.setProgressText(msg) QgsMessageLog.logMessage(msg, self.tr('Processing'), QgsMessageLog.WARNING) continue cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append( graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[0]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = startPoint.toString() feat['end'] = points[i].toString() feat['cost'] = cost / multiplier writer.addFeature(feat) route[:] = [] feedback.setProgress(int(i * total)) del writer
def run(self): """Run any post processors requested by the impact function. Args: None Returns: None Raises: None """ try: myRequestedPostProcessors = self.functionParams['postprocessors'] myPostProcessors = get_postprocessors(myRequestedPostProcessors) except (TypeError, KeyError): # TypeError is for when functionParams is none # KeyError is for when ['postprocessors'] is unavailable myPostProcessors = {} LOGGER.debug('Running this postprocessors: ' + str(myPostProcessors)) myFeatureNameAttribute = self.aggregator.attributes[ self.aggregator.defaults['AGGR_ATTR_KEY']] if myFeatureNameAttribute is None: self.attributeTitle = self.tr('Aggregation unit') else: self.attributeTitle = myFeatureNameAttribute myNameFieldIndex = self.aggregator.layer.fieldNameIndex( self.attributeTitle) mySumFieldIndex = self.aggregator.layer.fieldNameIndex( self._sumFieldName()) myFemaleRatioIsVariable = False myFemRatioFieldIndex = None myFemaleRatio = None if 'Gender' in myPostProcessors: # look if we need to look for a variable female ratio in a layer try: myFemRatioField = self.aggregator.attributes[ self.aggregator.defaults['FEM_RATIO_ATTR_KEY']] myFemRatioFieldIndex = self.aggregator.layer.fieldNameIndex( myFemRatioField) # something went wrong finding the female ratio field, # use defaults from below except block if myFemRatioFieldIndex == -1: raise KeyError myFemaleRatioIsVariable = True except KeyError: try: myFemaleRatio = self.keywordIO.read_keywords( self.aggregator.layer, self.aggregator.defaults['FEM_RATIO_KEY']) except KeywordNotFoundError: myFemaleRatio = self.aggregator.defaults['FEM_RATIO'] # iterate zone features myRequest = QgsFeatureRequest() myRequest.setFlags(QgsFeatureRequest.NoGeometry) myProvider = self.aggregator.layer.dataProvider() # start data retrieval: fetch no geometry and all attributes for each # feature myPolygonIndex = 0 for myFeature in myProvider.getFeatures(myRequest): # if a feature has no field called if myNameFieldIndex == -1: myZoneName = str(myFeature.id()) else: myZoneName = myFeature[myNameFieldIndex] # create dictionary of attributes to pass to postprocessor myGeneralParams = {'target_field': self.aggregator.targetField, 'function_params': self.functionParams} if self.aggregator.statisticsType == 'class_count': myGeneralParams['impact_classes'] = ( self.aggregator.statisticsClasses) elif self.aggregator.statisticsType == 'sum': myImpactTotal = myFeature[mySumFieldIndex] myGeneralParams['impact_total'] = myImpactTotal try: myGeneralParams['impact_attrs'] = ( self.aggregator.impactLayerAttributes[myPolygonIndex]) except IndexError: # rasters and attributeless vectors have no attributes myGeneralParams['impact_attrs'] = None for myKey, myValue in myPostProcessors.iteritems(): myParameters = myGeneralParams try: # look if params are available for this postprocessor myParameters.update( self.functionParams['postprocessors'][myKey]['params']) except KeyError: pass if myKey == 'Gender': if myFemaleRatioIsVariable: myFemaleRatio = myFeature[myFemRatioFieldIndex] if myFemaleRatio is None: myFemaleRatio = self.aggregator.defaults[ 'FEM_RATIO'] LOGGER.debug(myFemaleRatio) myParameters['female_ratio'] = myFemaleRatio myValue.setup(myParameters) myValue.process() myResults = myValue.results() myValue.clear() # LOGGER.debug(myResults) try: self.postProcessingOutput[myKey].append( (myZoneName, myResults)) except KeyError: self.postProcessingOutput[myKey] = [] self.postProcessingOutput[myKey].append( (myZoneName, myResults)) # increment the index myPolygonIndex += 1
def run(self): """Run any post processors requested by the impact function. """ try: requested_postprocessors = self.function_parameters["postprocessors"] postprocessors = get_postprocessors(requested_postprocessors) except (TypeError, KeyError): # TypeError is for when function_parameters is none # KeyError is for when ['postprocessors'] is unavailable postprocessors = {} feature_names_attribute = self.aggregator.attributes[self.aggregator.get_default_keyword("AGGR_ATTR_KEY")] if feature_names_attribute is None: self.attribute_title = self.tr("Aggregation unit") else: self.attribute_title = feature_names_attribute name_filed_index = self.aggregator.layer.fieldNameIndex(self.attribute_title) sum_field_index = self.aggregator.layer.fieldNameIndex(self._sum_field_name()) user_defined_female_ratio = False female_ratio_field_index = None female_ratio = None user_defined_age_ratios = False youth_ratio_field_index = None youth_ratio = None adult_ratio_field_index = None adult_ratio = None elderly_ratio_field_index = None elderly_ratio = None if "Gender" in postprocessors: # look if we need to look for a variable female ratio in a layer try: female_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword("FEMALE_RATIO_ATTR_KEY") ] female_ratio_field_index = self.aggregator.layer.fieldNameIndex(female_ratio_field) # something went wrong finding the female ratio field, # use defaults from below except block if female_ratio_field_index == -1: raise KeyError user_defined_female_ratio = True except KeyError: try: female_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword("FEMALE_RATIO_KEY") ) except KeywordNotFoundError: female_ratio = self.aggregator.get_default_keyword("FEMALE_RATIO") if "Age" in postprocessors: # look if we need to look for a variable age ratio in a layer try: youth_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword("YOUTH_RATIO_ATTR_KEY") ] youth_ratio_field_index = self.aggregator.layer.fieldNameIndex(youth_ratio_field) adult_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword("ADULT_RATIO_ATTR_KEY") ] adult_ratio_field_index = self.aggregator.layer.fieldNameIndex(adult_ratio_field) elderly_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword("ELDERLY_RATIO_ATTR_KEY") ] elderly_ratio_field_index = self.aggregator.layer.fieldNameIndex(elderly_ratio_field) # something went wrong finding the youth ratio field, # use defaults from below except block if youth_ratio_field_index == -1 or adult_ratio_field_index == -1 or elderly_ratio_field_index == -1: raise KeyError user_defined_age_ratios = True except KeyError: try: youth_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword("YOUTH_RATIO_KEY") ) adult_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword("ADULT_RATIO_KEY") ) elderly_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword("ELDERLY_RATIO_KEY") ) except KeywordNotFoundError: youth_ratio = self.aggregator.get_default_keyword("YOUTH_RATIO") adult_ratio = self.aggregator.get_default_keyword("ADULT_RATIO") elderly_ratio = self.aggregator.get_default_keyword("ELDERLY_RATIO") # iterate zone features request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) provider = self.aggregator.layer.dataProvider() # start data retrieval: fetch no geometry and all attributes for each # feature polygon_index = 0 for feature in provider.getFeatures(request): # if a feature has no field called if name_filed_index == -1: zone_name = str(feature.id()) else: zone_name = feature[name_filed_index] if isinstance(zone_name, QPyNullVariant): # proper format for i186 zone_name = tr("Unnamed Area %(feature_id)s") % {"feature_id": str(feature.id())} # create dictionary of attributes to pass to postprocessor general_params = {"target_field": self.aggregator.target_field, "function_params": self.function_parameters} impact_total = feature[sum_field_index] general_params["impact_total"] = impact_total try: general_params["impact_attrs"] = self.aggregator.impact_layer_attributes[polygon_index] except IndexError: # rasters and attributeless vectors have no attributes general_params["impact_attrs"] = None for key, value in postprocessors.iteritems(): parameters = general_params user_parameters = self.function_parameters["postprocessors"][key] user_parameters = dict( [(user_parameter.name, user_parameter.value) for user_parameter in user_parameters] ) try: # user parameters override default parameters parameters.update(user_parameters) except KeyError: pass if key == "Gender": if user_defined_female_ratio: female_ratio = feature[female_ratio_field_index] if female_ratio is None: female_ratio = self.aggregator.defaults["FEMALE_RATIO"] LOGGER.warning( "Data Driven Female ratio " "incomplete, using defaults for" " aggregation unit" " %s" % feature.id ) parameters["female_ratio"] = female_ratio if key == "Age": if user_defined_age_ratios: youth_ratio = feature[youth_ratio_field_index] adult_ratio = feature[adult_ratio_field_index] elderly_ratio = feature[elderly_ratio_field_index] if youth_ratio is None or adult_ratio is None or elderly_ratio is None: LOGGER.debug("--- only default age ratios used ---") youth_ratio = self.aggregator.defaults["YOUTH_RATIO"] adult_ratio = self.aggregator.defaults["ADULT_RATIO"] elderly_ratio = self.aggregator.defaults["ELDERLY_RATIO"] LOGGER.warning( "Data Driven Age ratios " "incomplete, using defaults for" " aggregation unit" " %s" % feature.id ) parameters["youth_ratio"] = youth_ratio parameters["adult_ratio"] = adult_ratio parameters["elderly_ratio"] = elderly_ratio if key == "BuildingType" or key == "RoadType": if key == "BuildingType": key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, "structure_class_field" ) elif key == "RoadType": key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, "road_class_field" ) else: try: key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, "key_attribute" ) except KeywordNotFoundError: # use 'type' as default key_attribute = "type" parameters["key_attribute"] = key_attribute LOGGER.debug("key_attribute: %s", key_attribute) value_map = self.keyword_io.read_keywords(self.aggregator.exposure_layer, "value_mapping") parameters["value_mapping"] = value_map try: value.setup(parameters) value.process() results = value.results() value.clear() if key not in self.output: self.output[key] = [] self.output[key].append([zone_name, results]) except PostProcessorError as e: message = m.Message( m.Heading(self.tr("%s postprocessor problem" % key), **styles.DETAILS_STYLE), m.Paragraph(self.tr(str(e))), ) self.error_message = message # increment the index polygon_index += 1 self.remove_empty_columns() self.remove_empty_lines()
def getMatchingFeatures(self, geometry, contains, singleSelect): newFeatures = [] if geometry.type() != QgsWkbTypes.PolygonGeometry: return newFeatures layer = self.canvas.currentLayer() if layer is None: return newFeatures selectGeomTrans = QgsGeometry(geometry) try: ct = QgsCoordinateTransform(self.canvas.mapSettings().destinationCrs(), layer.crs(), QgsProject.instance()) if not ct.isShortCircuited() and selectGeomTrans.type() == QgsWkbTypes.PolygonGeometry: poly = selectGeomTrans.asPolygon() if len(poly) == 1 and len(poly[0]) == 5: ringIn = poly[0] ringOut = [] ringOut.append(ringIn[0]) i = 1 for j in range(1, 5): v = QgsVector((ringIn[j] - ringIn[j - 1]) / 10.0) for k in range(9): ringOut.append(ringOut[i - 1] + v) i += 1 ringOut.append(ringIn[j]) i += 1 selectGeomTrans = QgsGeometry.fromPolygonXY([ringOut]) selectGeomTrans.transform(ct) except QgsCsException as e: QgsMessageLog.logMessage("Selection extends beyond layer's coordinate system") return newFeatures context = QgsRenderContext.fromMapSettings(self.canvas.mapSettings()) context.expressionContext().appendScope(QgsExpressionContextUtils.layerScope(layer)) r = None if layer.renderer(): r = layer.renderer().clone() r.startRender(context, layer.fields()) request = QgsFeatureRequest() request.setFilterRect(selectGeomTrans.boundingBox()) request.setFlags(QgsFeatureRequest.ExactIntersect) if r: request.setSubsetOfAttributes(r.usedAttributes(context), layer.fields()) else: request.setSubsetOfAttributes([]) closestFeatureId = 0 foundSingleFeature = False closestFeatureDist = sys.float_info.max for f in layer.getFeatures(request): context.expressionContext().setFeature(f) if r and not r.willRenderFeature(f, context): continue g = f.geometry() if contains: if not selectGeomTrans.contains(g): continue else: if not selectGeomTrans.intersects(g): continue if singleSelect: foundSingleFeature = True distance = g.distance(selectGeomTrans) if distance <= closestFeatureDist: closestFeatureDist = distance closestFeatureId = f.id() else: newFeatures.append(f.id()) if singleSelect and foundSingleFeature: newFeatures.append(closestFeatureId) if r: r.stopRender(context) return newFeatures
def aggregation_summary(aggregate_hazard, aggregation, callback=None): """Compute the summary from the aggregate hazard to the analysis layer. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | aggr_id | aggr_name | Output layer : | aggr_id | aggr_name | count of affected features per exposure type :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param aggregation: The aggregation vector layer where to write statistics. :type aggregation: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new aggregation layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_2_aggregation_steps['output_layer_name'] processing_step = summary_2_aggregation_steps['step_name'] source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = aggregation.keywords['inasafe_fields'] target_compulsory_fields = [ aggregation_id_field, aggregation_name_field, ] check_inputs(target_compulsory_fields, target_fields) # Missing exposure_count_field source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, affected_field, ] check_inputs(source_compulsory_fields, source_fields) pattern = exposure_count_field['key'] pattern = pattern.replace('%s', '') unique_exposure = read_dynamic_inasafe_field( source_fields, exposure_count_field) absolute_values = create_absolute_values_structure( aggregate_hazard, ['aggregation_id']) flat_table = FlatTable('aggregation_id', 'exposure_class') aggregation_index = source_fields[aggregation_id_field['key']] # We want to loop over affected features only. request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) expression = '\"%s\" = \'%s\'' % ( affected_field['field_name'], tr('True')) request.setFilterExpression(expression) for area in aggregate_hazard.getFeatures(request): for key, name_field in source_fields.iteritems(): if key.endswith(pattern): aggregation_id = area[aggregation_index] exposure_class = key.replace(pattern, '') value = area[name_field] flat_table.add_value( value, aggregation_id=aggregation_id, exposure_class=exposure_class ) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = area[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value( value, aggregation_id=area[aggregation_index], ) shift = aggregation.fields().count() aggregation.startEditing() add_fields( aggregation, absolute_values, [total_affected_field], unique_exposure, affected_exposure_count_field) aggregation_index = target_fields[aggregation_id_field['key']] request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregation.getFeatures(request): aggregation_value = area[aggregation_index] total = 0 for i, val in enumerate(unique_exposure): sum = flat_table.get_value( aggregation_id=aggregation_value, exposure_class=val ) total += sum aggregation.changeAttributeValue(area.id(), shift + i, sum) aggregation.changeAttributeValue( area.id(), shift + len(unique_exposure), total) for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value( aggregation_id=aggregation_value, ) target_index = shift + len(unique_exposure) + 1 + i aggregation.changeAttributeValue( area.id(), target_index, value) aggregation.commitChanges() aggregation.keywords['title'] = layer_purpose_aggregation_summary['name'] if qgis_version() >= 21800: aggregation.setName(aggregation.keywords['title']) else: aggregation.setLayerName(aggregation.keywords['title']) aggregation.keywords['layer_purpose'] = ( layer_purpose_aggregation_summary['key']) check_layer(aggregation) return aggregation
def run(self): """Run any post processors requested by the impact function. """ try: requested_postprocessors = self.function_parameters[ 'postprocessors'] postprocessors = get_postprocessors(requested_postprocessors) except (TypeError, KeyError): # TypeError is for when function_parameters is none # KeyError is for when ['postprocessors'] is unavailable postprocessors = {} feature_names_attribute = self.aggregator.attributes[ self.aggregator.get_default_keyword('AGGR_ATTR_KEY')] if feature_names_attribute is None: self.attribute_title = self.tr('Aggregation unit') else: self.attribute_title = feature_names_attribute name_filed_index = self.aggregator.layer.fieldNameIndex( self.attribute_title) sum_field_index = self.aggregator.layer.fieldNameIndex( self._sum_field_name()) user_defined_female_ratio = False female_ratio_field_index = None female_ratio = None user_defined_age_ratios = False youth_ratio_field_index = None youth_ratio = None adult_ratio_field_index = None adult_ratio = None elderly_ratio_field_index = None elderly_ratio = None if 'Gender' in postprocessors: # look if we need to look for a variable female ratio in a layer try: female_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'FEMALE_RATIO_ATTR_KEY')] female_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(female_ratio_field) # something went wrong finding the female ratio field, # use defaults from below except block if female_ratio_field_index == -1: raise KeyError user_defined_female_ratio = True except KeyError: try: female_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'FEMALE_RATIO_KEY')) except KeywordNotFoundError: female_ratio = \ self.aggregator.get_default_keyword('FEMALE_RATIO') if 'Age' in postprocessors: # look if we need to look for a variable age ratio in a layer try: youth_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'YOUTH_RATIO_ATTR_KEY')] youth_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(youth_ratio_field) adult_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'ADULT_RATIO_ATTR_KEY')] adult_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(adult_ratio_field) elderly_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'ELDERLY_RATIO_ATTR_KEY')] elderly_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(elderly_ratio_field) # something went wrong finding the youth ratio field, # use defaults from below except block if (youth_ratio_field_index == -1 or adult_ratio_field_index == -1 or elderly_ratio_field_index == -1): raise KeyError user_defined_age_ratios = True except KeyError: try: youth_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword('YOUTH_RATIO_KEY')) adult_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword('ADULT_RATIO_KEY')) elderly_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'ELDERLY_RATIO_KEY')) except KeywordNotFoundError: youth_ratio = \ self.aggregator.get_default_keyword('YOUTH_RATIO') adult_ratio = \ self.aggregator.get_default_keyword('ADULT_RATIO') elderly_ratio = \ self.aggregator.get_default_keyword('ELDERLY_RATIO') # iterate zone features request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) provider = self.aggregator.layer.dataProvider() # start data retrieval: fetch no geometry and all attributes for each # feature polygon_index = 0 for feature in provider.getFeatures(request): # if a feature has no field called if name_filed_index == -1: zone_name = str(feature.id()) else: zone_name = feature[name_filed_index] if isinstance(zone_name, QPyNullVariant): # proper format for i186 zone_name = tr('Unnamed Area %(feature_id)s') % { 'feature_id': str(feature.id()) } # create dictionary of attributes to pass to postprocessor general_params = { 'target_field': self.aggregator.target_field, 'function_params': self.function_parameters } impact_total = feature[sum_field_index] general_params['impact_total'] = impact_total try: general_params['impact_attrs'] = ( self.aggregator.impact_layer_attributes[polygon_index]) except IndexError: # rasters and attributeless vectors have no attributes general_params['impact_attrs'] = None for key, value in postprocessors.iteritems(): parameters = general_params user_parameters = self.function_parameters['postprocessors'][ key] user_parameters = dict([(user_parameter.name, user_parameter.value) for user_parameter in user_parameters]) try: # user parameters override default parameters parameters.update(user_parameters) except KeyError: pass if key == 'Gender': if user_defined_female_ratio: female_ratio = feature[female_ratio_field_index] if female_ratio is None: female_ratio = self.aggregator.defaults[ 'FEMALE_RATIO'] LOGGER.warning('Data Driven Female ratio ' 'incomplete, using defaults for' ' aggregation unit' ' %s' % feature.id) parameters['female_ratio'] = female_ratio if key == 'Age': if user_defined_age_ratios: youth_ratio = feature[youth_ratio_field_index] adult_ratio = feature[adult_ratio_field_index] elderly_ratio = feature[elderly_ratio_field_index] if (youth_ratio is None or adult_ratio is None or elderly_ratio is None): LOGGER.debug( '--- only default age ratios used ---') youth_ratio = self.aggregator.defaults[ 'YOUTH_RATIO'] adult_ratio = self.aggregator.defaults[ 'ADULT_RATIO'] elderly_ratio = self.aggregator.defaults[ 'ELDERLY_RATIO'] LOGGER.warning('Data Driven Age ratios ' 'incomplete, using defaults for' ' aggregation unit' ' %s' % feature.id) parameters['youth_ratio'] = youth_ratio parameters['adult_ratio'] = adult_ratio parameters['elderly_ratio'] = elderly_ratio if key == 'BuildingType' or key == 'RoadType': if key == 'BuildingType': key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, 'structure_class_field') elif key == 'RoadType': key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, 'road_class_field') else: try: key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, 'key_attribute') except KeywordNotFoundError: # use 'type' as default key_attribute = 'type' parameters['key_attribute'] = key_attribute LOGGER.debug('key_attribute: %s', key_attribute) value_map = self.keyword_io.read_keywords( self.aggregator.exposure_layer, 'value_mapping') parameters['value_mapping'] = value_map try: value.setup(parameters) value.process() results = value.results() value.clear() if key not in self.output: self.output[key] = [] self.output[key].append([zone_name, results]) except PostProcessorError as e: message = m.Message( m.Heading(self.tr('%s postprocessor problem' % key), **styles.DETAILS_STYLE), m.Paragraph(self.tr(str(e)))) self.error_message = message # increment the index polygon_index += 1 self.remove_empty_columns() self.remove_empty_lines()
def run(self): """Run any post processors requested by the impact function. """ try: requested_postprocessors = self.function_parameters[ 'postprocessors'] postprocessors = get_postprocessors(requested_postprocessors) except (TypeError, KeyError): # TypeError is for when function_parameters is none # KeyError is for when ['postprocessors'] is unavailable postprocessors = {} feature_names_attribute = self.aggregator.attributes[ self.aggregator.get_default_keyword('AGGR_ATTR_KEY')] if feature_names_attribute is None: self.attribute_title = self.tr('Aggregation unit') else: self.attribute_title = feature_names_attribute name_filed_index = self.aggregator.layer.fieldNameIndex( self.attribute_title) sum_field_index = self.aggregator.layer.fieldNameIndex( self._sum_field_name()) user_defined_female_ratio = False female_ratio_field_index = None female_ratio = None user_defined_age_ratios = False youth_ratio_field_index = None youth_ratio = None adult_ratio_field_index = None adult_ratio = None elderly_ratio_field_index = None elderly_ratio = None if 'Gender' in postprocessors: # look if we need to look for a variable female ratio in a layer try: female_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'FEMALE_RATIO_ATTR_KEY')] female_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(female_ratio_field) # something went wrong finding the female ratio field, # use defaults from below except block if female_ratio_field_index == -1: raise KeyError user_defined_female_ratio = True except KeyError: try: female_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'FEMALE_RATIO_KEY')) except KeywordNotFoundError: female_ratio = \ self.aggregator.get_default_keyword('FEMALE_RATIO') if 'Age' in postprocessors: # look if we need to look for a variable age ratio in a layer try: youth_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'YOUTH_RATIO_ATTR_KEY')] youth_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(youth_ratio_field) adult_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'ADULT_RATIO_ATTR_KEY')] adult_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(adult_ratio_field) elderly_ratio_field = self.aggregator.attributes[ self.aggregator.get_default_keyword( 'ELDERLY_RATIO_ATTR_KEY')] elderly_ratio_field_index = \ self.aggregator.layer.fieldNameIndex(elderly_ratio_field) # something went wrong finding the youth ratio field, # use defaults from below except block if (youth_ratio_field_index == -1 or adult_ratio_field_index == -1 or elderly_ratio_field_index == -1): raise KeyError user_defined_age_ratios = True except KeyError: try: youth_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'YOUTH_RATIO_KEY')) adult_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'ADULT_RATIO_KEY')) elderly_ratio = self.keyword_io.read_keywords( self.aggregator.layer, self.aggregator.get_default_keyword( 'ELDERLY_RATIO_KEY')) except KeywordNotFoundError: youth_ratio = \ self.aggregator.get_default_keyword('YOUTH_RATIO') adult_ratio = \ self.aggregator.get_default_keyword('ADULT_RATIO') elderly_ratio = \ self.aggregator.get_default_keyword('ELDERLY_RATIO') if 'BuildingType' or 'RoadType' in postprocessors: try: key_attribute = self.keyword_io.read_keywords( self.aggregator.exposure_layer, 'key_attribute') except KeywordNotFoundError: # use 'type' as default key_attribute = 'type' # iterate zone features request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) provider = self.aggregator.layer.dataProvider() # start data retrieval: fetch no geometry and all attributes for each # feature polygon_index = 0 for feature in provider.getFeatures(request): # if a feature has no field called if name_filed_index == -1: zone_name = str(feature.id()) else: zone_name = feature[name_filed_index] if isinstance(zone_name, QPyNullVariant): zone_name = 'Unnamed Area %s' % str(feature.id()) # create dictionary of attributes to pass to postprocessor general_params = { 'target_field': self.aggregator.target_field, 'function_params': self.function_parameters} if self.aggregator.statistics_type == 'class_count': general_params['impact_classes'] = ( self.aggregator.statistics_classes) elif self.aggregator.statistics_type == 'sum': impact_total = feature[sum_field_index] general_params['impact_total'] = impact_total try: general_params['impact_attrs'] = ( self.aggregator.impact_layer_attributes[polygon_index]) except IndexError: # rasters and attributeless vectors have no attributes general_params['impact_attrs'] = None for key, value in postprocessors.iteritems(): parameters = general_params user_parameters = self.function_parameters[ 'postprocessors'][key] user_parameters = dict( [(user_parameter.name, user_parameter.value) for user_parameter in user_parameters]) try: # user parameters override default parameters parameters.update(user_parameters) except KeyError: pass if key == 'Gender': if user_defined_female_ratio: female_ratio = feature[female_ratio_field_index] if female_ratio is None: female_ratio = self.aggregator.defaults[ 'FEMALE_RATIO'] LOGGER.warning('Data Driven Female ratio ' 'incomplete, using defaults for' ' aggregation unit' ' %s' % feature.id) parameters['female_ratio'] = female_ratio if key == 'Age': if user_defined_age_ratios: youth_ratio = feature[youth_ratio_field_index] adult_ratio = feature[adult_ratio_field_index] elderly_ratio = feature[elderly_ratio_field_index] if (youth_ratio is None or adult_ratio is None or elderly_ratio is None): LOGGER.debug( '--- only default age ratios used ---') youth_ratio = self.aggregator.defaults[ 'YOUTH_RATIO'] adult_ratio = self.aggregator.defaults[ 'ADULT_RATIO'] elderly_ratio = self.aggregator.defaults[ 'ELDERLY_RATIO'] LOGGER.warning('Data Driven Age ratios ' 'incomplete, using defaults for' ' aggregation unit' ' %s' % feature.id) parameters['youth_ratio'] = youth_ratio parameters['adult_ratio'] = adult_ratio parameters['elderly_ratio'] = elderly_ratio if key == 'BuildingType' or key == 'RoadType': # TODO: Fix this might be referenced before assignment parameters['key_attribute'] = key_attribute try: value.setup(parameters) value.process() results = value.results() value.clear() if key not in self.output: self.output[key] = [] self.output[key].append( (zone_name, results)) except PostProcessorError as e: message = m.Message( m.Heading(self.tr('%s postprocessor problem' % key), **styles.DETAILS_STYLE), m.Paragraph(self.tr(str(e)))) self.error_message = message # increment the index polygon_index += 1 self.remove_empty_columns()
def fetch_values_from_layer(self): # pylint: disable=too-many-locals, too-many-branches, too-many-statements """ (Re)fetches plot values from the source layer. """ # Note: we keep things nice and efficient and only iterate a single time over the layer! if not self.context_generator: context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes( self.source_layer)) else: context = self.context_generator.createExpressionContext() # add a new scope corresponding to the source layer -- this will potentially overwrite any other # layer scopes which may be present in the context (e.g. from atlas layers), but we need to ensure # that source layer fields and attributes are present in the context context.appendScope( self.source_layer.createExpressionContextScope()) self.settings.data_defined_properties.prepare(context) self.fetch_layout_properties(context) def add_source_field_or_expression(field_or_expression): field_index = self.source_layer.fields().lookupField( field_or_expression) if field_index == -1: expression = QgsExpression(field_or_expression) if not expression.hasParserError(): expression.prepare(context) return expression, expression.needsGeometry( ), expression.referencedColumns() return None, False, {field_or_expression} x_expression, x_needs_geom, x_attrs = add_source_field_or_expression(self.settings.properties['x_name']) if \ self.settings.properties[ 'x_name'] else (None, False, set()) y_expression, y_needs_geom, y_attrs = add_source_field_or_expression(self.settings.properties['y_name']) if \ self.settings.properties[ 'y_name'] else (None, False, set()) z_expression, z_needs_geom, z_attrs = add_source_field_or_expression(self.settings.properties['z_name']) if \ self.settings.properties[ 'z_name'] else (None, False, set()) additional_info_expression, additional_needs_geom, additional_attrs = add_source_field_or_expression( self.settings.layout['additional_info_expression'] ) if self.settings.layout['additional_info_expression'] else (None, False, set()) attrs = set().union( self.settings.data_defined_properties.referencedFields(), x_attrs, y_attrs, z_attrs, additional_attrs) request = QgsFeatureRequest() if self.settings.data_defined_properties.property( PlotSettings.PROPERTY_FILTER).isActive(): expression = self.settings.data_defined_properties.property( PlotSettings.PROPERTY_FILTER).asExpression() request.setFilterExpression(expression) request.setExpressionContext(context) request.setSubsetOfAttributes(attrs, self.source_layer.fields()) if not x_needs_geom and not y_needs_geom and not z_needs_geom and not additional_needs_geom and not self.settings.data_defined_properties.hasActiveProperties( ): request.setFlags(QgsFeatureRequest.NoGeometry) visible_geom_engine = None if self.visible_features_only and self.visible_region is not None: ct = QgsCoordinateTransform( self.visible_region.crs(), self.source_layer.crs(), QgsProject.instance().transformContext()) try: rect = ct.transformBoundingBox(self.visible_region) request.setFilterRect(rect) except QgsCsException: pass elif self.visible_features_only and self.polygon_filter is not None: ct = QgsCoordinateTransform( self.polygon_filter.crs(), self.source_layer.crs(), QgsProject.instance().transformContext()) try: rect = ct.transformBoundingBox( self.polygon_filter.geometry.boundingBox()) request.setFilterRect(rect) g = self.polygon_filter.geometry g.transform(ct) visible_geom_engine = QgsGeometry.createGeometryEngine( g.constGet()) visible_geom_engine.prepareGeometry() except QgsCsException: pass if self.selected_features_only: it = self.source_layer.getSelectedFeatures(request) else: it = self.source_layer.getFeatures(request) # Some plot types don't draw individual glyphs for each feature, but aggregate them instead. # In that case it doesn't make sense to evaluate expressions for settings like marker size or color for each # feature. Instead, the evaluation should be executed only once for these settings. aggregating = self.settings.plot_type in ['box', 'histogram'] executed = False xx = [] yy = [] zz = [] additional_hover_text = [] marker_sizes = [] colors = [] stroke_colors = [] stroke_widths = [] for f in it: if visible_geom_engine and not visible_geom_engine.intersects( f.geometry().constGet()): continue self.settings.feature_ids.append(f.id()) context.setFeature(f) x = None if x_expression: x = x_expression.evaluate(context) if x == NULL or x is None: continue elif self.settings.properties['x_name']: x = f[self.settings.properties['x_name']] if x == NULL or x is None: continue y = None if y_expression: y = y_expression.evaluate(context) if y == NULL or y is None: continue elif self.settings.properties['y_name']: y = f[self.settings.properties['y_name']] if y == NULL or y is None: continue z = None if z_expression: z = z_expression.evaluate(context) if z == NULL or z is None: continue elif self.settings.properties['z_name']: z = f[self.settings.properties['z_name']] if z == NULL or z is None: continue if additional_info_expression: additional_hover_text.append( additional_info_expression.evaluate(context)) elif self.settings.layout['additional_info_expression']: additional_hover_text.append( f[self.settings.layout['additional_info_expression']]) if x is not None: xx.append(x) if y is not None: yy.append(y) if z is not None: zz.append(z) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_MARKER_SIZE): default_value = self.settings.properties['marker_size'] context.setOriginalValueVariable(default_value) value, _ = self.settings.data_defined_properties.valueAsDouble( PlotSettings.PROPERTY_MARKER_SIZE, context, default_value) marker_sizes.append(value) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_STROKE_WIDTH): default_value = self.settings.properties['marker_width'] context.setOriginalValueVariable(default_value) value, _ = self.settings.data_defined_properties.valueAsDouble( PlotSettings.PROPERTY_STROKE_WIDTH, context, default_value) stroke_widths.append(value) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_COLOR) and (not aggregating or not executed): default_value = QColor(self.settings.properties['in_color']) value, conversion_success = self.settings.data_defined_properties.valueAsColor( PlotSettings.PROPERTY_COLOR, context, default_value) if conversion_success: # We were given a valid color specification, use that color colors.append(value.name()) else: try: # Attempt to interpret the value as a list of color specifications value_list = self.settings.data_defined_properties.value( PlotSettings.PROPERTY_COLOR, context) color_list = [ QgsSymbolLayerUtils.decodeColor(item).name() for item in value_list ] colors.extend(color_list) except TypeError: # Not a list of color specifications, use the default color instead colors.append(default_value.name()) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_STROKE_COLOR) and (not aggregating or not executed): default_value = QColor(self.settings.properties['out_color']) value, conversion_success = self.settings.data_defined_properties.valueAsColor( PlotSettings.PROPERTY_STROKE_COLOR, context, default_value) if conversion_success: # We were given a valid color specification, use that color stroke_colors.append(value.name()) else: try: # Attempt to interpret the value as a list of color specifications value_list = self.settings.data_defined_properties.value( PlotSettings.PROPERTY_STROKE_COLOR, context) color_list = [ QgsSymbolLayerUtils.decodeColor(item).name() for item in value_list ] stroke_colors.extend(color_list) except TypeError: # Not a list of color specifications, use the default color instead stroke_colors.append(default_value.name()) executed = True self.settings.additional_hover_text = additional_hover_text self.settings.x = xx self.settings.y = yy self.settings.z = zz if marker_sizes: self.settings.data_defined_marker_sizes = marker_sizes if colors: self.settings.data_defined_colors = colors if stroke_colors: self.settings.data_defined_stroke_colors = stroke_colors if stroke_widths: self.settings.data_defined_stroke_widths = stroke_widths
def processAlgorithm(self, feedback): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) startPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.START_POINTS)) strategy = self.getParameterValue(self.STRATEGY) travelCost = self.getParameterValue(self.TRAVEL_COST) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) writerPoints = self.getOutputFromName( self.OUTPUT_POINTS).getVectorWriter( fields, QgsWkbTypes.MultiPoint, layer.crs()) writerPolygons = self.getOutputFromName( self.OUTPUT_POLYGON).getVectorWriter( fields, QgsWkbTypes.Polygon, layer.crs()) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(iface.mapCanvas().mapSettings().destinationCrs(), iface.mapCanvas().hasCrsTransformEnabled(), tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = vector.features(startPoints, request) points = [] for f in features: points.append(f.geometry().asPoint()) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) feedback.pushInfo(self.tr('Calculating service areas...')) graph = builder.graph() vertices = [] upperBoundary = [] lowerBoundary = [] total = 100.0 / len(snappedPoints) for i, p in enumerate(snappedPoints): idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) for j, v in enumerate(cost): if v > travelCost and tree[j] != -1: vertexId = graph.edge(tree[j]).outVertex() if cost[vertexId] <= travelCost: vertices.append(j) for j in vertices: upperBoundary.append(graph.vertex(graph.edge(tree[j]).inVertex()).point()) lowerBoundary.append(graph.vertex(graph.edge(tree[j]).outVertex()).point()) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = origPoint writerPoints.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = origPoint writerPoints.addFeature(feat) upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = origPoint writerPolygons.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = origPoint writerPolygons.addFeature(feat) vertices[:] = [] upperBoundary[:] = [] lowerBoundary[:] = [] feedback.setProgress(int(i * total)) del writerPoints del writerPolygons