mirror of
https://github.com/norohind/EDMC_SpanshRouter.git
synced 2026-10-22 18:02:08 +03:00
1. Implemented display of LY distances for "carrier" type .csv import (and restoring on program reset)
Hopefully, this is the last PR related to display LYs. I have checked all import types, and only "carrier" was lacking the distance. "tourist" type displays LY distances on spansh website, buc is lacking them for .csv export. 2. Shortened "Remaining LY afterwards" to "LY afterwards" for all types, to avoid unnecessarily expanding EDMC GUI horizontally, for very long distances (like carrier route from Bubble to Colonia)
This commit is contained in:
1 parent
8ba3bdb7c8
commit
942e97a785
1 file changed
+114
-165
+114
-165
@@ -499,7 +499,7 @@ class SpanshRouter():
|
||||
total_rem = total
|
||||
|
||||
if total_rem is not None:
|
||||
self.dist_remaining = f"Remaining LY afterwards: {total_rem:.2f}"
|
||||
self.dist_remaining = f"LY afterwards: {total_rem:.2f}"
|
||||
else:
|
||||
# final fallback: sum numeric jumps (index 1) as approximate
|
||||
s = 0.0
|
||||
@@ -628,85 +628,71 @@ class SpanshRouter():
|
||||
self.clear_route(False)
|
||||
|
||||
route_reader = csv.DictReader(csvfile)
|
||||
|
||||
# Get column header names as string (preserve order)
|
||||
headerline = ','.join(route_reader.fieldnames) if route_reader.fieldnames else ""
|
||||
|
||||
# Define the different internal formats based on the CSV header row
|
||||
internalbasicheader1 = "System Name"
|
||||
internalbasicheader2 = "System Name,Jumps"
|
||||
internalrichesheader = "System Name,Jumps,Body Name,Body Subtype"
|
||||
internalfleetcarrierheader_with_distances = "System Name,Jumps,Distance To Arrival,Distance Remaining,Restock Tritium"
|
||||
internalfleetcarrierheader = "System Name,Jumps,Restock Tritium"
|
||||
internalgalaxyheader = "System Name,Refuel"
|
||||
# External/other import formats
|
||||
neutronimportheader = "System Name,Distance To Arrival,Distance Remaining,Neutron Star,Jumps"
|
||||
road2richesimportheader = "System Name,Body Name,Body Subtype,Is Terraformable,Distance To Arrival,Estimated Scan Value,Estimated Mapping Value,Jumps"
|
||||
fleetcarrierimportheader = "System Name,Distance,Distance Remaining,Tritium in tank,Tritium in market,Fuel Used,Icy Ring,Pristine,Restock Tritium"
|
||||
galaxyimportheader = "System Name,Distance,Distance Remaining,Fuel Left,Fuel Used,Refuel,Neutron Star"
|
||||
|
||||
# Helper to check and pick distance keys flexibly
|
||||
def get_distance_fields(row):
|
||||
# prefer "Distance To Arrival" (neutron style), fall back to "Distance"
|
||||
dist_to_arrival = row.get("Distance To Arrival", "") or row.get("Distance", "")
|
||||
dist_remaining = row.get("Distance Remaining", "") or ""
|
||||
return dist_to_arrival, dist_remaining
|
||||
|
||||
# Handle neutron-import style
|
||||
# --- neutron import ---
|
||||
if headerline == neutronimportheader:
|
||||
# CSV has: System Name,Distance To Arrival,Distance Remaining,Neutron Star,Jumps
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
jumps = row.get(self.jumps_header, "")
|
||||
dist_to_arrival = row.get("Distance To Arrival", "")
|
||||
dist_remaining = row.get("Distance Remaining", "")
|
||||
# keep index 1 compatible with existing code (jumps)
|
||||
self.route.append([
|
||||
row[self.system_header],
|
||||
jumps,
|
||||
dist_to_arrival,
|
||||
dist_remaining
|
||||
])
|
||||
if jumps:
|
||||
try:
|
||||
self.jumps_left += int(jumps)
|
||||
except:
|
||||
pass
|
||||
|
||||
# legacy/simple CSV: System Name [, Jumps]
|
||||
elif (headerline == internalbasicheader1) or (headerline == internalbasicheader2):
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
self.route.append([
|
||||
row[self.system_header],
|
||||
row.get(self.jumps_header, "") # Jumps column is optional
|
||||
])
|
||||
if row.get(self.jumps_header): # Jumps column is optional
|
||||
try:
|
||||
self.jumps_left += int(row[self.jumps_header])
|
||||
except:
|
||||
pass
|
||||
|
||||
# internal riches format (lists stored as Python-likes)
|
||||
elif headerline == internalrichesheader:
|
||||
self.roadtoriches = True
|
||||
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
bodynames = ast.literal_eval(row[self.bodyname_header])
|
||||
bodysubtypes = ast.literal_eval(row[self.bodysubtype_header])
|
||||
|
||||
self.route.append([
|
||||
row[self.system_header],
|
||||
row[self.jumps_header],
|
||||
bodynames,
|
||||
bodysubtypes
|
||||
row.get(self.jumps_header, ""),
|
||||
row.get("Distance To Arrival", ""),
|
||||
row.get("Distance Remaining", "")
|
||||
])
|
||||
try:
|
||||
self.jumps_left += int(row[self.jumps_header])
|
||||
except:
|
||||
pass
|
||||
|
||||
# internal fleetcarrier format
|
||||
# --- simple internal ---
|
||||
elif headerline in (internalbasicheader1, internalbasicheader2):
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
self.route.append([
|
||||
row[self.system_header],
|
||||
row.get(self.jumps_header, "")
|
||||
])
|
||||
try:
|
||||
self.jumps_left += int(row.get(self.jumps_header, 0))
|
||||
except:
|
||||
pass
|
||||
|
||||
# --- internal fleetcarrier WITH distances (load after restart) ---
|
||||
elif headerline == internalfleetcarrierheader_with_distances:
|
||||
self.fleetcarrier = True
|
||||
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
self.route.append([
|
||||
row[self.system_header],
|
||||
row[self.jumps_header],
|
||||
row.get("Distance To Arrival", ""),
|
||||
row.get("Distance Remaining", ""),
|
||||
row.get(self.restocktritium_header, "")
|
||||
])
|
||||
try:
|
||||
self.jumps_left += int(row[self.jumps_header])
|
||||
except:
|
||||
pass
|
||||
|
||||
# --- internal fleetcarrier (legacy, no distances) ---
|
||||
elif headerline == internalfleetcarrierheader:
|
||||
self.fleetcarrier = True
|
||||
|
||||
@@ -722,112 +708,59 @@ class SpanshRouter():
|
||||
except:
|
||||
pass
|
||||
|
||||
# road2riches import
|
||||
elif headerline == road2richesimportheader:
|
||||
self.roadtoriches = True
|
||||
|
||||
bodynames = []
|
||||
bodysubtypes = []
|
||||
|
||||
for row in route_reader:
|
||||
bodyname = row[self.bodyname_header]
|
||||
bodysubtype = row[self.bodysubtype_header]
|
||||
|
||||
# Update the current system with additional bodies from new CSV row
|
||||
if self.route.__len__() > 0 and row[self.system_header] == self.route[-1][0]:
|
||||
self.route[-1][2].append(bodyname)
|
||||
self.route[-1][3].append(bodysubtype)
|
||||
continue
|
||||
|
||||
if row not in (None, "", []):
|
||||
bodynames.append(bodyname)
|
||||
bodysubtypes.append(bodysubtype)
|
||||
|
||||
self.route.append([
|
||||
row[self.system_header],
|
||||
row[self.jumps_header],
|
||||
bodynames.copy(),
|
||||
bodysubtypes.copy()
|
||||
])
|
||||
# Clear bodies for next system
|
||||
bodynames.clear()
|
||||
bodysubtypes.clear()
|
||||
|
||||
try:
|
||||
self.jumps_left += int(row[self.jumps_header])
|
||||
except:
|
||||
pass
|
||||
|
||||
# external fleetcarrier import
|
||||
# --- EXTERNAL fleetcarrier import (WITH LY SUPPORT) ---
|
||||
elif headerline == fleetcarrierimportheader:
|
||||
self.fleetcarrier = True
|
||||
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
# treat every row as one jump in this import format
|
||||
dist_to_arrival, dist_remaining = get_distance_fields(row)
|
||||
|
||||
self.route.append([
|
||||
row[self.system_header],
|
||||
1, # Jumps is faked as every row is 1 jump
|
||||
1, # every row = one carrier jump
|
||||
dist_to_arrival,
|
||||
dist_remaining,
|
||||
row.get(self.restocktritium_header, "")
|
||||
])
|
||||
self.jumps_left += 1
|
||||
|
||||
# galaxy-type imports: handle both the long upstream format
|
||||
# and the simpler internal 'System Name,Refuel' header.
|
||||
# We detect galaxy files by presence of "Refuel" in headerline.
|
||||
# --- galaxy ---
|
||||
elif "Refuel" in headerline and self.system_header in headerline:
|
||||
self.galaxy = True
|
||||
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
system = row.get(self.system_header, "")
|
||||
# keep route[i][1] as Refuel so existing galaxy logic keeps working
|
||||
refuel = row.get(self.refuel_header, "")
|
||||
# attempt to read distances if present (either "Distance" or "Distance To Arrival")
|
||||
dist_to_arrival, dist_remaining = get_distance_fields(row)
|
||||
|
||||
# Build route row:
|
||||
# - always keep [system, refuel] (so update_route / pleaserefuel continue to work)
|
||||
# - append distances if we have them so compute_distances can display LY
|
||||
route_row = [system, refuel]
|
||||
if dist_to_arrival != "" or dist_remaining != "":
|
||||
route_row = [
|
||||
row.get(self.system_header, ""),
|
||||
row.get(self.refuel_header, "")
|
||||
]
|
||||
|
||||
if dist_to_arrival or dist_remaining:
|
||||
route_row.append(dist_to_arrival)
|
||||
route_row.append(dist_remaining)
|
||||
|
||||
self.route.append(route_row)
|
||||
|
||||
# galaxy logic: treat each row as one jump for jump counting
|
||||
self.jumps_left += 1
|
||||
|
||||
else:
|
||||
# Unknown header: try a best-effort basic parse as System Name,Jumps
|
||||
for row in route_reader:
|
||||
if row not in (None, "", []):
|
||||
# fallback to first two columns if possible
|
||||
try:
|
||||
system = row.get(self.system_header, "")
|
||||
jumps = row.get(self.jumps_header, "")
|
||||
except Exception:
|
||||
# as ultimate fallback, iterate values
|
||||
vals = list(row.values())
|
||||
system = vals[0] if len(vals) > 0 else ""
|
||||
jumps = vals[1] if len(vals) > 1 else ""
|
||||
system = row.get(self.system_header, "")
|
||||
jumps = row.get(self.jumps_header, "")
|
||||
self.route.append([system, jumps])
|
||||
if jumps:
|
||||
try:
|
||||
self.jumps_left += int(jumps)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.jumps_left += int(jumps)
|
||||
except:
|
||||
pass
|
||||
|
||||
# After loading route, initialize current waypoint
|
||||
if len(self.route) > 0:
|
||||
if self.route:
|
||||
self.offset = 0
|
||||
self.next_stop = self.route[0][0]
|
||||
# Compute LY distances immediately (so GUI shows them before pressing arrows)
|
||||
self.compute_distances()
|
||||
self.update_gui()
|
||||
else:
|
||||
self.show_error("Could not detect file format")
|
||||
|
||||
def plot_route(self):
|
||||
self.hide_error()
|
||||
@@ -1089,87 +1022,103 @@ class SpanshRouter():
|
||||
self.save_offset()
|
||||
|
||||
def save_route(self):
|
||||
"""Save current route to disk.
|
||||
|
||||
For galaxy routes we write System Name,Refuel,Distance To Arrival,Distance Remaining
|
||||
so that distances are preserved across restarts and LY display works like for neutron.
|
||||
Other formats keep previous behaviour.
|
||||
"""
|
||||
# If no route, try to remove saved files and exit
|
||||
if len(self.route) == 0:
|
||||
try:
|
||||
os.remove(self.save_route_path)
|
||||
except:
|
||||
logger.info("No route to delete")
|
||||
pass
|
||||
return
|
||||
|
||||
try:
|
||||
# --- Road to riches ---
|
||||
if self.roadtoriches:
|
||||
fieldnames = [self.system_header, self.jumps_header, self.bodyname_header, self.bodysubtype_header]
|
||||
fieldnames = [
|
||||
self.system_header,
|
||||
self.jumps_header,
|
||||
self.bodyname_header,
|
||||
self.bodysubtype_header
|
||||
]
|
||||
with open(self.save_route_path, 'w', newline='') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(fieldnames)
|
||||
for row in self.route:
|
||||
writer.writerow(row)
|
||||
writer.writerows(self.route)
|
||||
return
|
||||
|
||||
# --- Fleet carrier (WITH DISTANCES) ---
|
||||
if self.fleetcarrier:
|
||||
fieldnames = [self.system_header, self.jumps_header, self.restocktritium_header]
|
||||
fieldnames = [
|
||||
self.system_header,
|
||||
self.jumps_header,
|
||||
"Distance To Arrival",
|
||||
"Distance Remaining",
|
||||
self.restocktritium_header
|
||||
]
|
||||
with open(self.save_route_path, 'w', newline='') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(fieldnames)
|
||||
for row in self.route:
|
||||
writer.writerow(row)
|
||||
writer.writerow([
|
||||
row[0],
|
||||
row[1],
|
||||
row[2] if len(row) > 2 else "",
|
||||
row[3] if len(row) > 3 else "",
|
||||
row[4] if len(row) > 4 else ""
|
||||
])
|
||||
return
|
||||
|
||||
# --- Galaxy ---
|
||||
if self.galaxy:
|
||||
# Save galaxy routes including distances if present:
|
||||
# Write header so reload can restore distances: System Name,Refuel,Distance To Arrival,Distance Remaining
|
||||
fieldnames = [self.system_header, self.refuel_header, "Distance To Arrival", "Distance Remaining"]
|
||||
fieldnames = [
|
||||
self.system_header,
|
||||
self.refuel_header,
|
||||
"Distance To Arrival",
|
||||
"Distance Remaining"
|
||||
]
|
||||
with open(self.save_route_path, 'w', newline='') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(fieldnames)
|
||||
for row in self.route:
|
||||
# normalize row into 4 columns:
|
||||
system = row[0] if len(row) > 0 else ""
|
||||
refuel = row[1] if len(row) > 1 else ""
|
||||
dist_to_arrival = row[2] if len(row) > 2 else ""
|
||||
dist_remaining = row[3] if len(row) > 3 else ""
|
||||
writer.writerow([system, refuel, dist_to_arrival, dist_remaining])
|
||||
writer.writerow([
|
||||
row[0],
|
||||
row[1],
|
||||
row[2] if len(row) > 2 else "",
|
||||
row[3] if len(row) > 3 else ""
|
||||
])
|
||||
return
|
||||
|
||||
# Generic routes: check if any row contains distance columns (len >= 4)
|
||||
has_distance_columns = any(len(row) >= 4 for row in self.route)
|
||||
|
||||
if has_distance_columns:
|
||||
# Write as neutron-import style:
|
||||
# System Name,Distance To Arrival,Distance Remaining,Neutron Star,Jumps
|
||||
fieldnames = ["System Name", "Distance To Arrival", "Distance Remaining", "Neutron Star", "Jumps"]
|
||||
# --- Generic with distances ---
|
||||
if any(len(r) >= 4 for r in self.route):
|
||||
fieldnames = [
|
||||
"System Name",
|
||||
"Distance To Arrival",
|
||||
"Distance Remaining",
|
||||
"Neutron Star",
|
||||
"Jumps"
|
||||
]
|
||||
with open(self.save_route_path, 'w', newline='') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(fieldnames)
|
||||
for row in self.route:
|
||||
# Normalize row into 5 columns; Neutron Star unknown => empty string
|
||||
system = row[0] if len(row) > 0 else ""
|
||||
jumps = row[1] if len(row) > 1 else ""
|
||||
dist_to_arrival = row[2] if len(row) > 2 else ""
|
||||
dist_remaining = row[3] if len(row) > 3 else ""
|
||||
neutron_flag = "" # unknown from our data; leave empty
|
||||
writer.writerow([system, dist_to_arrival, dist_remaining, neutron_flag, jumps])
|
||||
writer.writerow([
|
||||
row[0],
|
||||
row[2] if len(row) > 2 else "",
|
||||
row[3] if len(row) > 3 else "",
|
||||
"",
|
||||
row[1]
|
||||
])
|
||||
return
|
||||
|
||||
# Fallback: simple System, Jumps
|
||||
fieldnames = [self.system_header, self.jumps_header]
|
||||
# --- Fallback ---
|
||||
with open(self.save_route_path, 'w', newline='') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(fieldnames)
|
||||
writer.writerow([self.system_header, self.jumps_header])
|
||||
writer.writerows(self.route)
|
||||
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
|
||||
logger.warning(''.join('!! ' + line for line in lines))
|
||||
self.show_error("An error occured while writing the route to disk.")
|
||||
|
||||
|
||||
def save_offset(self):
|
||||
if self.route.__len__() != 0:
|
||||
|
||||
Reference in new issue
Block a user