I want to perform a QgsCoordinateTransform on a QgsGeometry object and do some operations on this afterwards. I am doing this in a processing script and am getting the original QgsGeometry object from a QgsSpatialIndex with flags=QgsSpatialIndex.FlagStoreFeatureGeometries set.
So basically I am doing
overlay_geom = QgsGeometry(overlay_layer_idx.geometry(id)) overlay_geom.transform(QgsCoordinateTransform(source_layer_crs, overlay_layer_crs, QgsProject.instance())) nearest_point_geom = overlay_geom.nearestPoint(source_geom) and it causes QGIS to crash with no informations provided. Tested on 3.16 and 3.28.
How can I transform my QgsGeometry object and do an operation on this afterwards?
I'd have a workaround by simply using the native reproject algorithm, but want to avoid it, because it makes the parameterAsSource input unusable and forces to use parameterAsLayer instead.
Here is the script I am actually using narrowed down to the essentials. Its working fine when both inputs have the same CRS, but will crash if not.
Edit: Just figured out it is not actually the transformation causing the crash but the next operation on the transformed geometry. And it is only crashing when reprojecting to 4326. But it does work with 4326 when both inputs have this crs and therefore no transformation is done.
# -*- coding: utf-8 -*- import processing, math from PyQt5.QtCore import QCoreApplication, QVariant from qgis.core import (QgsProject, QgsFeature, QgsProcessing, QgsSpatialIndex, QgsGeometry, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsFeatureSink, QgsFeatureRequest, QgsProcessingAlgorithm, QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSource) class TransformTest(QgsProcessingAlgorithm): SOURCE_LYR = 'SOURCE_LYR' OVERLAY_LYR = 'OVERLAY_LYR' OUTPUT = 'OUTPUT' def initAlgorithm(self, config=None): self.addParameter( QgsProcessingParameterFeatureSource( self.SOURCE_LYR, self.tr('Points'), [QgsProcessing.TypeVectorPoint])) self.addParameter( QgsProcessingParameterFeatureSource( self.OVERLAY_LYR, self.tr('Lines'), [QgsProcessing.TypeVectorLine])) self.addParameter( QgsProcessingParameterFeatureSink( self.OUTPUT, self.tr('Test'))) def processAlgorithm(self, parameters, context, feedback): source_layer = self.parameterAsSource(parameters, self.SOURCE_LYR, context) source_layer_vl = self.parameterAsLayer(parameters, self.SOURCE_LYR, context) overlay_layer = self.parameterAsSource(parameters, self.OVERLAY_LYR, context) overlay_layer_vl = self.parameterAsLayer(parameters, self.OVERLAY_LYR, context) output_layer_fields = source_layer.fields() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, output_layer_fields, 2, # LineString = 2 source_layer.sourceCrs()) total = 100.0 / source_layer.featureCount() if source_layer.featureCount() else 0 source_layer_crs = QgsCoordinateReferenceSystem(source_layer_vl.crs().authid()) overlay_layer_crs = QgsCoordinateReferenceSystem(overlay_layer_vl.crs().authid()) overlay_layer_idx = QgsSpatialIndex(overlay_layer.getFeatures(), flags=QgsSpatialIndex.FlagStoreFeatureGeometries) for current, source_feat in enumerate(source_layer.getFeatures()): if feedback.isCanceled(): break nearest_lines = overlay_layer_idx.nearestNeighbor(source_feat.geometry().centroid().asPoint(), neighbors = 1, maxDistance = -1) for nearest_line_id in nearest_lines: new_feat = QgsFeature(output_layer_fields) attridx = 0 for attr in source_feat.attributes(): new_feat[attridx] = attr attridx += 1 nearest_line_geom = overlay_layer_idx.geometry(nearest_line_id) if source_layer_vl.sourceCrs() != overlay_layer_vl.sourceCrs(): feedback.setProgressText('Doing transform now') nearest_line_geom.transform(QgsCoordinateTransform(source_layer_crs, overlay_layer_crs, context.transformContext())) feedback.setProgressText('You see me, so the transform did not cause the crash') feedback.setProgressText('Will crash in 3.. 2.. 1.. go:') point_on_nearest_line = nearest_line_geom.nearestPoint(source_feat.geometry().centroid()) feedback.setProgressText('You will not see me, because it already crashed') sqrDist, minDistPoint, afterVertex, leftOf = nearest_line_geom.closestSegmentWithContext(point_on_nearest_line.asPoint(),1) vertexOnSegment2 = nearest_line_geom.vertexAt(afterVertex) vertexOnSegment1 = nearest_line_geom.vertexAt(afterVertex - 1) segmentAngle = math.atan2(vertexOnSegment2.x() - vertexOnSegment1.x(), vertexOnSegment2.y() - vertexOnSegment1.y()) segmentAngleDegree = math.degrees(segmentAngle) if segmentAngle > 0 else math.degrees(segmentAngle) + 180 segmentgeom = QgsGeometry.fromPolyline([vertexOnSegment1,vertexOnSegment2]) perpendicularLinePoint1 = point_on_nearest_line.asPoint().project(10,segmentAngleDegree+90) perpendicularLinePoint2 = point_on_nearest_line.asPoint().project(10,segmentAngleDegree-90) perpendicularLineGeom = QgsGeometry.fromPolylineXY([perpendicularLinePoint1,perpendicularLinePoint2]) new_feat.setGeometry(perpendicularLineGeom) sink.addFeature(new_feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id} def tr(self, string): return QCoreApplication.translate('Processing', string) def createInstance(self): return TransformTest() def name(self): return 'TransformTest' def displayName(self): return self.tr('Transform Test') def group(self): return self.tr(self.groupId()) def groupId(self): return 'Test' def shortHelpString(self): return self.tr('Testing Transform')