Move scenery tools into their own folder, added STG to UFO XML configuration file converter and begins of a JavaProp to JSBsim CP and CT table converter

This commit is contained in:
TheFGFSEagle
2022-02-28 17:11:21 +01:00
parent 576a1d94b6
commit 5df1ea3aca
3 changed files with 184 additions and 0 deletions

225
scenery/edit-stg.py Executable file
View File

@@ -0,0 +1,225 @@
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
import argparse
import sys
import os
import subprocess
import time
class SkipReason:
NotFound = 0
DirEmpty = 1
NoSTG = 2
def make_fgelev_pipe(fgelev, fgscenery, fgdata):
env = os.environ.copy()
env["FG_SCENERY"] = os.pathsep.join(fgscenery)
env["FG_DATA"] = fgdata[0]
pipe = subprocess.Popen(args=[fgelev, "--expire", "1"], env={**env, **os.environ}, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pipe.stdout.flush()
pipe.stdout.readline()
pipe.stdin.flush()
pipe.stdin.flush()
return pipe
def read_stg_file(path):
result = []
offset = 0
skipnext = False
with open(path, "r") as fp:
content = fp.readlines()
for number, line in enumerate(content):
line = line.strip()
if line.startswith("#"): # is a comment
if line.startswith("# offset"):
offset = line.split(" ")[-1]
result.append(line)
elif line.startswith("# skipnext"):
skipnext = True
result.append(line)
else:
offset = 0
skipnext = False
result.append(line)
elif line.strip() == "": # empty line
result.append(line)
else: # is an object
etype, *data = list(map(lambda s: s.strip(), line.split(" ")))
if etype in ["OBJECT_SHARED", "OBJECT_SHARED_AGL", "OBJECT_STATIC", "OBJECT_STATIC_AGL", "OBJECT_SIGN", "OBJECT_SIGN_AGL", "BUILDING_ROUGH", "BUILDING_DETAILED", "OBJECT_ROAD_ROUGH", "OBJECT_ROAD_DETAILED", "OBJECT_RAILWAY_ROUGH", "OBJECT_RAILWAY_DETAILED", "OBJECT_BUILDING_MESH_ROUGH", "OBJECT_BUILDING_MESH_DETAILED"]:
if len(data) == 5:
objectfile, longitude, latitude, elevation, heading, pitch, roll = *data, 0, 0
elif len(data) == 7:
objectfile, longitude, latitude, elevation, heading, pitch, roll = data
else:
print(f"Warning: file {path}, line {number} is malformed - not recalculating elevation")
result.append(line)
continue
result.append({
"etype": etype,
"objectfile": objectfile,
"longitude": longitude,
"latitude": latitude,
"elevation": float(elevation),
"offset": float(offset),
"heading": heading,
"pitch": pitch,
"roll": roll,
"skip": skipnext
})
elif etype in ["BUILDING_LIST", "TREE_LIST"]:
if len(data) == 5:
objectfile, material, longitude, latitude, elevation = data
else:
print(f"Warning: file {path}, line {number} is malformed - not recalculating elevation")
result.append(line)
continue
result.append({
"etype": etype,
"objectfile": objectfile,
"material": material,
"longitude": longitude,
"latitude": latitude,
"elevation": float(elevation),
"offset": float(offset),
"skip": skipnext
})
elif etype in ["OBJECT", "OBJECT_BASE", "LINEAR_FEATURE_LIST"]:
result.append(line)
else:
print(f"Warning: file {path} line {number} has wrong type - commenting out to prevent FG not loading scenery")
result.append("# " + line)
return result
def read_stg_files(paths):
stg_dict = dict.fromkeys(paths, {"infiles": [], "contents": []})
for path in paths:
if not os.path.exists(path):
print(f"Warning: Input file / directory {path} does not exist, skipping")
stg_dict[path] = SkipReason.NotFound
elif os.path.isdir(path):
for file in os.listdir(path):
if os.splitext(path)[1] == ".stg":
realpath = os.path.join(path, file)
content = read_stg_file(realpath)
stg_dict[path]["infiles"].append(file)
stg_dict[path]["contents"].append(content)
else:
print(f"Warning: No STG file found in input directory {path}, skipping")
stg_dict[path] = SkipReason.DirEmpty
else:
if os.path.splitext(path)[1] == ".stg":
content = read_stg_file(path)
stg_dict[path]["infiles"].append(path)
stg_dict[path]["contents"].append(content)
else:
print(f"Warning: Input file {path} is not an STG file, skipping")
stg_dict[path] = SkipReason.NoSTG
return stg_dict
def recalc_elevs(stg_dict, elevpipe):
for path in stg_dict:
if type(stg_dict[path]) == int: # some SkipReason, so let's skip this path
continue
else:
for content in stg_dict[path]["contents"]:
for object in content:
if type(object) == str:
continue
elif type(object) == dict:
if "elevation" in object.keys():
if not object["skip"]:
print(f"Recalculating elevation of {object['objectfile']}")
elevpipe.stdin.write(f"{object['objectfile']} {object['longitude']} {object['latitude']}\n".encode("utf-8"))
elevpipe.stdin.flush()
fgelevout = elevpipe.stdout.readline().split()
if len(fgelevout) == 2:
object["elevation"] = float(fgelevout[1])
else:
print(f"Skipping {object['objectfile']}")
object["elevation"] += object["offset"]
print(f"Final elevation: {object['elevation']} meters, offset was {object['offset']}")
return stg_dict
def write_stg_files(output_stg, outfiles):
for i, path in enumerate(output_stg):
for infile, content in zip(output_stg[path]["infiles"], output_stg[path]["contents"]):
if len(outfiles) == 1:
if outfiles[0] == "__INPUT__":
outfile = infile
else:
outfile = outfiles[0]
else:
if i < len(outfiles):
outfile = outfiles[i]
else:
outfile = outfiles[-1]
with open(outfile, "w") as outfp:
for object in content:
if type(object) == str:
line = object
else:
if object["etype"] in ["BUILDING_LIST", "TREE_LIST"]:
line = f"{object['etype']} {object['objectfile']} {object['material']} {object['longitude']} {object['latitude']} {object['elevation']}"
else:
line = f"{object['etype']} {object['objectfile']} {object['longitude']} {object['latitude']} {object['elevation']} {object['heading']}"
if "pitch" in object.keys():
line += f" {object['pitch']} {object['roll']}"
line += "\n"
outfp.write(line)
return 0
def main():
argp = argparse.ArgumentParser(description="Perform various STG file operations such as recalculating the elevation of models")
argp.add_argument(
"-i", "--input",
help="Input STG file. Mandatory, more than one file / directory can be passed",
nargs="+",
required=True
)
argp.add_argument(
"-s", "--fgscenery",
help="Path to FlightGear scenery directories containing Terrain, more than one directory can be passed.",
nargs="+",
default=["~/TerraSync", "~/TerraSync/TerraSync", "TerraSync", "TerraSync/TerraSync"]
)
argp.add_argument(
"-d", "--fgdata",
help="Path to FlightGear data directory.",
nargs=1,
default="~/fgdata"
)
argp.add_argument(
"-e", "--fgelev",
help="Path to FGelev",
default="fgelev",
)
argp.add_argument(
"-o", "--output",
help="Output STG file. Default is to overwrite the input file(s).",
nargs="+",
default=["__INPUT__"]
)
args = argp.parse_args()
infiles = args.input
outfiles = args.output
fgdata = args.fgdata
fgscenery = args.fgscenery
fgelev = args.fgelev
elevpipe = make_fgelev_pipe(fgelev, fgscenery, fgdata)
input_stg = read_stg_files(infiles)
output_stg = recalc_elevs(input_stg, elevpipe)
exitstatus = write_stg_files(output_stg, outfiles)
return exitstatus
if __name__ == "__main__":
sys.exit(main())

155
scenery/stg2ufo.py Executable file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
import argparse
import os
class SkipReason:
NotFound = 0
DirEmpty = 1
NoSTG = 2
def read_stg_file(path):
result = []
offset = 0
skipnext = False
with open(path, "r") as fp:
content = fp.readlines()
for number, line in enumerate(content):
line = line.strip()
if line.startswith("#"): # is a comment
if line.startswith("# offset"):
offset = line.split(" ")[-1]
result.append(line)
elif line.startswith("# skipnext"):
skipnext = True
elif line.strip() == "": # empty line
continue
else: # is an object
etype, *data = list(map(lambda s: s.strip(), line.split(" ")))
if etype in ["OBJECT_SHARED", "OBJECT_STATIC"]:
if len(data) == 5:
objectfile, longitude, latitude, elevation, heading, pitch, roll = *data, 0, 0
elif len(data) == 7:
objectfile, longitude, latitude, elevation, heading, pitch, roll = data
else:
print(f"Warning: file {path}, line {number} is malformed - not converting entry")
result.append(line)
continue
result.append({
"etype": etype,
"objectfile": objectfile,
"longitude": longitude,
"latitude": latitude,
"elevation": float(elevation),
"offset": float(offset),
"heading": heading,
"pitch": pitch,
"roll": roll,
"skip": skipnext
})
offset = 0
skipnext = False
else:
print(f"Warning: skipping line {number} in {path} because type {etype} is not supported")
return result
def read_stg_files(paths):
stg_dict = dict.fromkeys(paths, {"infiles": [], "contents": []})
for path in paths:
if not os.path.exists(path):
print(f"Warning: Input file / directory {path} does not exist, skipping")
stg_dict[path] = SkipReason.NotFound
elif os.path.isdir(path):
for file in os.listdir(path):
if os.splitext(path)[1] == ".stg":
realpath = os.path.join(path, file)
content = read_stg_file(realpath)
stg_dict[path]["infiles"].append(file)
stg_dict[path]["contents"].append(content)
else:
print(f"Warning: No STG file found in input directory {path}, skipping")
stg_dict[path] = SkipReason.DirEmpty
else:
if os.path.splitext(path)[1] == ".stg":
content = read_stg_file(path)
stg_dict[path]["infiles"].append(path)
stg_dict[path]["contents"].append(content)
else:
print(f"Warning: Input file {path} is not an STG file, skipping")
stg_dict[path] = SkipReason.NoSTG
return stg_dict
def write_xml_files(output_stg, outfiles):
for i, path in enumerate(output_stg):
if type(path) != str: # SkipReason
continue
for infile, content in zip(output_stg[path]["infiles"], output_stg[path]["contents"]):
if len(outfiles) == 1:
if outfiles[0] == "__INPUT__":
outfile = infile
else:
outfile = outfiles[0]
else:
if i < len(outfiles):
outfile = outfiles[i]
else:
outfile = outfiles[-1]
with open(outfile, "w") as outfp:
outfp.write("<?xml version=\"1.0\"?>\n")
outfp.write("<PropertyList>\n")
outfp.write(" <models>\n")
for object in content:
outfp.write(" <model>\n")
outfp.write(f" <legend>{object['objectfile'].split('/')[-1]}</legend>\n")
outfp.write(f" <pitch-deg>{object['pitch']}</pitch-deg>\n")
outfp.write(f" <roll-deg>{object['pitch']}</roll-deg>\n")
outfp.write(f" <heading-deg>{object['heading']}</heading-deg>\n")
outfp.write(f" <latitude-deg>{object['latitude']}</latitude-deg>\n")
outfp.write(f" <longitude-deg>{object['longitude']}</longitude-deg>\n")
outfp.write(f" <elevation-ft>{float(object['elevation']) * 3.2808399}</elevation-ft>\n")
outfp.write(f" <elevation-m>{object['elevation']}</elevation-m>\n")
outfp.write(f" <stg-heading-deg>{float(object['heading']) + 180}</stg-heading-deg>\n")
outfp.write(f" <stg-path>{os.path.abspath(path)}</stg-path>\n")
objectpath = object["objectfile"]
if object["etype"] == "OBJECT_STATIC":
objectpath = path + "/" + objectpath
outfp.write(f" <path>{objectpath}</path>\n")
line = f"{object['etype']} {object['objectfile']} {object['longitude']} {object['latitude']} {object['elevation'] + object['offset']} {object['heading']} {object['pitch']} {object['roll']}"
outfp.write(f" <object-line>{line}</object-line>\n")
outfp.write(" </model>\n")
outfp.write(" </models>\n")
outfp.write("</PropertyList>\n")
def main():
argp = argparse.ArgumentParser(description="Perform various STG file operations such as recalculating the elevation of models")
argp.add_argument(
"-i", "--input",
help="Input STG file. Mandatory, more than one file / directory can be passed",
nargs="+",
required=True
)
argp.add_argument(
"-o", "--output",
help="Output STG file. Default is to overwrite the input file(s).",
nargs="+",
default=["__INPUT__"]
)
args = argp.parse_args()
infiles = args.input
outfiles = args.output
input_stg = read_stg_files(infiles)
exitstatus = write_xml_files(input_stg, outfiles)
if __name__ == "__main__":
main()