def create_spatial_functions(con) -> tuple[callable]:
    """
    Use of function to assign global variable con.
    Generated functions cannot have additional arguments since it is used as SQL function.
    Args:
        - con : sqlite3.connection
    """

    def st_3dlinesubstring(line_geom, start_fraction, end_fraction):

        if line_geom is None or start_fraction >= end_fraction or end_fraction > 1:
            return

        cursor = con.cursor()
        n = cursor.execute("select st_npoints(?)", (line_geom,)).fetchone()[0]
        length = cursor.execute("select st_3dlength(?)", (line_geom,)).fetchone()[0]
        start_length = start_fraction * length
        end_length = end_fraction * length
        vertices = []
        distance = 0
        start_added = False
        for i in range(1, n):
            vertex = cursor.execute(
                "select st_pointn(?, ?)",
                (
                    line_geom,
                    i,
                ),
            ).fetchone()[0]
            next_vertex = cursor.execute(
                "select st_pointn(?, ?)",
                (
                    line_geom,
                    i + 1,
                ),
            ).fetchone()[0]
            d = abs(
                cursor.execute(
                    "select st_3ddistance(?, ?)",
                    (
                        vertex,
                        next_vertex,
                    ),
                ).fetchone()[0]
            )

            x = cursor.execute("select st_x(?)", (vertex,)).fetchone()[0]
            y = cursor.execute("select st_y(?)", (vertex,)).fetchone()[0]
            z = cursor.execute("select st_z(?)", (vertex,)).fetchone()[0]

            # vertex between start and end
            if distance < end_length and distance > start_length:
                vertices.append(f"{x} {y} {z}")

            if distance + d > start_length and start_added is False:
                fraction = (start_length - distance) / d
                next_x = cursor.execute("select st_x(?)", (next_vertex,)).fetchone()[0]
                next_y = cursor.execute("select st_y(?)", (next_vertex,)).fetchone()[0]
                next_z = cursor.execute("select st_z(?)", (next_vertex,)).fetchone()[0]
                new_x = x + (next_x - x) * fraction
                new_y = y + (next_y - y) * fraction
                new_z = z + (next_z - z) * fraction
                start_vertex = [f"{new_x} {new_y} {new_z}"]
                start_added = True

            if distance + d >= end_length:
                fraction = (end_length - distance) / d
                next_x = cursor.execute("select st_x(?)", (next_vertex,)).fetchone()[0]
                next_y = cursor.execute("select st_y(?)", (next_vertex,)).fetchone()[0]
                next_z = cursor.execute("select st_z(?)", (next_vertex,)).fetchone()[0]
                new_x = x + (next_x - x) * fraction
                new_y = y + (next_y - y) * fraction
                new_z = z + (next_z - z) * fraction
                end_vertex = [f"{new_x} {new_y} {new_z}"]
                break

            distance += d

        vertices = start_vertex + vertices + end_vertex
        vertices_text = ",".join(vertices)
        geom_text = f"LINESTRING Z({vertices_text})"

        return str(geom_text)

    def st_3dlineinterpolatepoint(line_geom, fraction):

        if line_geom is None or fraction > 1:
            return

        cursor = con.cursor()
        n = cursor.execute("select st_npoints(?)", (line_geom,)).fetchone()[0]
        length = cursor.execute("select st_3dlength(?)", (line_geom,)).fetchone()[0]
        stop_length = fraction * length
        distance = 0
        for i in range(1, n):
            vertex = cursor.execute(
                "select st_pointn(?, ?)",
                (
                    line_geom,
                    i,
                ),
            ).fetchone()[0]
            next_vertex = cursor.execute(
                "select st_pointn(?, ?)",
                (
                    line_geom,
                    i + 1,
                ),
            ).fetchone()[0]
            d = abs(
                cursor.execute(
                    "select st_3ddistance(?, ?)",
                    (
                        vertex,
                        next_vertex,
                    ),
                ).fetchone()[0]
            )

            if distance + d >= stop_length:
                arc_fraction = (stop_length - distance) / d
                x = cursor.execute("select st_x(?)", (vertex,)).fetchone()[0]
                y = cursor.execute("select st_y(?)", (vertex,)).fetchone()[0]
                z = cursor.execute("select st_z(?)", (vertex,)).fetchone()[0]
                next_x = cursor.execute("select st_x(?)", (next_vertex,)).fetchone()[0]
                next_y = cursor.execute("select st_y(?)", (next_vertex,)).fetchone()[0]
                next_z = cursor.execute("select st_z(?)", (next_vertex,)).fetchone()[0]
                new_x = x + (next_x - x) * arc_fraction
                new_y = y + (next_y - y) * arc_fraction
                new_z = z + (next_z - z) * arc_fraction
                new_vertex = f"{new_x} {new_y} {new_z}"
                break

            distance += d

        geom_text = f"POINT Z({new_vertex})"

        return str(geom_text)

    return st_3dlinesubstring, st_3dlineinterpolatepoint
