Profile interpolieren

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
bfillitz1
User
Beiträge: 4
Registriert: Freitag 17. November 2023, 11:45

Hi,

Ich bin relativ neu in python und versuche mich darin. Ich habe folgendes Problem. Ich habe in einer txt Datei eine Masse an Profilen mit folgender Struktur:

Code: Alles auswählen

CROSS_SECTION {
	name                     = 390037
	distance_coord           = 0.949
	left_point_global_coords = (23891.970, 213016.040, 880.280)
	orientation_angle        = 324.347960
	main_channel_range       = (29.869, 90.058)
	bottom_range             = (35.336,85.860)
	friction_ranges          = ((0.000,35.33600), (35.336,85.8600), (85.860,107.74400))
	friction_coefficients    = (18,25,18)
	node_coords              = ((0.000,880.280),(3.605,880.210),(6.191,879.790),(11.609,876.350),(21.613,876.500),(29.869,876.300),(32.011,875.210),(33.538,874.330),(34.606,873.650),(35.336,872.410),(38.125,872.770),(40.295,872.760),(42.740,872.880),(45.046,873.220),(49.633,873.270),(53.704,873.670),(67.757,874.340),(77.065,873.950),(81.889,873.880),(85.860,873.800),(87.572,875.060),(90.058,876.450),(97.332,876.380),(107.744,881.300))
	theta_critic             = -1.0
	SOIL_DEF {
		index = 2
		range = (35.336,85.860)
	}
}
Die Profile sind alle gleich aufgebaut von der Struktur her.
Ich versuche oder will es schaffen, dass das Skript untersucht welche node_coords innerhalb der bottom_range liegen und dann auch das Profil anzeigt welches die meisten Punkte innerhalb der range hat. (das funktioniert auch schon)

Wo ich scheitere ist, dass ich dann gerne hätte, dass er ausgehend von dem mit den meisten Punkten innerhalb der range, die anderen Profile so adaptiert, dass diese dann auch innerhalb der bottom_range Punkte hinzugefügt bekommen (normal verteilt), sodass schlussendlich dann alle Profile die gleiche Punktezahl in der bottom_range aufweisen. (die Anzahl von dem vorher rausgefiltertem mit den meisten Punkten)

Aktuell habe ich diesen Code. Da funktioniert das mit den Punkten einfügen aber noch garnicht. kann mir da jemand helfen?
Vielen Dank

Code: Alles auswählen

 import re

def count_points_within_range(node_coords, bottom_range):
    count = 0
    for x, _ in node_coords:
        if bottom_range[0] <= x <= bottom_range[1]:
            count += 1
    return count

def distribute_points_evenly(start, end, existing_points, total_points):
    points = set(existing_points)
    while len(points) < total_points:
        step = (end - start) / (total_points - 1)
        new_points = [start + i * step for i in range(total_points) if start + i * step not in points]
        points.update(new_points)
    return sorted(list(points))

def extract_data_from_profile(profile_text):
    # Angepasster regulärer Ausdruck, der auch negative Zahlen erfasst
    node_coords_match = re.search(r'node_coords\s+=\s+\((((-\d+|\d+)\.\d+,(?:-\d+|\d+)\.\d+),?\s*)+\)', profile_text)
    bottom_range_match = re.search(r'bottom_range\s+=\s+\(((-\d+|\d+)\.\d+,(-\d+|\d+)\.\d+)\)', profile_text)

    if node_coords_match and bottom_range_match:
        node_coords_str = node_coords_match.group(1)
        node_coords_raw = re.findall(r'\(((-\d+|\d+)\.\d+,(?:-\d+|\d+)\.\d+)\)', node_coords_str)
        node_coords = [(float(x), float(z)) for x, z in node_coords_raw]

        bottom_range = tuple(map(float, bottom_range_match.groups()))
        return node_coords, bottom_range
    else:
        return None, None

def add_points_to_profile(node_coords, bottom_range, max_points):
    existing_points_in_range = [coord for coord in node_coords if bottom_range[0] <= coord[0] <= bottom_range[1]]
    num_existing_points = len(existing_points_in_range)

    if num_existing_points < max_points:
        # Bestimme die Anzahl der hinzuzufügenden Punkte
        points_to_add = max_points - num_existing_points
        new_points = []

        # Berechne die Positionen für die neuen Punkte
        for i in range(1, points_to_add + 1):
            x = bottom_range[0] + (bottom_range[1] - bottom_range[0]) * i / (points_to_add + 1)
            # Finde den nächstgelegenen z-Wert zu diesem x-Wert
            _, nearest_z = min(existing_points_in_range, key=lambda coord: abs(coord[0] - x))
            new_points.append((x, nearest_z))

        # Füge die neuen Punkte zur Liste hinzu
        node_coords.extend(new_points)
        node_coords.sort(key=lambda coord: coord[0])

    return node_coords


def modify_profiles(content, max_points):
    modified_profiles = []
    profiles = content.split('CROSS_SECTION')[1:]  # Trenne die Profile

    for profile_text in profiles:
        header, rest = profile_text.split('node_coords', 1)
        coords_text, footer = rest.split(')', 1)
        node_coords, bottom_range = extract_data_from_profile('node_coords' + coords_text + ')')

        if node_coords and bottom_range:
            # Füge Punkte hinzu und erhalte die modifizierten node_coords
            modified_node_coords = add_points_to_profile(node_coords, bottom_range, max_points)

            # Erstelle den aktualisierten node_coords Teil als String
            updated_coords_str = "node_coords              = " + str(modified_node_coords).replace("'", "").replace(",", ", ")

            # Rekonstruiere das Profil
            modified_profile = header + updated_coords_str + ')' + footer
        else:
            modified_profile = profile_text

        modified_profiles.append('CROSS_SECTION ' + modified_profile)

    return ''.join(modified_profiles)


# Finden des Profils mit den meisten Punkten innerhalb des bottom_range
def find_profile_with_most_points(file_path):
    with open(file_path, 'r') as file:
        content = file.read()

    profiles = content.split('CROSS_SECTION')[1:]  # Profile trennen
    max_points = 0

    for profile in profiles:
        node_coords, bottom_range = extract_data_from_profile(profile)
        if node_coords and bottom_range:
            count = count_points_within_range(node_coords, bottom_range)
            if count > max_points:
                max_points = count

    return max_points

# Hauptteil des Skriptes
file_path = 'C:/Users/xxx/Desktop/Profile_Anpassen/TM-P_06_AZ.txt'
max_points = find_profile_with_most_points(file_path)

with open(file_path, 'r') as file:
    content = file.read()

modified_content = modify_profiles(content, max_points)

new_file_path = 'C:/Users/xxx/Desktop/Profile_Anpassen/TM-P_06_AZ_modified.txt'
with open(new_file_path, 'w') as new_file:
    new_file.write(modified_content)
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was stellen denn diese Node-Coords dar? Ist das ein Polygon, oder eine Punktemenge? Wenn Polygon, sollen die neuen Punkte auf dem Polygon liegen? Wenn Punktemenge, sollen die neuen Punkte innerhalb der konvexen Huelle der bestehenden Punkte liegen? Oder welches Kriterium gibt es da?
bfillitz1
User
Beiträge: 4
Registriert: Freitag 17. November 2023, 11:45

node_coords sind Punkte (X-Wert, Z-Wert) die zusammen die Profillinie bilden. Ja die neuen Punkte sollten innerhalb der bottom_rang (ist ein bestimmter Bereich auf der Profillinie) liegen.
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was heisst das, die bilden die Profillinie. Ist das ein Polygon, in dem die Puntke nacheinander kommen? Oder ist das eine Punktemenge? Und was ist dieser bottom_range, einfach ein Rechteck, das impzilit im Nullpunkt beginnt?
bfillitz1
User
Beiträge: 4
Registriert: Freitag 17. November 2023, 11:45

Es wird von punkt zu punkt eine linie gezogen und bildet so z.b. einen Geländequerschnitt.

https://www.researchgate.net/figure/Cal ... _221918475

die Bottom_range ist ein Bereich der in auf diesen Linien Liegt und eine Bandbreite von - bis angibt.
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das hier interpoliert zumindest mal linear mehr Punkte, so das die resultierende Anzahl gleich ist.

Code: Alles auswählen

import random
import pprint
import matplotlib.pyplot as plt
import numpy as np


CROSS_SECTION = {
	"name"                     : 390037,
	"distance_coord"           : 0.949,
	"left_point_global_coords" : (23891.970, 213016.040, 880.280),
	"orientation_angle"        : 324.347960,
	"main_channel_range"       : (29.869, 90.058),
	"bottom_range"             : (35.336,85.860),
	"friction_ranges"          : ((0.000,35.33600), (35.336,85.8600), (85.860,107.74400)),
	"friction_coefficients"    : (18,25,18),
	"node_coords"              : ((0.000,880.280),(3.605,880.210),(6.191,879.790),(11.609,876.350),(21.613,876.500),(29.869,876.300),(32.011,875.210),(33.538,874.330),(34.606,873.650),(35.336,872.410),(38.125,872.770),(40.295,872.760),(42.740,872.880),(45.046,873.220),(49.633,873.270),(53.704,873.670),(67.757,874.340),(77.065,873.950),(81.889,873.880),(85.860,873.800),(87.572,875.060),(90.058,876.450),(97.332,876.380),(107.744,881.300)),
	"theta_critic"             : -1.0,
	"SOIL_DEF": {
		"index" : 2,
		"range" : (35.336,85.860),
	}
}

def filter_coords_by_range(coords, range_):
    range_low, range_high = range_
    return [(x, y) for x, y in coords if range_low <= x <= range_high]


def random_coord_selection(coords, removed_points=5):
    assert len(coords) >= removed_points + 2
    new_coords = list(coords)
    for _ in range(removed_points):
        removed_index = random.randint(1, len(new_coords) - 2) # exclude left/rightmost
        new_coords = new_coords[:removed_index] + new_coords[removed_index+1:]
    return new_coords


def sample_point(left, right):
    lx, ly = left
    rx, ry = right
    # simple linear interpolation, adapt for other distribution
    return (lx + rx) / 2, (ly + ry) / 2


def interpolate_coords(coords, total_length):
    coords = list(coords) # copy, because we modify in place
    while len(coords) < total_length:
        _, longest_segment_index = max([((p2[0] - p1[0]), i) for i, (p1, p2) in enumerate(zip(coords[:-1], coords[1:]))])
        left, right = coords[longest_segment_index], coords[longest_segment_index + 1]
        middlepoint = sample_point(left, right)
        coords.insert(longest_segment_index + 1, middlepoint)
    return coords


def main():
    pprint.pprint(CROSS_SECTION)

    filtered_coords = filter_coords_by_range(CROSS_SECTION["node_coords"], CROSS_SECTION["bottom_range"])
    coords_with_fewer_entries = random_coord_selection(filtered_coords)
    pprint.pprint(filtered_coords)
    pprint.pprint(coords_with_fewer_entries)
    interpolated_coords = interpolate_coords(coords_with_fewer_entries, len(filtered_coords))
    pprint.pprint(interpolated_coords)

    filtered_coords = np.array(filtered_coords)
    coords_with_fewer_entries = np.array(coords_with_fewer_entries)
    interpolated_coords = np.array(interpolated_coords)

    plt.plot(filtered_coords[:, 0], filtered_coords[:, 1], label='filter_coords')
    plt.plot(coords_with_fewer_entries[:, 0], coords_with_fewer_entries[:, 1], label='coords_with_fewer_entries')
    plt.plot(interpolated_coords[:, 0], interpolated_coords[:, 1], label='interpolated_coords')
    plt.legend()
    plt.show()

# main guard
if __name__ == '__main__':
    main()
Dein Kriterium "normal verteilt" ist mir in diesem Zusammenhang unklar.
bfillitz1
User
Beiträge: 4
Registriert: Freitag 17. November 2023, 11:45

ok. super danke dir schonmal!!
lg
Antworten