pyCreateTh

This commit is contained in:
Alex38Lyon
2025-06-25 21:57:32 +02:00
parent 9d2e7cef5c
commit 7ef9405aed
5 changed files with 163 additions and 42 deletions
+1 -1
View File
@@ -79,7 +79,7 @@ endsurvey
thconfigTemplate = """ thconfigTemplate = """
source "{th_file}" source "{th_file}"
layout minimal layout minimal
scale 1 {scale} scale 1 100
endlayout endlayout
select {selector} select {selector}
+160 -39
View File
@@ -20,19 +20,24 @@ Version 2025 06 16 : Création fonction create_th_folders
En cours : En cours :
- créer fonction wall shot pour faire habillage des th2 files, les jointures...
- reprendre les options en ligne de commande, tester, documenter - reprendre les options en ligne de commande, tester, documenter
- trouver une solution pour les teams et les clubs manquants - trouver une solution pour les teams et les clubs manquants
- tester la nouvelle version de DAT (CORRECTION2 et suivants) - tester la nouvelle version de DAT (CORRECTION2 et suivants)
- comparer résultats Therion - Compass (Stat, kml, etc....) - comparer résultats Therion - Compass (Stat, kml, etc....)
- intégrer .tro files d'après XRo - intégrer .tro files d'après XRo
- ajouter codes pour lat/long - ajouter codes pour lat/long
- créer fonction wall shot pour faire habillage des th2 files, les jointures...
- traiter les splays pour les LRUD
- traiter les series à 1 et 2 stations
""" """
Version ="2025.06.24" Version = "2025.06.25"
################################################################################################# #################################################################################################
################################################################################################# #################################################################################################
@@ -299,7 +304,7 @@ def convert_to_line_polaire_df(df_lines):
# Calcul de la longueur et de l'azimut # Calcul de la longueur et de l'azimut
length = np.hypot(dx, dy) length = np.hypot(dx, dy)
azimut = (np.degrees(np.arctan2(dy, dx))) % 360 azimut = (np.degrees(np.arctan2(dx, dy))) % 360
if "group_id" in df_lines.columns: if "group_id" in df_lines.columns:
df_polaire = pd.DataFrame({ df_polaire = pd.DataFrame({
@@ -403,6 +408,7 @@ def assign_groups_and_ranks(df_lines):
used_edges = set() used_edges = set()
results = [] results = []
equates = [] # Liste des (group_id, start_point, end_point)
group_id = 0 group_id = 0
def walk_path(u, prev=None): def walk_path(u, prev=None):
@@ -410,7 +416,7 @@ def assign_groups_and_ranks(df_lines):
current = u current = u
while True: while True:
neighbors = [n for n in G.neighbors(current) if n != prev] neighbors = [n for n in G.neighbors(current) if n != prev]
if len(neighbors) != 1: # fin de chemin : cul-de-sac ou embranchement if len(neighbors) != 1:
break break
next_node = neighbors[0] next_node = neighbors[0]
edge = tuple(sorted((current, next_node))) edge = tuple(sorted((current, next_node)))
@@ -422,7 +428,6 @@ def assign_groups_and_ranks(df_lines):
current = next_node current = next_node
return path return path
# Départ depuis toutes les feuilles ou nœuds d'ordre >2 (embranchements)
start_nodes = [n for n in G.nodes if G.degree(n) != 2] start_nodes = [n for n in G.nodes if G.degree(n) != 2]
for node in start_nodes: for node in start_nodes:
@@ -432,6 +437,7 @@ def assign_groups_and_ranks(df_lines):
continue continue
used_edges.add(edge) used_edges.add(edge)
path = [(node, neighbor)] + walk_path(neighbor, node) path = [(node, neighbor)] + walk_path(neighbor, node)
for rank, (n1, n2) in enumerate(path): for rank, (n1, n2) in enumerate(path):
match = df_lines[(df_lines["name1"] == n1) & (df_lines["name2"] == n2)] match = df_lines[(df_lines["name1"] == n1) & (df_lines["name2"] == n2)]
if match.empty: if match.empty:
@@ -441,33 +447,153 @@ def assign_groups_and_ranks(df_lines):
row["group_id"] = group_id row["group_id"] = group_id
row["rank_in_group"] = rank row["rank_in_group"] = rank
results.append(row) results.append(row)
if rank == 0:
start_point = n1
end_point = path[-1][1] if path else start_point
equates.append((group_id, str(start_point), str(end_point)))
group_id += 1 group_id += 1
return pd.DataFrame(results) # Création du DataFrame principal
df_result = pd.DataFrame(results)
# Création du DataFrame equates
df_equates = pd.DataFrame(equates, columns=["group_id", "start_point", "end_point"])
df_equates["group_id"] = df_equates["group_id"].astype(int)
df_equates["start_point"] = df_equates["start_point"].astype(str)
df_equates["end_point"] = df_equates["end_point"].astype(str)
# Ajout de la colonne max_rank
max_ranks = df_result.groupby("group_id")["rank_in_group"].max().reset_index()
max_ranks.rename(columns={"rank_in_group": "max_rank"}, inplace=True)
max_ranks["max_rank"] = max_ranks["max_rank"].astype(int)
df_equates = df_equates.merge(max_ranks, on="group_id", how="left")
# Ajout de la colonne start_group (raccord entre start_point <-> end_point d'un autre groupe)
end_to_group = df_equates[["end_point", "group_id"]].copy()
end_to_group.rename(columns={"end_point": "start_point", "group_id": "start_group"}, inplace=True)
end_to_group["start_point"] = end_to_group["start_point"].astype(str)
df_equates = df_equates.merge(end_to_group, on="start_point", how="left")
# Remplacer les NaN dans start_group par 0 (entier)
df_equates["start_group"] = df_equates["start_group"].fillna(0).astype(int)
return df_result, df_equates
#################################################################################################
def add_start_end_splays(df_splays_complet, df_equates):
df_splays_new = df_splays_complet.copy()
# print(f"\n df_lines_polaire: {len(df_splays_new)}")
# print(df_splays_new)
for _, row in df_equates.iterrows():
group_id = row["group_id"]
end_point = row["end_point"]
start_point = row["start_point"]
start_group = row["start_group"]
# Vérifie si le end_point est déjà dans les splays
mask = (df_splays_complet["name1"] == end_point) & (df_splays_complet["group_id"] == group_id)
if not mask.any():
# Trouver un splay existant du même groupe pour copier la structure
splay_example = df_splays_complet[df_splays_complet["name1"] == end_point].copy()
if not splay_example.empty:
splay_example["group_id"] = group_id
splay_example["rank_in_group"] = row["max_rank"] + 1
ref_row = df_splays_complet[
(df_splays_complet["group_id"] == group_id) &
(df_splays_complet["rank_in_group"] == row["max_rank"] - 1)
]
if not ref_row.empty:
splay_example["longueur_ref"] = ref_row.iloc[0]["longueur_ref"]
splay_example["bissectrice"] = ref_row.iloc[0]["bissectrice"]
splay_example = splay_example.drop_duplicates()
df_splays_new = pd.concat([df_splays_new, splay_example], ignore_index=True)
# print(f"\n splay_example end add: {len(splay_example)}")
# print(splay_example)
# Vérifie si le end_point est déjà dans les splays
mask = (df_splays_complet["name1"] == start_point) & (df_splays_complet["group_id"] == start_group)
if not mask.any():
# Trouver un splay existant du même groupe pour copier la structure
splay_example = df_splays_complet[df_splays_complet["name1"] == start_point].copy()
if not splay_example.empty:
splay_example["group_id"] = group_id
splay_example["rank_in_group"] = 0
ref_row = df_splays_complet[
(df_splays_complet["group_id"] == start_group) &
(df_splays_complet["rank_in_group"] == 0)
]
if not ref_row.empty:
splay_example["longueur_ref"] = ref_row.iloc[0]["longueur_ref"]
splay_example["bissectrice"] = ref_row.iloc[0]["bissectrice"]
splay_example = splay_example.drop_duplicates()
df_splays_new = pd.concat([df_splays_new, splay_example], ignore_index=True)
# print(f"\n splay_example start add : {len(splay_example)}")
# print(splay_example)
else:
# Aucun splay existant pour ce group_id : on ignore ou on crée un modèle vide
print(f"Aucun modèle de splay pour group_id {group_id} — point {end_point} ignoré.")
return df_splays_new
################################################################################################# #################################################################################################
def wall_construction(df_stations, df_lines, df_splays, x_min, x_max, y_min, y_max): def wall_construction(df_stations, df_lines, df_splays, x_min, x_max, y_min, y_max):
th2_walls=[]
_list = ""
# pd.set_option('display.max_rows', None)
# pd.set_option('display.max_columns', None)
# pd.set_option('display.width', None)
# pd.set_option('display.max_colwidth', None)
if len(df_lines) <= 2:
return th2_walls, 0, 0, 0, 0
df_lines = assign_groups_and_ranks(df_lines) df_lines, df_equates = assign_groups_and_ranks(df_lines)
# Conversion en polaire # Conversion en polaire
df_lines_polaire = convert_to_line_polaire_df(df_lines) df_lines_polaire = convert_to_line_polaire_df(df_lines)
df_splays_polaire = convert_to_line_polaire_df(df_splays) df_splays_polaire = convert_to_line_polaire_df(df_splays)
df_temp = df_lines_polaire.copy()
df_temp['rank_in_group_prev'] = df_temp['rank_in_group'] + 1
# Fusionner pour récupérer l'azimut précédent
df_lines_polaire = df_lines_polaire.merge(
df_temp[['group_id', 'rank_in_group_prev', 'azimut_deg']],
left_on=['group_id', 'rank_in_group'],
right_on=['group_id', 'rank_in_group_prev'],
how='left',
suffixes=('', '_prev')
)
# Renommer et nettoyer
df_lines_polaire['azimut_prev_deg'] = df_lines_polaire['azimut_deg_prev']
df_lines_polaire = df_lines_polaire.drop(['rank_in_group_prev', 'azimut_deg_prev'], axis=1)
df_lines_polaire['azimut_prev_deg'] = df_lines_polaire['azimut_prev_deg'].fillna(df_lines_polaire['azimut_deg'])
df_lines_polaire['bissectrice'] = (df_lines_polaire['azimut_deg'] + df_lines_polaire['azimut_prev_deg']) / 2
# print(f"\n df_lines_polaire: {len(df_lines_polaire)} :\n{df_lines_polaire}")
# print(f"\n df_equates: {len(df_equates)} :\n{df_equates}")
# Index des lignes polaires par station name1 # Index des lignes polaires par station name1
index_by_station = df_lines_polaire.set_index("name1")[["azimut_deg", "longueur"]] index_by_station = df_lines_polaire.set_index("name1")[["bissectrice", "longueur"]]
# Jointure pour récupérer azimut_ref et longueur_ref # Jointure pour récupérer azimut_ref et longueur_ref
_df_splays_complet = df_splays_polaire.copy() _df_splays_complet = df_splays_polaire.copy()
_df_splays_complet = _df_splays_complet.join(index_by_station, on="name1", rsuffix="_ref") _df_splays_complet = _df_splays_complet.join(index_by_station, on="name1", rsuffix="_ref")
# Remplacer les valeurs manquantes par défaut : azimut_ref = 0, longueur_ref = 0 # Remplacer les valeurs manquantes par défaut : azimut_ref = 0, longueur_ref = 0
_df_splays_complet["azimut_deg_ref"] = _df_splays_complet["azimut_deg_ref"].fillna(0) _df_splays_complet["bissectrice"] = _df_splays_complet["bissectrice"].fillna(0)
_df_splays_complet["longueur_ref"] = _df_splays_complet["longueur_ref"].fillna(0) _df_splays_complet["longueur_ref"] = _df_splays_complet["longueur_ref"].fillna(0)
df_splays_complet = _df_splays_complet.merge( df_splays_complet = _df_splays_complet.merge(
@@ -476,9 +602,6 @@ def wall_construction(df_stations, df_lines, df_splays, x_min, x_max, y_min, y_m
how="left" how="left"
) )
print(f"\n df_splays_complet: {len(df_splays_complet)}")
print(df_splays_complet)
missing_mask = df_splays_complet["group_id"].isna() missing_mask = df_splays_complet["group_id"].isna()
for idx, row in df_splays_complet[missing_mask].iterrows(): for idx, row in df_splays_complet[missing_mask].iterrows():
@@ -488,61 +611,59 @@ def wall_construction(df_stations, df_lines, df_splays, x_min, x_max, y_min, y_m
group_id = match["group_id"].values[0] group_id = match["group_id"].values[0]
max_rank = df_lines_polaire[df_lines_polaire["group_id"] == group_id]["rank_in_group"].max() max_rank = df_lines_polaire[df_lines_polaire["group_id"] == group_id]["rank_in_group"].max()
df_splays_complet.at[idx, "azimut_deg_ref"] = match["azimut_deg"].values[0] df_splays_complet.at[idx, "bissectrice"] = match["azimut_deg"].values[0]
df_splays_complet.at[idx, "longueur_ref"] = match["longueur"].values[0] df_splays_complet.at[idx, "longueur_ref"] = match["longueur"].values[0]
df_splays_complet.at[idx, "group_id"] = group_id df_splays_complet.at[idx, "group_id"] = group_id
df_splays_complet.at[idx, "rank_in_group"] = max_rank + 1 df_splays_complet.at[idx, "rank_in_group"] = max_rank + 1
df_splays_complet = add_start_end_splays(df_splays_complet, df_equates)
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', None)
df_splays_complet = df_splays_complet.sort_values(by=["group_id", "rank_in_group"]).reset_index(drop=True) df_splays_complet = df_splays_complet.sort_values(by=["group_id", "rank_in_group"]).reset_index(drop=True)
print(f"\n df_splays_complet: {len(df_splays_complet)}")
print(df_splays_complet) df_splays_complet["delta_azimut"] = df_splays_complet["bissectrice"] - df_splays_complet["azimut_deg"]
# Calcul de la projection : sin(delta azimut) * longueur_ref # Calcul de la projection : sin(delta azimut) * longueur_ref
def calc_projection(row): def calc_projection(row):
try: try:
delta = math.radians(row["azimut_deg_ref"] - row["azimut_deg"]) delta = math.radians(row["bissectrice"] - row["azimut_deg"])
return math.sin(delta) * row["longueur"] return math.sin(delta) * row["longueur"]
except: except:
return None return None
df_splays_complet["proj"] = df_splays_complet.apply(calc_projection, axis=1) df_splays_complet["proj"] = df_splays_complet.apply(calc_projection, axis=1)
df_splays_complet["group_id"] = df_splays_complet["group_id"].astype(int)
df_splays_complet["rank_in_group"] = df_splays_complet["rank_in_group"].astype(int)
# print(f"\n df_splays_complet: {len(df_splays_complet)} :\n{df_splays_complet}")
# Filtrage des extrêmes min/max par station name1 # Filtrage des extrêmes min/max par station name1
df_valid_proj = df_splays_complet.dropna(subset=["proj"]) df_valid_proj = df_splays_complet.dropna(subset=["proj"])
idx_min = df_valid_proj.groupby("name1")["proj"].idxmin()
idx_max = df_valid_proj.groupby("name1")["proj"].idxmax()
# print(f"\n df_splays_complet: {len(df_splays_complet)} :\n{df_splays_complet}")
idx_max = df_valid_proj.groupby(["group_id", "rank_in_group"])["proj"].idxmax()
df_result01 = df_valid_proj.loc[idx_max].reset_index(drop=True)
# idx_max = df_valid_proj.groupby("name1")["proj"].idxmax()
df_result01 = pd.concat([df_valid_proj.loc[idx_max]]).drop_duplicates() df_result01 = pd.concat([df_valid_proj.loc[idx_max]]).drop_duplicates()
df_result02 = pd.concat([df_valid_proj.loc[idx_min]]).drop_duplicates()
df_result01["group_id"] = df_result01["group_id"].astype(int)
df_result01["rank_in_group"] = df_result01["rank_in_group"].astype(int)
df_sorted01 = df_result01.sort_values(by=["group_id", "rank_in_group"]).reset_index(drop=True) df_sorted01 = df_result01.sort_values(by=["group_id", "rank_in_group"]).reset_index(drop=True)
# Convertir les colonnes en entiers idx_min = df_valid_proj.groupby(["group_id", "rank_in_group"])["proj"].idxmin()
df_result02["group_id"] = df_result02["group_id"].astype(int) df_result02 = df_valid_proj.loc[idx_min].reset_index(drop=True)
df_result02["rank_in_group"] = df_result02["rank_in_group"].astype(int) # idx_min = df_valid_proj.groupby("name1")["proj"].idxmin()
df_result02 = pd.concat([df_valid_proj.loc[idx_min]]).drop_duplicates()
df_sorted02 = df_result02.sort_values(by=["group_id", "rank_in_group"]).reset_index(drop=True) df_sorted02 = df_result02.sort_values(by=["group_id", "rank_in_group"]).reset_index(drop=True)
# Affichage de contrôle # Affichage de contrôle
print(f"\n df_sorted01: {len(df_sorted01)}") # print(f"\n df_sorted01: {len(df_sorted01)} :\n{df_sorted01}")
print(df_sorted01) # print(f"\n df_sorted02: {len(df_sorted02)} :\n{df_sorted02}")
# print(f"\n df_sorted02: {len(df_sorted02)}") # print(f"\n idx_min: {len(idx_min)} :\n{idx_min}")
# print(df_sorted02)
th2_walls=[]
_list = ""
for gid in sorted(df_sorted01["group_id"].unique()): for gid in sorted(df_sorted01["group_id"].unique()):
df_group = df_sorted01[df_sorted01["group_id"] == gid] df_group = df_sorted02[df_sorted02["group_id"] == gid]
_list += f"line wall\n" _list += f"line wall\n"
@@ -556,7 +677,7 @@ def wall_construction(df_stations, df_lines, df_splays, x_min, x_max, y_min, y_m
_list += "endline\n\nline wall -reverse on\n" _list += "endline\n\nline wall -reverse on\n"
df_group = df_sorted02[df_sorted02["group_id"] == gid] df_group = df_sorted01[df_sorted01["group_id"] == gid]
for line in df_group.itertuples(index=False): for line in df_group.itertuples(index=False):
_list += f"\t{line.x2} {line.y2}\n" _list += f"\t{line.x2} {line.y2}\n"
if line.x2 > x_max: x_max = line.x2 if line.x2 > x_max: x_max = line.x2
@@ -2464,7 +2585,7 @@ if __name__ == u'__main__':
#parser.add_argument("--format", choices=['th2', 'plt'], help="Output format. Either th2 for producing skeleton for drawing or plt for visualizing in aven/loch", default="th2") #parser.add_argument("--format", choices=['th2', 'plt'], help="Output format. Either th2 for producing skeleton for drawing or plt for visualizing in aven/loch", default="th2")
parser.add_argument("--output", default="./", help="Output folder path") parser.add_argument("--output", default="./", help="Output folder path")
# parser.add_argument("--therion-path", help="Path to therion binary", default="therion") # parser.add_argument("--therion-path", help="Path to therion binary", default="therion")
parser.add_argument("--scale", help="Scale for the exports", default="100") parser.add_argument("--scale", help="Scale for the exports", default="1000")
parser.add_argument("--lines", type=str_to_bool, help="Shot lines in th2 files", default=-1) parser.add_argument("--lines", type=str_to_bool, help="Shot lines in th2 files", default=-1)
parser.add_argument("--names", type=str_to_bool, help="Stations names in th2 files", default=-1) parser.add_argument("--names", type=str_to_bool, help="Stations names in th2 files", default=-1)
parser.add_argument("--update", help="Mode update, option th2", default="") parser.add_argument("--update", help="Mode update, option th2", default="")