def run(self): """Experimental impact function.""" self.validate() self.prepare() # Get parameters from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.hazard_class_mapping = self.hazard.keyword('value_map') self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') # Prepare Hazard Layer hazard_provider = self.hazard.layer.dataProvider() # Check affected field exists in the hazard layer affected_field_index = hazard_provider.fieldNameIndex( self.hazard_class_attribute) if affected_field_index == -1: message = tr( 'Field "%s" is not present in the attribute table of the ' 'hazard layer. Please change the Affected Field parameter in ' 'the IF Option.') % self.hazard_class_attribute raise GetDataError(message) srs = self.exposure.layer.crs().toWkt() exposure_provider = self.exposure.layer.dataProvider() exposure_fields = exposure_provider.fields() # Check self.exposure_class_attribute exists in exposure layer building_type_field_index = exposure_provider.fieldNameIndex( self.exposure_class_attribute) if building_type_field_index == -1: message = tr('Field "%s" is not present in the attribute table of ' 'the exposure layer. Please change the Building Type ' 'Field parameter in the IF Option.' ) % self.exposure_class_attribute raise GetDataError(message) # If target_field does not exist, add it: if exposure_fields.indexFromName(self.target_field) == -1: exposure_provider.addAttributes( [QgsField(self.target_field, QVariant.Int)]) target_field_index = exposure_provider.fieldNameIndex( self.target_field) exposure_fields = exposure_provider.fields() # Create layer to store the lines from E and extent building_layer = QgsVectorLayer('Polygon?crs=' + srs, 'impact_buildings', 'memory') building_provider = building_layer.dataProvider() # Set attributes building_provider.addAttributes(exposure_fields.toList()) building_layer.startEditing() building_layer.commitChanges() # Filter geometry and data using the requested extent requested_extent = QgsRectangle(*self.requested_extent) # This is a hack - we should be setting the extent CRS # in the IF base class via safe/engine/core.py:calculate_impact # for now we assume the extent is in 4326 because it # is set to that from geo_extent # See issue #1857 transform = QgsCoordinateTransform( QgsCoordinateReferenceSystem('EPSG:%i' % self._requested_extent_crs), self.hazard.layer.crs()) projected_extent = transform.transformBoundingBox(requested_extent) request = QgsFeatureRequest() request.setFilterRect(projected_extent) # Split building_layer by H and save as result: # 1) Filter from H inundated features # 2) Mark buildings as inundated (1) or not inundated (0) # make spatial index of affected polygons hazard_index = QgsSpatialIndex() hazard_geometries = {} # key = feature id, value = geometry has_hazard_objects = False for feature in self.hazard.layer.getFeatures(request): value = feature[affected_field_index] if value not in self.hazard_class_mapping[self.wet]: continue hazard_index.insertFeature(feature) hazard_geometries[feature.id()] = QgsGeometry(feature.geometry()) has_hazard_objects = True if not has_hazard_objects: message = tr( 'There are no objects in the hazard layer with %s ' 'value in %s. Please check your data or use another ' 'attribute.') % (self.hazard_class_attribute, ', '.join( self.hazard_class_mapping[self.wet])) raise GetDataError(message) features = [] for feature in self.exposure.layer.getFeatures(request): building_geom = feature.geometry() affected = False # get tentative list of intersecting hazard features # only based on intersection of bounding boxes ids = hazard_index.intersects(building_geom.boundingBox()) for fid in ids: # run (slow) exact intersection test if hazard_geometries[fid].intersects(building_geom): affected = True break f = QgsFeature() f.setGeometry(building_geom) f.setAttributes(feature.attributes()) f[target_field_index] = 1 if affected else 0 features.append(f) # every once in a while commit the created features # to the output layer if len(features) == 1000: (_, __) = building_provider.addFeatures(features) features = [] (_, __) = building_provider.addFeatures(features) building_layer.updateExtents() # Generate simple impact report self.buildings = {} self.affected_buildings = OrderedDict([(tr('Flooded'), {})]) buildings_data = building_layer.getFeatures() building_type_field_index = building_layer.fieldNameIndex( self.exposure_class_attribute) for building in buildings_data: record = building.attributes() building_type = record[building_type_field_index] if building_type in [None, 'NULL', 'null', 'Null']: building_type = 'Unknown type' if building_type not in self.buildings: self.buildings[building_type] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ building_type] = OrderedDict([ (tr('Buildings Affected'), 0) ]) self.buildings[building_type] += 1 if record[target_field_index] == 1: self.affected_buildings[tr('Flooded')][building_type][tr( 'Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() impact_summary = self.html_report() # For printing map purpose map_title = tr('Buildings inundated') legend_title = tr('Structure inundated status') style_classes = [ dict(label=tr('Not Inundated'), value=0, colour='#1EFC7C', transparency=0, size=0.5), dict(label=tr('Inundated'), value=1, colour='#F31A1C', transparency=0, size=0.5) ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # Convert QgsVectorLayer to inasafe layer and return it. if building_layer.featureCount() < 1: raise ZeroImpactException( tr('No buildings were impacted by this flood.')) building_layer = Vector(data=building_layer, name=tr('Flooded buildings'), keywords={ 'impact_summary': impact_summary, 'map_title': map_title, 'legend_title': legend_title, 'target_field': self.target_field, 'buildings_total': self.total_buildings, 'buildings_affected': self.total_affected_buildings }, style_info=style_info) self._impact = building_layer return building_layer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) proximity = self.parameterAsDouble(parameters, self.PROXIMITY, context) radius = self.parameterAsDouble(parameters, self.DISTANCE, context) horizontal = self.parameterAsBool(parameters, self.HORIZONTAL, context) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), source.wkbType(), source.sourceCrs()) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 def searchRect(p): return QgsRectangle(p.x() - proximity, p.y() - proximity, p.x() + proximity, p.y() + proximity) index = QgsSpatialIndex() # NOTE: this is a Python port of QgsPointDistanceRenderer::renderFeature. If refining this algorithm, # please port the changes to QgsPointDistanceRenderer::renderFeature also! clustered_groups = [] group_index = {} group_locations = {} for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().asPoint() other_features_within_radius = index.intersects(searchRect(point)) if not other_features_within_radius: index.insertFeature(f) group = [f] clustered_groups.append(group) group_index[f.id()] = len(clustered_groups) - 1 group_locations[f.id()] = point else: # find group with closest location to this point (may be more than one within search tolerance) min_dist_feature_id = other_features_within_radius[0] min_dist = group_locations[min_dist_feature_id].distance(point) for i in range(1, len(other_features_within_radius)): candidate_id = other_features_within_radius[i] new_dist = group_locations[candidate_id].distance(point) if new_dist < min_dist: min_dist = new_dist min_dist_feature_id = candidate_id group_index_pos = group_index[min_dist_feature_id] group = clustered_groups[group_index_pos] # calculate new centroid of group old_center = group_locations[min_dist_feature_id] group_locations[min_dist_feature_id] = QgsPointXY( (old_center.x() * len(group) + point.x()) / (len(group) + 1.0), (old_center.y() * len(group) + point.y()) / (len(group) + 1.0)) # add to a group clustered_groups[group_index_pos].append(f) group_index[f.id()] = group_index_pos feedback.setProgress(int(current * total)) current = 0 total = 100.0 / len(clustered_groups) if clustered_groups else 1 feedback.setProgress(0) fullPerimeter = 2 * math.pi for group in clustered_groups: if feedback.isCanceled(): break count = len(group) if count == 1: sink.addFeature(group[0], QgsFeatureSink.FastInsert) else: angleStep = fullPerimeter / count if count == 2 and horizontal: currentAngle = math.pi / 2 else: currentAngle = 0 old_point = group_locations[group[0].id()] for f in group: if feedback.isCanceled(): break sinusCurrentAngle = math.sin(currentAngle) cosinusCurrentAngle = math.cos(currentAngle) dx = radius * sinusCurrentAngle dy = radius * cosinusCurrentAngle # we want to keep any existing m/z values point = f.geometry().constGet().clone() point.setX(old_point.x() + dx) point.setY(old_point.y() + dy) f.setGeometry(QgsGeometry(point)) sink.addFeature(f, QgsFeatureSink.FastInsert) currentAngle += angleStep current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) geomType = QgsWkbTypes.multiType(sourceA.wkbType()) fields = vector.combineFields(sourceA.fields(), sourceB.fields()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, geomType, sourceA.sourceCrs()) featB = QgsFeature() outFeat = QgsFeature() indexA = QgsSpatialIndex(sourceA) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs()))) total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 count = 0 for featA in sourceA.getFeatures(): if feedback.isCanceled(): break geom = featA.geometry() diffGeom = QgsGeometry(geom) attrs = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs()) for featB in sourceB.getFeatures(request): if feedback.isCanceled(): break tmpGeom = featB.geometry() if diffGeom.intersects(tmpGeom): diffGeom = QgsGeometry(diffGeom.difference(tmpGeom)) try: outFeat.setGeometry(diffGeom) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.WARNING) continue count += 1 feedback.setProgress(int(count * total)) length = len(sourceA.fields()) for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs())): if feedback.isCanceled(): break geom = featA.geometry() diffGeom = QgsGeometry(geom) attrs = featA.attributes() attrs = [NULL] * length + attrs intersects = indexA.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) for featB in sourceA.getFeatures(request): if feedback.isCanceled(): break tmpGeom = featB.geometry() if diffGeom.intersects(tmpGeom): diffGeom = QgsGeometry(diffGeom.difference(tmpGeom)) try: outFeat.setGeometry(diffGeom) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.WARNING) continue count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def createSpatialIndex(self): if self._spatialindex is None: self._spatialindex = QgsSpatialIndex() for f in self._features.values(): self._spatialindex.insertFeature(f) return True
def regularMatrix(self, parameters, context, source, inField, target_source, targetField, nPoints, feedback): distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) inIdx = source.fields().lookupField(inField) targetIdx = target_source.fields().lookupField(targetField) index = QgsSpatialIndex( target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) first = True sink = None dest_id = None features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() if first: featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) first = False fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('ID') fields.append(input_id_field) for f in target_source.getFeatures( QgsFeatureRequest().setFilterFids( featList).setSubsetOfAttributes([ targetIdx ]).setDestinationCrs(source.sourceCrs(), context.transformContext())): fields.append( QgsField(str(f[targetField]), QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) data = [inFeat[inField]] for target in target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setFilterFids(featList).setDestinationCrs( source.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break outGeom = target.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) data.append(dist) out_feature = QgsFeature() out_feature.setGeometry(inGeom) out_feature.setAttributes(data) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.VECTOR)) pointCount = float(self.getParameterValue(self.POINT_NUMBER)) minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QGis.WKBPoint, layer.crs()) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 featureCount = layer.featureCount() total = 100.0 / pointCount index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: # pick random feature fid = random.randint(0, featureCount - 1) f = layer.getFeatures(request.setFilterFid(fid)).next() fGeom = QgsGeometry(f.geometry()) if fGeom.isMultipart(): lines = fGeom.asMultiPolyline() # pick random line lineId = random.randint(0, len(lines) - 1) vertices = lines[lineId] else: vertices = fGeom.asPolyline() # pick random segment if len(vertices) == 2: vid = 0 else: vid = random.randint(0, len(vertices) - 2) startPoint = vertices[vid] endPoint = vertices[vid + 1] length = da.measureLine(startPoint, endPoint) dist = length * random.random() if dist > minDistance: d = dist / (length - dist) rx = (startPoint.x() + d * endPoint.x()) / (1 + d) ry = (startPoint.y() + d * endPoint.y()) / (1 + d) # generate random point pnt = QgsPoint(rx, ry) geom = QgsGeometry.fromPoint(pnt) if vector.checkMinDistance(pnt, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) writer.addFeature(f) index.insertFeature(f) points[nPoints] = pnt nPoints += 1 progress.setPercentage(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Can not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) del writer
def processAlgorithm(self, feedback): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.VECTOR)) value = float(self.getParameterValue(self.VALUE)) minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) strategy = self.getParameterValue(self.STRATEGY) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, layer.crs()) da = QgsDistanceArea() features = vector.features(layer) for current, f in enumerate(features): fGeom = f.geometry() bbox = fGeom.boundingBox() if strategy == 0: pointCount = int(value) else: pointCount = int(round(value * da.measureArea(fGeom))) index = QgsSpatialIndex() points = dict() nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount random.seed() while nIterations < maxIterations and nPoints < pointCount: rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() pnt = QgsPoint(rx, ry) geom = QgsGeometry.fromPoint(pnt) if geom.within(fGeom) and \ vector.checkMinDistance(pnt, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) writer.addFeature(f) index.insertFeature(f) points[nPoints] = pnt nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr('Can not generate requested number of random ' 'points. Maximum number of attempts exceeded.')) feedback.setProgress(0) del writer
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.INTERSECT, context) fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS, context) fieldsB = self.parameterAsFields(parameters, self.INTERSECT_FIELDS, context) fieldListA = QgsFields() field_indices_a = [] if len(fieldsA) > 0: for f in fieldsA: idxA = sourceA.fields().lookupField(f) if idxA >= 0: field_indices_a.append(idxA) fieldListA.append(sourceA.fields()[idxA]) else: fieldListA = sourceA.fields() field_indices_a = [i for i in range(0, fieldListA.count())] fieldListB = QgsFields() field_indices_b = [] if len(fieldsB) > 0: for f in fieldsB: idxB = sourceB.fields().lookupField(f) if idxB >= 0: field_indices_b.append(idxB) fieldListB.append(sourceB.fields()[idxB]) else: fieldListB = sourceB.fields() field_indices_b = [i for i in range(0, fieldListB.count())] fieldListB = vector.testForUniqueness(fieldListA, fieldListB) for b in fieldListB: fieldListA.append(b) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fieldListA, QgsWkbTypes.Point, sourceA.sourceCrs()) spatialIndex = QgsSpatialIndex( sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(sourceA.sourceCrs())), feedback) outFeat = QgsFeature() features = sourceA.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(field_indices_a)) total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 0 for current, inFeatA in enumerate(features): if feedback.isCanceled(): break if not inFeatA.hasGeometry(): continue inGeom = inFeatA.geometry() has_intersections = False lines = spatialIndex.intersects(inGeom.boundingBox()) engine = None if len(lines) > 0: has_intersections = True # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(inGeom.geometry()) engine.prepareGeometry() if has_intersections: request = QgsFeatureRequest().setFilterFids(lines) request.setDestinationCrs(sourceA.sourceCrs()) request.setSubsetOfAttributes(field_indices_b) for inFeatB in sourceB.getFeatures(request): if feedback.isCanceled(): break tmpGeom = inFeatB.geometry() points = [] if engine.intersects(tmpGeom.geometry()): tempGeom = inGeom.intersection(tmpGeom) out_attributes = [ inFeatA.attributes()[i] for i in field_indices_a ] out_attributes.extend( [inFeatB.attributes()[i] for i in field_indices_b]) if tempGeom.type() == QgsWkbTypes.PointGeometry: if tempGeom.isMultipart(): points = tempGeom.asMultiPoint() else: points.append(tempGeom.asPoint()) for j in points: outFeat.setGeometry(tempGeom.fromPoint(j)) outFeat.setAttributes(out_attributes) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])) total = 100.0 / source.featureCount() if source.featureCount() else 0 geoms = dict() null_geom_features = set() index = QgsSpatialIndex() for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): null_geom_features.add(f.id()) continue geoms[f.id()] = f.geometry() index.addFeature(f) feedback.setProgress(int(0.10 * current * total)) # takes about 10% of time # start by assuming everything is unique, and chop away at this list unique_features = dict(geoms) current = 0 removed = 0 for feature_id, geometry in geoms.items(): if feedback.isCanceled(): break if feature_id not in unique_features: # feature was already marked as a duplicate continue candidates = index.intersects(geometry.boundingBox()) candidates.remove(feature_id) for candidate_id in candidates: if candidate_id not in unique_features: # candidate already marked as a duplicate (not sure if this is possible, # since it would mean the current feature would also have to be a duplicate! # but let's be safe!) continue if geometry.isGeosEqual(geoms[candidate_id]): # candidate is a duplicate of feature del unique_features[candidate_id] removed += 1 current += 1 feedback.setProgress(int(0.80 * current * total) + 10) # takes about 80% of time # now, fetch all the feature attributes for the unique features only # be super-smart and don't re-fetch geometries distinct_geoms = set(unique_features.keys()) output_feature_ids = distinct_geoms.union(null_geom_features) total = 100.0 / len(output_feature_ids) if output_feature_ids else 1 request = QgsFeatureRequest().setFilterFids(list(output_feature_ids)).setFlags(QgsFeatureRequest.NoGeometry) for current, f in enumerate(source.getFeatures(request)): if feedback.isCanceled(): break # use already fetched geometry if f.id() not in null_geom_features: f.setGeometry(unique_features[f.id()]) sink.addFeature(f, QgsFeatureSink.FastInsert) feedback.setProgress(int(0.10 * current * total) + 90) # takes about 10% of time feedback.pushInfo(self.tr('{} duplicate features removed'.format(removed))) return {self.OUTPUT: dest_id, self.DUPLICATE_COUNT: removed, self.RETAINED_COUNT: len(output_feature_ids)}
def processAlgorithm(self, feedback): layerA = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_A)) splitLayer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_B)) sameLayer = self.getParameterValue( self.INPUT_A) == self.getParameterValue(self.INPUT_B) fieldList = layerA.fields() writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fieldList, QgsWkbTypes.multiType(layerA.wkbType()), layerA.crs()) spatialIndex = QgsSpatialIndex() splitGeoms = {} request = QgsFeatureRequest() request.setSubsetOfAttributes([]) for aSplitFeature in vector.features(splitLayer, request): splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry() spatialIndex.insertFeature(aSplitFeature) # honor the case that user has selection on split layer and has setting "use selection" outFeat = QgsFeature() features = vector.features(layerA) if len(features) == 0: total = 100 else: total = 100.0 / float(len(features)) for current, inFeatA in enumerate(features): inGeom = inFeatA.geometry() attrsA = inFeatA.attributes() outFeat.setAttributes(attrsA) if inGeom.isMultipart(): inGeoms = [] for g in inGeom.asGeometryCollection(): inGeoms.append(g) else: inGeoms = [inGeom] lines = spatialIndex.intersects(inGeom.boundingBox()) if len(lines) > 0: # has intersection of bounding boxes splittingLines = [] engine = QgsGeometry.createGeometryEngine(inGeom.geometry()) engine.prepareGeometry() for i in lines: try: splitGeom = splitGeoms[i] except: continue # check if trying to self-intersect if sameLayer: if inFeatA.id() == i: continue if engine.intersects(splitGeom.geometry()): splittingLines.append(splitGeom) if len(splittingLines) > 0: for splitGeom in splittingLines: splitterPList = None outGeoms = [] split_geom_engine = QgsGeometry.createGeometryEngine( splitGeom.geometry()) split_geom_engine.prepareGeometry() while len(inGeoms) > 0: inGeom = inGeoms.pop() if inGeom.isNull( ): # this has been encountered and created a run-time error continue if split_geom_engine.intersects(inGeom.geometry()): inPoints = vector.extractPoints(inGeom) if splitterPList == None: splitterPList = vector.extractPoints( splitGeom) try: result, newGeometries, topoTestPoints = inGeom.splitGeometry( splitterPList, False) except: ProcessingLog.addToLog( ProcessingLog.LOG_WARNING, self. tr('Geometry exception while splitting' )) result = 1 # splitGeometry: If there are several intersections # between geometry and splitLine, only the first one is considered. if result == 0: # split occurred if inPoints == vector.extractPoints( inGeom): # bug in splitGeometry: sometimes it returns 0 but # the geometry is unchanged outGeoms.append(inGeom) else: inGeoms.append(inGeom) for aNewGeom in newGeometries: inGeoms.append(aNewGeom) else: outGeoms.append(inGeom) else: outGeoms.append(inGeom) inGeoms = outGeoms parts = [] for aGeom in inGeoms: passed = True if QgsWkbTypes.geometryType( aGeom.wkbType()) == QgsWkbTypes.LineGeometry: numPoints = aGeom.geometry().numPoints() if numPoints <= 2: if numPoints == 2: passed = not aGeom.geometry().isClosed( ) # tests if vertex 0 = vertex 1 else: passed = False # sometimes splitting results in lines of zero length if passed: parts.append(aGeom) if len(parts) > 0: outFeat.setGeometry(QgsGeometry.collectGeometry(parts)) writer.addFeature(outFeat) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) expression = QgsExpression( self.parameterAsString(parameters, self.EXPRESSION, context)) if expression.hasParserError(): raise ProcessingException(expression.parserErrorString()) expressionContext = self.createExpressionContext(parameters, context) if not expression.prepare(expressionContext): raise ProcessingException( self.tr('Evaluation error: {0}').format( expression.evalErrorString())) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs()) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(source.getFeatures()): if feedback.isCanceled(): break expressionContext.setFeature(f) value = expression.evaluate(expressionContext) if expression.hasEvalError(): feedback.pushInfo( self.tr('Evaluation error for feature ID {}: {}').format( f.id(), expression.evalErrorString())) continue fGeom = f.geometry() bbox = fGeom.boundingBox() if strategy == 0: pointCount = int(value) else: pointCount = int(round(value * da.measureArea(fGeom))) if pointCount == 0: feedback.pushInfo( "Skip feature {} as number of points for it is 0.") continue index = QgsSpatialIndex() points = dict() nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount if pointCount else 1 random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) if geom.within(fGeom) and \ vector.checkMinDistance(p, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.insertFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr('Could not generate requested number of random ' 'points. Maximum number of attempts exceeded.')) feedback.setProgress(0) return {self.OUTPUT: dest_id}
def graph_builder(self, network, cost_field, origins, tolerance, crs, epsg): # Settings otf = False # Get index of cost field network_fields = network.fields() network_cost_index = network_fields.indexFromName(cost_field) # Setting up graph build director director = QgsVectorLayerDirector(network, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth) # Determining cost calculation if cost_field != 'length': strategy = ct.CustomCost(network_cost_index, 0.01) else: strategy = QgsNetworkDistanceStrategy() # Creating graph builder director.addStrategy(strategy) builder = QgsGraphBuilder(crs, otf, tolerance, epsg) # Reading origins and making list of coordinates graph_origin_points = [] # Loop through the origin points and add graph vertex indices for index, origin in enumerate(origins): graph_origin_points.append(origins[index]['geom'].asPoint()) # Get origin graph vertex index tied_origin_vertices = director.makeGraph(builder, graph_origin_points) # Build the graph graph = builder.graph() # Create dictionary of origin names and tied origins tied_origins = {} # Combine origin names and tied point vertices for index, tied_origin in enumerate(tied_origin_vertices): tied_origins[index] = { 'name': origins[index]['name'], 'vertex': tied_origin } self.spIndex = QgsSpatialIndex() self.indices = {} self.attributes_dict = {} self.centroids = {} i = 0 for f in network.getFeatures(): if f.geometry().type() == QgsWkbTypes.LineGeometry: if not f.geometry().isMultipart(): self.attributes_dict[f.id()] = f.attributes() polyline = f.geometry().asPolyline() for idx, p in enumerate(polyline[1:]): ml = QgsGeometry.fromPolylineXY([polyline[idx], p]) new_f = QgsFeature() new_f.setGeometry(ml.centroid()) new_f.setAttributes([f.id()]) new_f.setId(i) self.spIndex.addFeature(new_f) self.centroids[i] = f.id() i += 1 else: self.attributes_dict[f.id()] = f.attributes() for pl in f.geometry().asMultiPolyline(): for idx, p in enumerate(pl[1:]): ml = QgsGeometry.fromPolylineXY([pl[idx], p]) new_f = QgsFeature() new_f.setGeometry(ml.centroid()) new_f.setAttributes([f.id()]) new_f.setId(i) self.spIndex.addFeature(new_f) self.centroids[i] = f.id() i += 1 self.network_fields = network_fields return graph, tied_origins
def processAlgorithm(self, parameters, context, feedback): self.feature_source = self.parameterAsSource(parameters, self.INPUT, context) raster_lyr = self.parameterAsRasterLayer(parameters, self.RASTER_INPUT, context) all_rasters = self.parameterAsBool(parameters, self.ALL_ACTIVE_RASTERS, context) temp_path = self.parameterAsString(parameters, '', context) self.dest_field = self.parameterAsString(parameters, self.FIELD, context) psm = self.parameterAsInt(parameters, 'PSM', context) oem = self.parameterAsInt(parameters, 'OEM', context) self.comma = self.parameterAsBool(parameters, 'Remove_comma', context) (self.sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, self.feature_source.fields(), self.feature_source.wkbType(), self.feature_source.sourceCrs()) self.source_layer = self.feature_source.materialize( QgsFeatureRequest()) feedback.pushInfo('Temporary files path: ' + str(temp_path)) self.source_encod = self.source_layer.dataProvider().encoding() '''context.setDefaultEncoding(self.source_encod) self.output_encod = context.defaultEncoding() feedback.pushInfo('sys.getdefaultencoding(): ' + sys.getdefaultencoding()) feedback.pushInfo('in: ' + self.source_encod + ', out: ' + self.output_encod)''' if self.source_layer == None: list = QgsProject.instance().mapLayersByName( self.feature_source.sourceName()) for lyr in list: if self.feature_source.sourceCrs() == lyr.sourceCrs(): self.source_layer = lyr #feedback.pushInfo('self.source_layer.name(): ' + self.source_layer.name()) if self.feature_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) if raster_lyr is None and not all_rasters: feedback.pushInfo('\nNo raster layer selected!\n') raise QgsProcessingException( self.invalidSourceError(parameters, self.RASTER_INPUT)) self.output_temp_tif = os.path.normpath( os.path.join(temp_path, 'output.tif')) self.output_temp_shp = os.path.normpath( os.path.join(temp_path, 'each_feature.shp')) self.output_temp_page = os.path.normpath( os.path.join(temp_path, 'current_page.tif')) '''here is tesseract config string''' self.config = '--psm ' + str(psm) + ' --oem ' + str(oem) feedback.pushInfo('Tessearct config: ' + self.config) '''creating temporary shp file, necessary for clipping''' self.crs = self.feature_source.sourceCrs().authid() layer = QgsVectorLayer( "multipolygon?crs=" + self.crs + "&field=id:integer", "temporary layer", "memory") QgsVectorFileWriter.writeAsVectorFormat( layer, self.output_temp_shp, self.source_encod, self.feature_source.sourceCrs(), "ESRI Shapefile", False) self.temp_shp_layer = QgsVectorLayer(self.output_temp_shp, "temp", "ogr") features = self.feature_source.getFeatures(QgsFeatureRequest()) self.index = QgsSpatialIndex() for feat in features: self.index.insertFeature(feat) feedback.pushInfo('\nprocessing time calculating...\n') n = [] if not all_rasters and raster_lyr: n = self.index.intersects(raster_lyr.extent()) else: for layer in iface.mapCanvas().layers(): if layer.type() == 1: n = n + self.index.intersects(layer.extent()) self.total = len(n) self.actual = 0 if self.total > 0: feedback.setProgress(self.actual / self.total * 100) if not all_rasters: self.OnThisRaster(feedback, raster_lyr) else: for layer in iface.mapCanvas().layers(): if feedback.isCanceled(): break if layer.type() == 1: self.OnThisRaster(feedback, layer) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): feedback.pushInfo(self.tr("[QNEAT3Algorithm] This is a QNEAT3 Algorithm: '{}'".format(self.displayName()))) network = self.parameterAsVectorLayer(parameters, self.INPUT, context) #QgsVectorLayer startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs()) #QgsPointXY max_dist = self.parameterAsDouble(parameters, self.MAX_DIST, context)#float cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context)#int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int interpolation_method = self.parameterAsEnum(parameters, self.METHOD, context)#int entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) #str defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) #int speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) #str defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) #float tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float output_path = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) analysisCrs = network.sourceCrs() input_coordinates = [startPoint] input_point = getFeatureFromPointParameter(startPoint) feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], entry_cost_calc_method, feedback) feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints([analysis_point], max_dist) feedback.setProgress(70) uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") if interpolation_method == 0: feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(99) else: #prepare numpy coordinate grids NoData_value = -9999 raster_rectangle = iso_pointcloud_layer.extent() #implement spatial index for lines (closest line, etc...) spt_idx = QgsSpatialIndex(iso_pointcloud_layer.getFeatures(QgsFeatureRequest()), feedback) #top left point xmin = raster_rectangle.xMinimum() ymin = raster_rectangle.yMinimum() xmax = raster_rectangle.xMaximum() ymax = raster_rectangle.yMaximum() cols = int((xmax - xmin) / cell_size) rows = int((ymax - ymin) / cell_size) output_interpolation_raster = gdal.GetDriverByName('GTiff').Create(output_path, cols, rows, 1, gdal.GDT_Float64 ) output_interpolation_raster.SetGeoTransform((xmin, cell_size, 0, ymax, 0, -cell_size)) band = output_interpolation_raster.GetRasterBand(1) band.SetNoDataValue(NoData_value) #initialize zero array with 2 dimensions (according to rows and cols) raster_routingcost_data = zeros(shape=(rows, cols)) #compute raster cell MIDpoints x_pos = linspace(xmin+(cell_size/2), xmax -(cell_size/2), raster_routingcost_data.shape[1]) y_pos = linspace(ymax-(cell_size/2), ymin + (cell_size/2), raster_routingcost_data.shape[0]) x_grid, y_grid = meshgrid(x_pos, y_pos) feedback.pushInfo('[QNEAT3Network][calcQneatInterpolation] Beginning with interpolation') total_work = rows * cols counter = 0 feedback.pushInfo('[QNEAT3Network][calcQneatInterpolation] Total workload: {} cells'.format(total_work)) feedback.setProgress(0) for i in range(rows): for j in range(cols): current_pixel_midpoint = QgsPointXY(x_grid[i,j],y_grid[i,j]) nearest_vertex_fid = spt_idx.nearestNeighbor(current_pixel_midpoint, 1)[0] nearest_feature = iso_pointcloud_layer.getFeature(nearest_vertex_fid) nearest_vertex = net.network.vertex(nearest_feature['vertex_id']) #yields a list of all incoming and outgoing edges edges = nearest_vertex.incomingEdges() + nearest_vertex.outgoingEdges() vertex_found = False nearest_counter = 2 while vertex_found == False: #find the second nearest vertex (eg, the vertex with least cost of all edges incoming to the first nearest vertex) second_nearest_feature_fid = spt_idx.nearestNeighbor(current_pixel_midpoint, nearest_counter)[nearest_counter-1] second_nearest_feature = iso_pointcloud_layer.getFeature(second_nearest_feature_fid) second_nearest_vertex_id = second_nearest_feature['vertex_id'] for edge_id in edges: from_vertex_id = net.network.edge(edge_id).fromVertex() to_vertex_id = net.network.edge(edge_id).toVertex() if second_nearest_vertex_id == from_vertex_id: vertex_found = True vertex_type = "from_vertex" from_point = second_nearest_feature.geometry().asPoint() from_vertex_cost = second_nearest_feature['cost'] if second_nearest_vertex_id == to_vertex_id: vertex_found = True vertex_type = "to_vertex" to_point = second_nearest_feature.geometry().asPoint() to_vertex_cost = second_nearest_feature['cost'] nearest_counter = nearest_counter + 1 """ if nearest_counter == 5: vertex_found = True vertex_type = "end_vertex" """ if vertex_type == "from_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY([from_point, nearest_vertex.point()]) res = nearest_edge_geometry.closestSegmentWithContext(current_pixel_midpoint) segment_point = res[1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance(current_pixel_midpoint) dist_edge = from_point.distance(segment_point) #feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = from_vertex_cost + dist_edge + dist_to_segment raster_routingcost_data[i,j] = pixel_cost elif vertex_type == "to_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY([nearest_vertex.point(), to_point]) res = nearest_edge_geometry.closestSegmentWithContext(current_pixel_midpoint) segment_point = res[1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance(current_pixel_midpoint) dist_edge = to_point.distance(segment_point) #feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = to_vertex_cost + dist_edge + dist_to_segment raster_routingcost_data[i,j] = pixel_cost else: pixel_cost = -99999#nearest_feature['cost'] + (nearest_vertex.point().distance(current_pixel_midpoint)) """ nearest_feature_pointxy = nearest_feature.geometry().asPoint() nearest_feature_cost = nearest_feature['cost'] dist_to_vertex = current_pixel_midpoint.distance(nearest_feature_pointxy) #implement time cost pixel_cost = dist_to_vertex + nearest_feature_cost raster_data[i,j] = pixel_cost """ counter = counter+1 if counter%1000 == 0: feedback.pushInfo("[QNEAT3Network][calcQneatInterpolation] Interpolated {} cells...".format(counter)) feedback.setProgress((counter/total_work)*100) band.WriteArray(raster_routingcost_data) outRasterSRS = osr.SpatialReference() outRasterSRS.ImportFromWkt(net.AnalysisCrs.toWkt()) output_interpolation_raster.SetProjection(outRasterSRS.ExportToWkt()) band.FlushCache() feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") feedback.setProgress(100) results = {} results[self.OUTPUT] = output_path return results
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) if self.MIN_DISTANCE in parameters and parameters[ self.MIN_DISTANCE] is not None: minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) else: minDistance = None expressionContext = self.createExpressionContext( parameters, context, source) dynamic_value = QgsProcessingParameters.isDynamic(parameters, "VALUE") value_property = None if self.EXPRESSION in parameters and parameters[ self.EXPRESSION] is not None: expression = QgsExpression( self.parameterAsString(parameters, self.EXPRESSION, context)) value = None if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) expression.prepare(expressionContext) else: expression = None if dynamic_value: value_property = parameters["VALUE"] value = self.parameterAsDouble(parameters, self.VALUE, context) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.ellipsoid()) total = 100.0 / source.featureCount() if source.featureCount() else 0 current_progress = 0 pointId = 0 for current, f in enumerate(source.getFeatures()): if feedback.isCanceled(): break if not f.hasGeometry(): continue current_progress = total * current feedback.setProgress(current_progress) this_value = value if value_property is not None or expression is not None: expressionContext.setFeature(f) if value_property: this_value, _ = value_property.valueAsDouble( expressionContext, value) else: this_value = expression.evaluate(expressionContext) if expression.hasEvalError(): feedback.pushInfo( self.tr('Evaluation error for feature ID {}: {}'). format(f.id(), expression.evalErrorString())) continue fGeom = f.geometry() engine = QgsGeometry.createGeometryEngine(fGeom.constGet()) engine.prepareGeometry() bbox = fGeom.boundingBox() if strategy == 0: pointCount = int(this_value) else: pointCount = int(round(this_value * da.measureArea(fGeom))) if pointCount == 0: feedback.pushInfo( "Skip feature {} as number of points for it is 0.".format( f.id())) continue index = None if minDistance: index = QgsSpatialIndex() points = dict() nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 feature_total = total / pointCount if pointCount else 1 random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) if engine.contains(geom.constGet()) and \ (not minDistance or vector.checkMinDistance(p, index, minDistance, points)): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', pointId) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) if minDistance: index.addFeature(f) points[nPoints] = p nPoints += 1 pointId += 1 feedback.setProgress(current_progress + int(nPoints * feature_total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr('Could not generate requested number of random ' 'points. Maximum number of attempts exceeded.')) feedback.setProgress(100) return {self.OUTPUT: dest_id}
def doWork(self): flayer = self.flayer tlayer = self.tlayer ffield = self.ffield tfield = self.tfield self.from_layer = get_vector_layer_by_name(flayer) self.to_layer = get_vector_layer_by_name(tlayer) self.transform = None if self.from_layer.dataProvider().crs() != self.to_layer.dataProvider().crs(): self.transform = QgsCoordinateTransform(self.from_layer.dataProvider().crs(), self.to_layer.dataProvider().crs()) # FIELDS INDICES idx = self.from_layer.fieldNameIndex(ffield) fid = self.to_layer.fieldNameIndex(tfield) # We create an spatial self.index to hold all the features of the layer that will receive the data # And a dictionary that will hold all the features IDs found to intersect with each feature in the spatial index self.emit(SIGNAL("ProgressMaxValue( PyQt_PyObject )"), self.to_layer.dataProvider().featureCount()) self.emit(SIGNAL("ProgressText( PyQt_PyObject )"), 'Building Spatial Index') allfeatures = {} merged = {} self.index = QgsSpatialIndex() for i, feature in enumerate(self.to_layer.getFeatures()): allfeatures[feature.id()] = feature merged[feature.id()] = feature self.index.insertFeature(feature) self.emit(SIGNAL("ProgressValue( PyQt_PyObject )"), i) self.emit(SIGNAL("ProgressText( PyQt_PyObject )"), 'Duplicating Layers') self.all_attr = {} # We create the memory layer that will have the analysis result, which is the lowest common # denominator of both layers epsg_code = int(self.to_layer.crs().authid().split(":")[1]) if self.from_layer.wkbType() in self.poly_types and self.to_layer.wkbType() in self.poly_types: lcd_layer = QgsVectorLayer("MultiPolygon?crs=epsg:" + str(epsg_code), "output", "memory") self.output_type = 'Poly' elif self.from_layer.wkbType() in self.poly_types + self.line_types and \ self.to_layer.wkbType() in self.poly_types + self.line_types: lcd_layer = QgsVectorLayer("MultiLineString?crs=epsg:" + str(epsg_code), "output", "memory") self.output_type = 'Line' else: lcd_layer = QgsVectorLayer("MultiPoint?crs=epsg:" + str(epsg_code), "output", "memory") self.output_type = 'Point' lcdpr = lcd_layer.dataProvider() lcdpr.addAttributes([QgsField("Part_ID", QVariant.Int), QgsField(ffield, self.from_layer.fields().field(idx).type()), QgsField(tfield, self.to_layer.fields().field(fid).type()), QgsField('P-' + str(ffield), QVariant.Double), # percentage of the from field QgsField('P-' + str(tfield), QVariant.Double)]) # percentage of the to field lcd_layer.updateFields() # PROGRESS BAR self.emit(SIGNAL("ProgressMaxValue( PyQt_PyObject )"), self.from_layer.dataProvider().featureCount()) self.emit(SIGNAL("ProgressText( PyQt_PyObject )"), 'Running Analysis') part_id = 1 features = [] for fc, feat in enumerate(self.from_layer.getFeatures()): geom = feat.geometry() if geom is not None: if self.transform is not None: a = geom.transform(self.transform) geometry, statf = self.find_geometry(geom) uncovered, statf = self.find_geometry(geom) # uncovered = copy.deepcopy(geometry) intersecting = self.index.intersects(geometry.boundingBox()) # Find all intersecting parts for f in intersecting: g = geometry.intersection(allfeatures[f].geometry()) if g.area() > 0: feature = QgsFeature() geo, stati = self.find_geometry(g) feature.setGeometry(geo) geo, statt = self.find_geometry(allfeatures[f].geometry()) perct = stati / statt percf = stati / statf feature.setAttributes([part_id, feat.attributes()[idx], allfeatures[f].attributes()[fid], percf, perct]) features.append(feature) # prepare the data for the non overlapping if uncovered is not None: uncovered = uncovered.difference(g) aux = merged[f].geometry().difference(g) if aux is not None: merged[f].setGeometry(aux) part_id += 1 # Find the part that does not intersect anything if uncovered is not None: if uncovered.area() > 0: feature = QgsFeature() geo, stati = self.find_geometry(uncovered) feature.setGeometry(geo) perct = 0 percf = stati / statf feature.setAttributes([part_id, feat.attributes()[idx], '', percf, perct]) features.append(feature) part_id += 1 self.emit(SIGNAL("ProgressValue( PyQt_PyObject )"), fc) self.emit(SIGNAL("ProgressText( PyQt_PyObject )"), 'Running Analysis (' + "{:,}".format(fc) + '/' + "{:,}".format( self.from_layer.featureCount()) + ')') # Find the features on TO that have no correspondence in FROM for f, feature in merged.iteritems(): geom = feature.geometry() aux, statt = self.find_geometry(allfeatures[f].geometry()) if geom.area() > 0: feature = QgsFeature() geo, stati = self.find_geometry(geom) feature.setGeometry(geo) perct = stati / statt percf = 0 feature.setAttributes([part_id, '', allfeatures[f].attributes()[fid], percf, perct]) features.append(feature) part_id += 1 if features: a = lcdpr.addFeatures(features) self.result = lcd_layer self.emit(SIGNAL("ProgressValue( PyQt_PyObject )"), self.from_layer.dataProvider().featureCount()) self.emit(SIGNAL("finished_threaded_procedure( PyQt_PyObject )"), "procedure")
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) geomType = QgsWkbTypes.multiType(sourceA.wkbType()) fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, geomType, sourceA.sourceCrs()) featA = QgsFeature() featB = QgsFeature() outFeat = QgsFeature() indexA = QgsSpatialIndex(sourceA, feedback) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs(), context.transformContext())), feedback) total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 count = 0 for featA in sourceA.getFeatures(): if feedback.isCanceled(): break lstIntersectingB = [] geom = featA.geometry() atMapA = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: geom.convertToMultiType() outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: # This really shouldn't happen, as we haven't # edited the input geom at all feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext()) engine = QgsGeometry.createGeometryEngine(geom.constGet()) engine.prepareGeometry() for featB in sourceB.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.constGet()): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if not int_geom: # There was a problem creating the intersection feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.wkbType()) == QgsWkbTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: int_geom.convertToMultiType() outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if QgsWkbTypes.geometryType(int_geom.wkbType()) == QgsWkbTypes.geometryType(geomType): try: int_geom.convertToMultiType() outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) # the remaining bit of featA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(diff_geom.wkbType()) == QgsWkbTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) count += 1 feedback.setProgress(int(count * total)) length = len(sourceA.fields()) atMapA = [None] * length for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break add = False geom = featA.geometry() diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(featA.attributes()) intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: geom.convertToMultiType() outFeat.setGeometry(geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext()) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(diff_geom.constGet()) engine.prepareGeometry() for featB in sourceA.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.constGet()): add = True diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) else: try: # Ihis only happens if the bounding box # intersects, but the geometry doesn't diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) if add: try: diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) geomType = QgsWkbTypes.multiType(sourceA.wkbType()) fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS, context) fieldsB = self.parameterAsFields(parameters, self.OVERLAY_FIELDS, context) fieldListA = QgsFields() field_indices_a = [] if len(fieldsA) > 0: for f in fieldsA: idxA = sourceA.fields().lookupField(f) if idxA >= 0: field_indices_a.append(idxA) fieldListA.append(sourceA.fields()[idxA]) else: fieldListA = sourceA.fields() field_indices_a = [i for i in range(0, fieldListA.count())] fieldListB = QgsFields() field_indices_b = [] if len(fieldsB) > 0: for f in fieldsB: idxB = sourceB.fields().lookupField(f) if idxB >= 0: field_indices_b.append(idxB) fieldListB.append(sourceB.fields()[idxB]) else: fieldListB = sourceB.fields() field_indices_b = [i for i in range(0, fieldListB.count())] output_fields = QgsProcessingUtils.combineFields( fieldListA, fieldListB) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, output_fields, geomType, sourceA.sourceCrs()) outFeat = QgsFeature() indexB = QgsSpatialIndex( sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(sourceA.sourceCrs())), feedback) total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 1 count = 0 for featA in sourceA.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(field_indices_a)): if feedback.isCanceled(): break if not featA.hasGeometry(): continue geom = featA.geometry() atMapA = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects) request.setDestinationCrs(sourceA.sourceCrs()) request.setSubsetOfAttributes(field_indices_b) engine = None if len(intersects) > 0: # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(geom.constGet()) engine.prepareGeometry() for featB in sourceB.getFeatures(request): if feedback.isCanceled(): break tmpGeom = featB.geometry() if engine.intersects(tmpGeom.constGet()): out_attributes = [ featA.attributes()[i] for i in field_indices_a ] out_attributes.extend( [featB.attributes()[i] for i in field_indices_b]) int_geom = QgsGeometry(geom.intersection(tmpGeom)) if int_geom.wkbType( ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType( int_geom.wkbType( )) == QgsWkbTypes.GeometryCollection: int_com = geom.combine(tmpGeom) int_geom = QgsGeometry() if int_com: int_sym = geom.symDifference(tmpGeom) int_geom = QgsGeometry(int_com.difference(int_sym)) if int_geom.isEmpty() or not int_geom.isGeosValid(): raise QgsProcessingException( self.tr('GEOS geoprocessing error: One or ' 'more input features have invalid ' 'geometry.')) try: if QgsWkbTypes.geometryType(int_geom.wkbType( )) == QgsWkbTypes.geometryType(geomType): int_geom.convertToMultiType() outFeat.setGeometry(int_geom) outFeat.setAttributes(out_attributes) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: raise QgsProcessingException( self.tr('Feature geometry error: One or more ' 'output features ignored due to invalid ' 'geometry.')) count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): line_source = self.parameterAsSource(parameters, self.LINES, context) poly_source = self.parameterAsSource(parameters, self.POLYGONS, context) length_field_name = self.parameterAsString(parameters, self.LEN_FIELD, context) count_field_name = self.parameterAsString(parameters, self.COUNT_FIELD, context) fields = poly_source.fields() if fields.lookupField(length_field_name) < 0: fields.append(QgsField(length_field_name, QVariant.Double)) length_field_index = fields.lookupField(length_field_name) if fields.lookupField(count_field_name) < 0: fields.append(QgsField(count_field_name, QVariant.Int)) count_field_index = fields.lookupField(count_field_name) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, poly_source.wkbType(), poly_source.sourceCrs()) spatialIndex = QgsSpatialIndex( line_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(poly_source.sourceCrs())), feedback) distArea = QgsDistanceArea() distArea.setSourceCrs(poly_source.sourceCrs()) distArea.setEllipsoid(context.project().ellipsoid()) features = poly_source.getFeatures() total = 100.0 / poly_source.featureCount() if poly_source.featureCount( ) else 0 for current, poly_feature in enumerate(features): if feedback.isCanceled(): break output_feature = QgsFeature() count = 0 length = 0 if poly_feature.hasGeometry(): poly_geom = poly_feature.geometry() has_intersections = False lines = spatialIndex.intersects(poly_geom.boundingBox()) engine = None if len(lines) > 0: has_intersections = True # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine( poly_geom.constGet()) engine.prepareGeometry() if has_intersections: request = QgsFeatureRequest().setFilterFids( lines).setSubsetOfAttributes([]).setDestinationCrs( poly_source.sourceCrs()) for line_feature in line_source.getFeatures(request): if feedback.isCanceled(): break if engine.intersects( line_feature.geometry().constGet()): outGeom = poly_geom.intersection( line_feature.geometry()) length += distArea.measureLength(outGeom) count += 1 output_feature.setGeometry(poly_geom) attrs = poly_feature.attributes() if length_field_index == len(attrs): attrs.append(length) else: attrs[length_field_index] = length if count_field_index == len(attrs): attrs.append(count) else: attrs[count_field_index] = count output_feature.setAttributes(attrs) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): layerA = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_A)) splitLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_B)) sameLayer = self.getParameterValue(self.INPUT_A) == self.getParameterValue(self.INPUT_B) fieldList = layerA.fields() writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fieldList, layerA.wkbType(), layerA.crs()) spatialIndex = QgsSpatialIndex() splitGeoms = {} request = QgsFeatureRequest() request.setSubsetOfAttributes([]) for aSplitFeature in vector.features(splitLayer, request): splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry() spatialIndex.insertFeature(aSplitFeature) # honor the case that user has selection on split layer and has setting "use selection" outFeat = QgsFeature() features = vector.features(layerA) if len(features) == 0: total = 100 else: total = 100.0 / float(len(features)) multiGeoms = 0 # how many multi geometries were encountered for current, inFeatA in enumerate(features): inGeom = inFeatA.geometry() if inGeom.isMultipart(): multiGeoms += 1 # MultiGeometries are not allowed because the result of a splitted part cannot be clearly defined: # 1) add both new parts as new features # 2) store one part as a new feature and the other one as part of the multi geometry # 2a) which part should be which, seems arbitrary else: attrsA = inFeatA.attributes() outFeat.setAttributes(attrsA) inGeoms = [inGeom] lines = spatialIndex.intersects(inGeom.boundingBox()) if len(lines) > 0: # has intersection of bounding boxes splittingLines = [] engine = QgsGeometry.createGeometryEngine(inGeom.geometry()) engine.prepareGeometry() for i in lines: try: splitGeom = splitGeoms[i] except: continue # check if trying to self-intersect if sameLayer: if inFeatA.id() == i: continue if engine.intersects(splitGeom.geometry()): splittingLines.append(splitGeom) if len(splittingLines) > 0: for splitGeom in splittingLines: splitterPList = None outGeoms = [] split_geom_engine = QgsGeometry.createGeometryEngine(splitGeom.geometry()) split_geom_engine.prepareGeometry() while len(inGeoms) > 0: inGeom = inGeoms.pop() if split_geom_engine.intersects(inGeom.geometry()): inPoints = vector.extractPoints(inGeom) if splitterPList == None: splitterPList = vector.extractPoints(splitGeom) try: result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False) except: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('Geometry exception while splitting')) result = 1 # splitGeometry: If there are several intersections # between geometry and splitLine, only the first one is considered. if result == 0: # split occurred if inPoints == vector.extractPoints(inGeom): # bug in splitGeometry: sometimes it returns 0 but # the geometry is unchanged QgsMessageLog.logMessage("appending") outGeoms.append(inGeom) else: inGeoms.append(inGeom) for aNewGeom in newGeometries: inGeoms.append(aNewGeom) else: QgsMessageLog.logMessage("appending else") outGeoms.append(inGeom) else: outGeoms.append(inGeom) inGeoms = outGeoms for aGeom in inGeoms: passed = True if QgsWkbTypes.geometryType( aGeom.wkbType() ) == QgsWkbTypes.LineGeometry \ and not QgsWkbTypes.isMultiType(aGeom.wkbType()): passed = len(aGeom.asPolyline()) > 2 if not passed: passed = (len(aGeom.asPolyline()) == 2 and aGeom.asPolyline()[0] != aGeom.asPolyline()[1]) # sometimes splitting results in lines of zero length if passed: outFeat.setGeometry(aGeom) writer.addFeature(outFeat) progress.setPercentage(int(current * total)) if multiGeoms > 0: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: %s input features ignored due to multi-geometry.') % str(multiGeoms)) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) bbox = source.sourceExtent() sourceIndex = QgsSpatialIndex(source, feedback) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs()) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPoint(p) ids = sourceIndex.intersects(geom.buffer(5, 5).boundingBox()) if len(ids) > 0 and \ vector.checkMinDistance(p, index, minDistance, points): request = QgsFeatureRequest().setFilterFids( ids).setSubsetOfAttributes([]) for f in source.getFeatures(request): if feedback.isCanceled(): break tmpGeom = f.geometry() if geom.within(tmpGeom): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.insertFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr( 'Could not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) return {self.OUTPUT: dest_id}
def processAlgorithm(self, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.VECTOR), context) fieldName = self.getParameterValue(self.FIELD) minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) strategy = self.getParameterValue(self.STRATEGY) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context) da = QgsDistanceArea() features = QgsProcessingUtils.getFeatures(layer, context) for current, f in enumerate(features): fGeom = f.geometry() bbox = fGeom.boundingBox() if strategy == 0: pointCount = int(f[fieldName]) else: pointCount = int(round(f[fieldName] * da.measureArea(fGeom))) if pointCount == 0: feedback.pushInfo("Skip feature {} as number of points for it is 0.") continue index = QgsSpatialIndex() points = dict() nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount random.seed() while nIterations < maxIterations and nPoints < pointCount: rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() pnt = QgsPoint(rx, ry) geom = QgsGeometry.fromPoint(pnt) if geom.within(fGeom) and \ vector.checkMinDistance(pnt, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) writer.addFeature(f) index.insertFeature(f) points[nPoints] = pnt nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: QgsMessageLog.logMessage(self.tr('Can not generate requested number of random ' 'points. Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO) feedback.setProgress(0) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 featureCount = source.featureCount() total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break # pick random feature fid = random.randint(0, featureCount - 1) f = next( source.getFeatures( request.setFilterFid(fid).setSubsetOfAttributes([]))) fGeom = f.geometry() if fGeom.isMultipart(): lines = fGeom.asMultiPolyline() # pick random line lineId = random.randint(0, len(lines) - 1) vertices = lines[lineId] else: vertices = fGeom.asPolyline() # pick random segment if len(vertices) == 2: vid = 0 else: vid = random.randint(0, len(vertices) - 2) startPoint = vertices[vid] endPoint = vertices[vid + 1] length = da.measureLine(startPoint, endPoint) dist = length * random.random() if dist > minDistance: d = dist / (length - dist) rx = (startPoint.x() + d * endPoint.x()) / (1 + d) ry = (startPoint.y() + d * endPoint.y()) / (1 + d) # generate random point p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) if vector.checkMinDistance(p, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.insertFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr( 'Could not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) return {self.OUTPUT: dest_id}
def generate_sampling_points(self, pixel_values, number_of_samples, min_distance, neighbor_aggregation, attempts_by_sampling, progress_bar, random_seed): """Some code base from (by Alexander Bruy): https://github.com/qgis/QGIS/blob/release-2_18/python/plugins/processing/algs/qgis/RandomPointsExtent.py """ self.pixel_values = pixel_values self.number_of_samples = number_of_samples # desired self.total_of_samples = None # total generated self.min_distance = min_distance self.neighbor_aggregation = neighbor_aggregation progress_bar.setValue(0) # init progress bar self.ThematicR_boundaries = QgsGeometry().fromRect(self.ThematicR.extent()) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) thematic_CRS = self.ThematicR.qgs_layer.crs() file_format = \ "GPKG" if self.output_file.endswith(".gpkg") else "ESRI Shapefile" if self.output_file.endswith(".shp") else None writer = QgsVectorFileWriter(self.output_file, "System", fields, QgsWkbTypes.Point, thematic_CRS, file_format) if self.sampling_type == "simple": total_of_samples = self.number_of_samples if self.sampling_type == "stratified": total_of_samples = sum(self.number_of_samples) self.samples_in_categories = [0] * len(self.number_of_samples) # total generated by categories nPoints = 0 nIterations = 0 self.index = QgsSpatialIndex() if attempts_by_sampling: maxIterations = total_of_samples * attempts_by_sampling else: maxIterations = float('Inf') # init the random sampling seed self.random_seed = random_seed random.seed(self.random_seed) points_generated = [] while nIterations < maxIterations and nPoints < total_of_samples: random_sampling_point = RandomPoint(self.ThematicR.extent()) # checks to the sampling point, else discard and continue if not self.check_sampling_point(random_sampling_point): nIterations += 1 continue if self.sampling_type == "stratified": self.samples_in_categories[random_sampling_point.index_pixel_value] += 1 points_generated.append(random_sampling_point) # it requires tmp save the point to check min distance for the next sample f = QgsFeature(nPoints) f.setGeometry(random_sampling_point.QgsGeom) self.index.insertFeature(f) self.points[nPoints] = random_sampling_point.QgsPnt nPoints += 1 nIterations += 1 # update progress bar progress_bar.setValue(int(nPoints)) # guarantee the random order for the classification random.shuffle(points_generated) self.points = dict() # restart for num_point, point_generated in enumerate(points_generated): # random sampling point passed the checks, save it f = QgsFeature() f.initAttributes(1) f.setFields(fields) f.setAttribute('id', num_point+1) f.setGeometry(point_generated.QgsGeom) writer.addFeature(f) self.points[num_point] = point_generated.QgsPnt # save the total point generated self.total_of_samples = len(points_generated) del writer, self.index
def linearMatrix(self, parameters, context, source, inField, target_source, targetField, matType, nPoints, feedback): inIdx = source.fields().lookupField(inField) outIdx = target_source.fields().lookupField(targetField) fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('InputID') fields.append(input_id_field) if matType == 0: target_id_field = target_source.fields()[outIdx] target_id_field.setName('TargetID') fields.append(target_id_field) fields.append(QgsField('Distance', QVariant.Double)) else: fields.append(QgsField('MEAN', QVariant.Double)) fields.append(QgsField('STDDEV', QVariant.Double)) fields.append(QgsField('MIN', QVariant.Double)) fields.append(QgsField('MAX', QVariant.Double)) out_wkb = QgsWkbTypes.multiType(source.wkbType()) if matType == 0 else source.wkbType() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, out_wkb, source.sourceCrs()) index = QgsSpatialIndex(target_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(source.sourceCrs())), feedback) distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs()) distArea.setEllipsoid(context.project().ellipsoid()) features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() inID = str(inFeat.attributes()[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) distList = [] vari = 0.0 request = QgsFeatureRequest().setFilterFids(featList).setSubsetOfAttributes([outIdx]).setDestinationCrs(source.sourceCrs()) for outFeat in target_source.getFeatures(request): if feedback.isCanceled(): break outID = outFeat.attributes()[outIdx] outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) if matType == 0: out_feature = QgsFeature() out_geom = QgsGeometry.unaryUnion([inFeat.geometry(), outFeat.geometry()]) out_feature.setGeometry(out_geom) out_feature.setAttributes([inID, outID, dist]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: distList.append(float(dist)) if matType != 0: mean = sum(distList) / len(distList) for i in distList: vari += (i - mean) * (i - mean) vari = math.sqrt(vari / len(distList)) out_feature = QgsFeature() out_feature.setGeometry(inFeat.geometry()) out_feature.setAttributes([inID, mean, vari, min(distList), max(distList)]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) spatialIndex = QgsSpatialIndex(source, feedback) distance = QgsDistanceArea() distance.setSourceCrs(source.sourceCrs(), context.transformContext()) distance.setEllipsoid(context.project().ellipsoid()) sumDist = 0.00 A = source.sourceExtent() A = float(A.width() * A.height()) features = source.getFeatures() count = source.featureCount() total = 100.0 / count if count else 1 for current, feat in enumerate(features): if feedback.isCanceled(): break neighbourID = spatialIndex.nearestNeighbor( feat.geometry().asPoint(), 2)[1] request = QgsFeatureRequest().setFilterFid( neighbourID).setSubsetOfAttributes([]) neighbour = next(source.getFeatures(request)) sumDist += distance.measureLine(neighbour.geometry().asPoint(), feat.geometry().asPoint()) feedback.setProgress(int(current * total)) do = float(sumDist) / count de = float(0.5 / math.sqrt(count / A)) d = float(do / de) SE = float(0.26136 / math.sqrt(count**2 / A)) zscore = float((do - de) / SE) results = {} results[self.OBSERVED_MD] = do results[self.EXPECTED_MD] = de results[self.NN_INDEX] = d results[self.POINT_COUNT] = count results[self.Z_SCORE] = zscore if output_file: data = [] data.append('Observed mean distance: ' + str(do)) data.append('Expected mean distance: ' + str(de)) data.append('Nearest neighbour index: ' + str(d)) data.append('Number of points: ' + str(count)) data.append('Z-Score: ' + str(zscore)) self.createHTML(output_file, data) results[self.OUTPUT_HTML_FILE] = output_file return results
def processAlgorithm(self, parameters, context, feedback): poly_source = self.parameterAsSource(parameters, self.POLYGONS, context) point_source = self.parameterAsSource(parameters, self.POINTS, context) weight_field = self.parameterAsString(parameters, self.WEIGHT, context) weight_field_index = -1 if weight_field: weight_field_index = point_source.fields().lookupField( weight_field) class_field = self.parameterAsString(parameters, self.CLASSFIELD, context) class_field_index = -1 if class_field: class_field_index = point_source.fields().lookupField(class_field) field_name = self.parameterAsString(parameters, self.FIELD, context) fields = poly_source.fields() if fields.lookupField(field_name) < 0: fields.append(QgsField(field_name, QVariant.Int)) field_index = fields.lookupField(field_name) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, poly_source.wkbType(), poly_source.sourceCrs()) spatialIndex = QgsSpatialIndex( point_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(poly_source.sourceCrs())), feedback) point_attribute_indices = [] if weight_field_index >= 0: point_attribute_indices.append(weight_field_index) if class_field_index >= 0: point_attribute_indices.append(class_field_index) features = poly_source.getFeatures() total = 100.0 / poly_source.featureCount() if poly_source.featureCount( ) else 0 for current, polygon_feature in enumerate(features): if feedback.isCanceled(): break count = 0 output_feature = QgsFeature() if polygon_feature.hasGeometry(): geom = polygon_feature.geometry() engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() count = 0 classes = set() points = spatialIndex.intersects(geom.boundingBox()) if len(points) > 0: request = QgsFeatureRequest().setFilterFids( points).setDestinationCrs(poly_source.sourceCrs()) request.setSubsetOfAttributes(point_attribute_indices) for point_feature in point_source.getFeatures(request): if feedback.isCanceled(): break if engine.contains( point_feature.geometry().geometry()): if weight_field_index >= 0: weight = point_feature.attributes( )[weight_field_index] try: count += float(weight) except: # Ignore fields with non-numeric values pass elif class_field_index >= 0: point_class = point_feature.attributes( )[class_field_index] if point_class not in classes: classes.add(point_class) else: count += 1 output_feature.setGeometry(geom) attrs = polygon_feature.attributes() if class_field_index >= 0: score = len(classes) else: score = count if field_index == len(attrs): attrs.append(score) else: attrs[field_index] = score output_feature.setAttributes(attrs) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def get_feats_on_bbox(layer, bbox): index = QgsSpatialIndex(layer.getFeatures()) feats_int = index.intersects(bbox) return feats_int
def processAlgorithm(self, parameters, context, feedback): if parameters[self.INPUT] == parameters[self.HUBS]: raise QgsProcessingException( self.tr('Same layer given for both hubs and spokes')) point_source = self.parameterAsSource(parameters, self.INPUT, context) if point_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) hub_source = self.parameterAsSource(parameters, self.HUBS, context) if hub_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.HUBS)) fieldName = self.parameterAsString(parameters, self.FIELD, context) units = self.UNITS[self.parameterAsEnum(parameters, self.UNIT, context)] fields = point_source.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, point_source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) index = QgsSpatialIndex( hub_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(point_source.sourceCrs(), context.transformContext()))) distance = QgsDistanceArea() distance.setSourceCrs(point_source.sourceCrs(), context.transformContext()) distance.setEllipsoid(context.project().ellipsoid()) # Scan source points, find nearest hub, and write to output file features = point_source.getFeatures() total = 100.0 / point_source.featureCount( ) if point_source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): sink.addFeature(f, QgsFeatureSink.FastInsert) continue src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next( hub_source.getFeatures(QgsFeatureRequest().setFilterFid( neighbors[0]).setSubsetOfAttributes( [fieldName], hub_source.fields()).setDestinationCrs( point_source.sourceCrs(), context.transformContext()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) if units != self.LAYER_UNITS: hub_dist_in_desired_units = distance.convertLengthMeasurement( hubDist, units) else: hub_dist_in_desired_units = hubDist attributes = f.attributes() attributes.append(ft[fieldName]) attributes.append(hub_dist_in_desired_units) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPointXY(src)) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def addTransportData(self, shapefile, startTime, epsgCode, roadTypeField, roadTypeNames, inputIdField, speedDataField=None, speedConversionFactor=None, totalAADTField=None, vAADTFields=None): ''' Adds transport data to the object, associates it with a start time and calculates disaggregated hourly mean transport QF :param shapefile: string: path to input shapefile :param startTime: datetime: time from which this data should be used :param roadTypeField: string: Attribute containing the road classification :param roadTypeNames: dict: How the main road types are identified in the shapefile {'motorway':str, 'primary_road':str, 'secondary_road':str} :param inputIdField: str: Name of shapefile field containing unique identifiers for each road segment :param speedDataField: str: shapefile attribute containing speed data (None if not available) :param speedConversionFactor: float: if speed is read from shapefile, multiply it by this factor to convert it to km/h :param totalAADTField: str: shapefile attribute containing total Annual Averaged Daily Total traffic count (total across all vechile types) (None if not available) :param vADDTflds: dict: shapefile attributes to use for each separate vehicle type's AADT Two variants allowed, one with separate fuels for LGVs and cars, and one without: Allowed keys 1: diesel_car, petrol_car, diesel_lgv, petrol_lgv, motorcycle, taxi, bus, coach, rigid, artic Allowed keys 2: total_car, total_lgv, motorcycle, taxi, bus, coach, rigid, artic :param epsgCode: int: EPSG code of shapefile :return: QgsVectorLayer: Mean hourly transport heat flux in each output area polygon ''' # Flags to identify the level of detail of the input data speedDataAvailable = False # Mean traffic speed available for each road segment # AADT is broken down into the vehicles listed in completeInputs completeInputAADTprovided = False # AADT data is broken down into the vehicles listed in modelledTypes modelledTypesAADTprovided = False self.validateInputs(startTime, shapefile, epsgCode, roadTypeField, roadTypeNames) # This is a way to look up our version of the road type, given the shapefile's version of the road type roadTypeLookup = {roadTypeNames[key]: key for key in self.roadTypes} # For all other road types that don't match the above roadTypeLookup['other'] = 'other' # Establish what speed data is available # TODO: Use speed in fuel efficiency lookup # Master list of all the fields to sample from the shapefile. Gets built up as we go along... fieldsToSample = [roadTypeField] if type(speedDataField) is str: speedDataAvailable = True try: speedConversionFactor = float(speedConversionFactor) except Exception: raise ValueError('Vehicle speed multiplier must be a number') fieldsToSample.append(speedDataField) # Establish what AADT data is available if type(totalAADTField) is str: fieldsToSample.append(totalAADTField) # Validate fields for AADT by vehicle type, if it was provided if type(vAADTFields) is dict: allowedKeys1 = self.completeInputs allowedKeys2 = self.modelledTypes missingFrom1 = list( set(allowedKeys1).difference(list(vAADTFields.keys()))) missingFrom2 = list( set(allowedKeys2).difference(list(vAADTFields.keys()))) if len(missingFrom1) == 0: completeInputAADTprovided = True elif len(missingFrom2) == 0: modelledTypesAADTprovided = True else: raise ValueError( 'The vehicle AADT field names provided are incomplete. Expected: ' + str(allowedKeys1) + ' OR ' + str(allowedKeys2) + '. Got: ' + str(list(vAADTFields.keys()))) fieldsToSample.extend(list(vAADTFields.values())) # Make a copy of the shapefile in a temp folder (we wish to change it) # Ensure the input layer has the right projection when it gets there shapefile = reprojectVectorLayer(shapefile, self.transport.templateEpsgCode) #inputLayer = loadShapeFile(shapefile) inputLayer = openShapeFileInMemory(shapefile, self.transport.templateEpsgCode, 'transport') try: # Try to delete tempfile but don't explode if fail as QGIS sometimes hangs onto them for slightly too long rmtree(os.path.dirname(shapefile)) except: pass # TODO: Explain to the logger what we are doing with the various fields # Get lookup between field names and indices fieldNames = { a.name(): i for i, a in enumerate(inputLayer.dataProvider().fields()) } # Check that the requested field names are actually present in the layer missingFields = list( set(fieldsToSample).difference(list(fieldNames.keys()))) if len(missingFields) > 0: raise ValueError( 'Some of the transport shapefile fields referenced were not found in the shapefile:' + str(missingFields)) # Calculate fuel use in each segment # TODO 2: Support a vehicle age profile (only a static profile is likely to be tractable) # TODO 1: Get this from the parameters file # For now, just assumes every vehicle was made in 2005 and looks up values from the fuel consumption object vehDate = pd.datetime.strptime('2005-01-01', '%Y-%m-%d') # Come up with a read-across between transport road types and euroclass road types # Any roads not matching these are assumed to be very minor and are omitted # TODO: Refine this treatment # Clone the output layer (populate this with [dis]aggregated data) and create spatial index outputLayer = duplicateVectorLayer( self.transport.outputLayer, targetEPSG=self.transport.templateEpsgCode) inputIndex = QgsSpatialIndex() for feat in inputLayer.getFeatures(): inputIndex.addFeature(feat) # Get translation to look up internal feature ID based on our preferred ID field t = shapefile_attributes(outputLayer)[self.transport.templateIdField] featureMapper = pd.Series(index=list(map(intOrString, t.values)), data=list(map(intOrString, t.index))) t = None # Convert road lengths and AADT data to total fuel use each day on each segment of road fuelUseDict = calculate_fuel_use( inputLayer, inputIdField, totalAADTField=totalAADTField, roadTypeField=roadTypeField, vAADTFields=vAADTFields, completeInputs=self.completeInputs, modelParams=self.modelParams, age=vehDate, fuelCon=self.fc, roadTypeLookup=roadTypeLookup, completeInputAADTprovided=completeInputAADTprovided, modelledTypesAADTprovided=modelledTypesAADTprovided) fuelUseData = fuelUseDict['fuelUse'] fuelUseNames = fuelUseDict['names'] allFuelFields = list(fuelUseNames['petrol'].values()) allFuelFields.extend(list(fuelUseNames['diesel'].values())) # Get road segment lengths inside each output polygon, along with attributes of each of these intersected segments intersectedLines = intersecting_amounts([], inputIndex, inputLayer, outputLayer, inputIdField, self.transport.templateIdField) # Add total fuel consumption fields to output layer into which fuel consumption will go] for newField in allFuelFields: outputLayer = addNewField(outputLayer, newField) # Find out where new fields reside newFieldIndices = [ get_field_index(outputLayer, fn) for fn in fuelUseData.columns ] # Refer to everything in terms of field index instead fuelUseData.columns = newFieldIndices # intersecting_amounts gives us enough information (original segment length and intersected length) # to disaggregate fuel use into each output feature, and to calculate total fuel use in each output feature areas = self.transport.getAreas() outputLayer.startEditing() fuelConsumption = pd.DataFrame( index=list(intersectedLines.keys()), columns=newFieldIndices) # Results container for each feature for outfeat_id in list(intersectedLines.keys()): fuelConsumption[:].loc[outfeat_id] = 0 if len(intersectedLines[outfeat_id]) > 0: # If there are any areas intersected by this polygon # Total fuel consumption within this output area lengths = pd.DataFrame().from_dict( intersectedLines[outfeat_id]).T proportionIntersected = lengths['amountIntersected'] / \ lengths['originalAmount'] # Get total of each fuel/vehicle combination by summing across segments in output area fuelConsumption[:].loc[outfeat_id] = fuelUseData[:].loc[lengths.index]\ .multiply(proportionIntersected, axis=0)\ .sum(axis=0, skipna=True) # Update shapefile attributes one by one. Doing it in bulk via the dataprovider /should/ work too, but doesn't seem to # Convert to kg fuel per square metre of output area too [ outputLayer.changeAttributeValue( featureMapper[outfeat_id], fi, float(fuelConsumption[fi][outfeat_id]) / float(areas[outfeat_id])) for fi in newFieldIndices ] outputLayer.commitChanges() # This output layer is a set of polygons with associated fuel use and can be treated like any other # fuel consumption input shapefile confirmedOutput = self.transport.addInput( outputLayer, startTime, allFuelFields, self.transport.templateIdField, epsgCode=epsgCode) return confirmedOutput