Ejemplo n.º 1
0
    def calcQneatInterpolation(self, iso_pointcloud_featurelist, resolution,
                               interpolation_raster_path):
        #prepare spatial index
        uri = 'PointM?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&key=vertex_id&index=yes'.format(
            self.AnalysisCrs.authid())

        mIsoPointcloud = QgsVectorLayer(uri, "mIsoPointcloud_layer", "memory")
        mIsoPointcloud_provider = mIsoPointcloud.dataProvider()
        mIsoPointcloud_provider.addFeatures(iso_pointcloud_featurelist,
                                            QgsFeatureSink.FastInsert)

        #implement spatial index for lines (closest line, etc...)
        spt_idx = QgsSpatialIndex(
            mIsoPointcloud.getFeatures(QgsFeatureRequest()), self.feedback)

        #prepare numpy coordinate grids
        NoData_value = -9999
        raster_rectangle = mIsoPointcloud.extent()

        #top left point
        xmin = raster_rectangle.xMinimum()
        ymin = raster_rectangle.yMinimum()
        xmax = raster_rectangle.xMaximum()
        ymax = raster_rectangle.yMaximum()

        cols = int((xmax - xmin) / resolution)
        rows = int((ymax - ymin) / resolution)

        output_interpolation_raster = gdal.GetDriverByName('GTiff').Create(
            interpolation_raster_path, cols, rows, 1, gdal.GDT_Float64)
        output_interpolation_raster.SetGeoTransform(
            (xmin, resolution, 0, ymax, 0, -resolution))

        band = output_interpolation_raster.GetRasterBand(1)
        band.SetNoDataValue(NoData_value)

        #initialize zero array with 2 dimensions (according to rows and cols)
        raster_data = zeros(shape=(rows, cols))

        #compute raster cell MIDpoints
        x_pos = linspace(xmin + (resolution / 2), xmax - (resolution / 2),
                         raster_data.shape[1])
        y_pos = linspace(ymax - (resolution / 2), ymin + (resolution / 2),
                         raster_data.shape[0])
        x_grid, y_grid = meshgrid(x_pos, y_pos)

        self.feedback.pushInfo(
            '[QNEAT3Network][calcQneatInterpolation] Beginning with interpolation'
        )
        total_work = rows * cols
        counter = 0

        self.feedback.pushInfo(
            '[QNEAT3Network][calcQneatInterpolation] Total workload: {} cells'.
            format(total_work))
        self.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 = mIsoPointcloud.getFeature(nearest_vertex_fid)

                nearest_vertex = self.network.vertex(
                    nearest_feature['vertex_id'])

                edges = nearest_vertex.incomingEdges(
                ) + nearest_vertex.outgoingEdges()

                vertex_found = False
                nearest_counter = 2
                while vertex_found == False:
                    n_nearest_feature_fid = spt_idx.nearestNeighbor(
                        current_pixel_midpoint,
                        nearest_counter)[nearest_counter - 1]
                    n_nearest_feature = mIsoPointcloud.getFeature(
                        n_nearest_feature_fid)
                    n_nearest_vertex_id = n_nearest_feature['vertex_id']

                    for edge_id in edges:
                        from_vertex_id = self.network.edge(
                            edge_id).fromVertex()
                        to_vertex_id = self.network.edge(edge_id).toVertex()

                        if n_nearest_vertex_id == from_vertex_id:
                            vertex_found = True
                            vertex_type = "from_vertex"
                            from_point = n_nearest_feature.geometry().asPoint()
                            from_vertex_cost = n_nearest_feature['cost']
                        if n_nearest_vertex_id == to_vertex_id:
                            vertex_found = True
                            vertex_type = "to_vertex"
                            to_point = n_nearest_feature.geometry().asPoint()
                            to_vertex_cost = n_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)
                    #self.feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment))
                    #self.feedback.pushInfo("dist_on_edge = {}".format(dist_edge))
                    #self.feedback.pushInfo("cost = {}".format(from_vertex_cost))
                    pixel_cost = from_vertex_cost + dist_edge + dist_to_segment
                    raster_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)
                    #self.feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment))
                    #self.feedback.pushInfo("dist_on_edge = {}".format(dist_edge))
                    #self.feedback.pushInfo("cost = {}".format(from_vertex_cost))
                    pixel_cost = to_vertex_cost + dist_edge + dist_to_segment
                    raster_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:
                    self.feedback.pushInfo(
                        "[QNEAT3Network][calcQneatInterpolation] Interpolated {} cells..."
                        .format(counter))
                self.feedback.setProgress((counter / total_work) * 100)

        band.WriteArray(raster_data)
        outRasterSRS = osr.SpatialReference()
        outRasterSRS.ImportFromWkt(self.AnalysisCrs.toWkt())
        output_interpolation_raster.SetProjection(outRasterSRS.ExportToWkt())
        band.FlushCache()
Ejemplo n.º 2
0
    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

        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)

        if analysisCrs.isGeographic():
            raise QgsProcessingException(
                'QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!'
            )

        if analysisCrs != startPoint.sourceCrs():
            raise QgsProcessingException(
                'QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).'
            )

        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],
                                             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
Ejemplo n.º 3
0
 def topology(self):
     """Add to nearest segments each vertex in a polygon layer."""
     threshold = self.dist_thr  # Distance threshold to create nodes
     dup_thr = self.dup_thr
     straight_thr = self.straight_thr
     tp = 0
     td = 0
     if log.app_level <= logging.DEBUG:
         debshp = DebugWriter("debug_topology.shp", self)
     geometries = {
         f.id(): QgsGeometry(f.geometry())
         for f in self.getFeatures()
     }
     index = self.get_index()
     to_change = {}
     nodes = set()
     pbar = self.get_progressbar(_("Topology"), len(geometries))
     for (gid, geom) in geometries.items():
         if geom.area() < config.min_area:
             continue
         for point in frozenset(Geometry.get_outer_vertices(geom)):
             if point not in nodes:
                 area_of_candidates = Point(point).boundingBox(threshold)
                 fids = index.intersects(area_of_candidates)
                 for fid in fids:
                     g = QgsGeometry(geometries[fid])
                     (p, ndx, ndxa, ndxb, dist_v) = g.closestVertex(point)
                     (dist_s, closest,
                      vertex) = g.closestSegmentWithContext(point)[:3]
                     va = Point(g.vertexAt(ndxa))
                     vb = Point(g.vertexAt(ndxb))
                     note = ""
                     if dist_v == 0:
                         dist_a = va.sqrDist(point)
                         dist_b = vb.sqrDist(point)
                         if dist_a < dup_thr**2:
                             g.deleteVertex(ndxa)
                             note = "dupe refused by isGeosValid"
                             if Geometry.is_valid(g):
                                 note = "Merge dup. %.10f %.5f,%.5f->%.5f,%.5f" % (
                                     dist_a,
                                     va.x(),
                                     va.y(),
                                     point.x(),
                                     point.y(),
                                 )
                                 nodes.add(p)
                                 nodes.add(va)
                                 td += 1
                         if dist_b < dup_thr**2:
                             g.deleteVertex(ndxb)
                             note = "dupe refused by isGeosValid"
                             if Geometry.is_valid(g):
                                 note = "Merge dup. %.10f %.5f,%.5f->%.5f,%.5f" % (
                                     dist_b,
                                     vb.x(),
                                     vb.y(),
                                     point.x(),
                                     point.y(),
                                 )
                                 nodes.add(p)
                                 nodes.add(vb)
                                 td += 1
                     elif dist_v < dup_thr**2:
                         g.moveVertex(point.x(), point.y(), ndx)
                         note = "dupe refused by isGeosValid"
                         if Geometry.is_valid(g):
                             note = "Merge dup. %.10f %.5f,%.5f->%.5f,%.5f" % (
                                 dist_v,
                                 p.x(),
                                 p.y(),
                                 point.x(),
                                 point.y(),
                             )
                             nodes.add(p)
                             td += 1
                     elif (dist_s < threshold**2 and closest != va
                           and closest != vb):
                         va = Point(g.vertexAt(vertex))
                         vb = Point(g.vertexAt(vertex - 1))
                         angle = abs(point.azimuth(va) - point.azimuth(vb))
                         note = "Topo refused by angle: %.2f" % angle
                         if abs(180 - angle) <= straight_thr:
                             note = "Topo refused by insertVertex"
                             if g.insertVertex(point.x(), point.y(),
                                               vertex):
                                 note = "Topo refused by isGeosValid"
                                 if Geometry.is_valid(g):
                                     note = "Add topo %.6f %.5f,%.5f" % (
                                         dist_s,
                                         point.x(),
                                         point.y(),
                                     )
                                     tp += 1
                     if note.startswith("Merge") or note.startswith("Add"):
                         to_change[fid] = g
                         geometries[fid] = g
                     if note and log.app_level <= logging.DEBUG:
                         debshp.add_point(point, note)
         if len(to_change) > BUFFER_SIZE:
             self.writer.changeGeometryValues(to_change)
             to_change = {}
         pbar.update()
     pbar.close()
     if len(to_change) > 0:
         self.writer.changeGeometryValues(to_change)
     if td:
         log.debug(_("Merged %d close vertices in the '%s' layer"), td,
                   self.name())
         report.values["vertex_close_" + self.name()] = td
     if tp:
         log.debug(_("Created %d topological points in the '%s' layer"), tp,
                   self.name())
         report.values["vertex_topo_" + self.name()] = tp
def split_line_at_points(
    polyline_input,
    point_features,
    point_feature_id_field="id",
    start_node_id=None,
    end_node_id=None,
):
    """Split line at points

    Args:
        polyline (QgsPolyline):
        point_features (iteratable object of QgsFeature or list of dictonaries with id and geometry ('geom'):
        point_feature_id_field (str): fieldname if the id field of the point_features
        start_node_id (str or int): node id of point at begin of polyline
        end_node_id (str or int): node id of point at end of polyline

    Returns:
         (list of dict): Splitted polyline into parts as dictonary with:
                geom: Polyline geometry
                start_node_id: id of node at starting point of line
                end_node_id: id of node at end point of line
                distance at line: is distance of original line at the begin of this line part

    """

    snap_points = []

    source_crs = QgsCoordinateReferenceSystem(4326)
    dest_crs = QgsCoordinateReferenceSystem(3857)

    transform = QgsCoordinateTransform(source_crs, dest_crs,
                                       QgsProject.instance())
    transform_back = QgsCoordinateTransform(dest_crs, source_crs,
                                            QgsProject.instance())

    polyline = QgsGeometry(polyline_input)
    polyline.transform(transform)

    for point in point_features:
        if type(point) == QgsFeature:
            point = {
                point_feature_id_field: point[point_feature_id_field],
                "geom": point.geometry(),
            }

        if hasattr(point["geom"], "asPoint"):
            geom = point["geom"].asPoint()
        else:
            geom = point["geom"]

        geom = QgsGeometry.fromPointXY(geom)
        geom.transform(transform)
        geom = geom.asPoint()

        closest_seg = polyline.closestSegmentWithContext(geom)
        # get nearest point (related to the point) on the line
        point_on_line = closest_seg[1]
        point_geom_on_line = QgsPoint(point_on_line[0], point_on_line[1])

        # get nr of vertex (at the end of the line where the closest point is
        # found
        end_vertex_nr = closest_seg[2]
        start_vertex_nr = end_vertex_nr - 1

        p1 = polyline.asPolyline()[start_vertex_nr]  # first vertex
        p2 = point_geom_on_line

        distance_on_subline = math.hypot(p2.x() - p1.x(), p2.y() - p1.y())

        snap_points.append((
            start_vertex_nr,
            distance_on_subline,
            point_geom_on_line,
            point[point_feature_id_field],
        ))

    # order on vertex nr and if same vertex nr on distance
    snap_points.sort(key=lambda x: x[1])
    snap_points.sort(key=lambda x: x[0])

    # create line parts
    line_parts = []
    line_points = []
    start_point_id = start_node_id
    total_line_distance = 0

    for i, vertex in enumerate(polyline.asPolyline()):
        line_points.append(QgsPoint(vertex[0], vertex[1]))

        # get points after this vertex
        split_points_on_segment = [p for p in snap_points if p[0] == i]
        for point in split_points_on_segment:
            # only add another point if point is not equal to last vertex
            # todo: throw error when at begin or end vertex of original line?
            # todo: what to do of multiple points on same location?
            line_points.append(point[2])
            geom = QgsGeometry.fromPolyline(line_points)

            # length, unit_type = d.convertMeasurement(
            #     d.computeDistance(line_points),
            #     Qgis.Meters, Qgis.Meters, False)  # Qgis.Degrees

            length = geom.length()

            # add line parts
            line_parts.append({
                "geom": geom.transform(transform_back),
                "start_point_id": start_point_id,
                "end_point_id": point[3],
                "distance_at_line": total_line_distance,
                "length": length,
            })
            # create starting point of new line
            line_points = [point[2]]
            start_point_id = point[3]
            total_line_distance += length

    # last part of the line
    geom = QgsGeometry.fromPolyline(line_points)
    length = geom.length()
    # length, something = d.convertMeasurement(
    #     d.computeDistance(line_points),
    #     Qgis.Meters, Qgis.Meters, False)

    line_parts.append({
        "geom": geom,
        "start_point_id": start_point_id,
        "end_point_id": end_node_id,
        "distance_at_line": total_line_distance,
        "length": length,
    })

    return line_parts