#! /usr/bin/python3 import os import re import sys from time import sleep try: import tty, termios except ImportError: # Probably Windows. try: import msvcrt except ImportError: # Just give up here. raise ImportError('getch not available') else: getch = msvcrt.getch else: def getch(): fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(fd) ch = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch project = "" argc = len(sys.argv) i = 1 while i < argc: if sys.argv[i] == "-p" or sys.argv[i] == "--project": i += 1 project = sys.argv[i] elif sys.argv[i] == "-h" or sys.argv[i] == "--help": print("useage: edit-settings -p [OPTIONS]") print("You can edit your project settings\n") print("OPTIONS") print(" -p, --project Mandatory, project you want to edit") print(" -h, --help Shows this help and exit") sys.exit(0) else: print("Unknown option " + sys.argv[i]) sys.exit(1) i += 1 if project == "": print("Option -p is mandatory") sys.exit(1) if os.path.isfile("projects/" + project + "/params.ini") == False: print("Project '" + project + "' not found") sys.exit(1) console_height, console_width = os.popen('stty size', 'r').read().split() def fill_space ( string, aim_length ): length = len(string) while length < aim_length: string += " " length += 1 return string options = [ { "Group_Name": "Basics", "Options": [ { "Name": "PREFIX", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "PATH_TO_SCENERY", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "PATH_TO_SCENERY_OPT", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "PATH_TO_OUTPUT", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "PATH_TO_OSM2CITY_DATA", "default": "", "value": "", "type": "str", "description": "", "changed": False } ] }, { "Group_Name": "FG", "Options": [ { "Name": "NO_ELEV", "default": False, "value": False, "type": "bool", "description": "", "changed": False }, { "Name": "FG_ELEV", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "PROBE_FOR_WATER", "default": False, "value": False, "type": "bool", "description": "", "changed": False } ] }, { "Group_Name": "Database", "Options": [ { "Name": "DB_HOST", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "DB_PORT", "default": 5432, "value": 5432, "type": "int", "description": "", "changed": False }, { "Name": "DB_NAME", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "DB_USER", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "DB_USER_PASSWORD", "default": "", "value": "", "type": "str", "description": "", "changed": False } ] }, { "Group_Name": "Buildings", "Options": [ { "Name": "BUILDING_USE_SHARED_WORSHIP", "default": False, "value": False, "type": "bool", "description": "", "changed": False }, { "Name": "BUILDING_MIN_HEIGHT", "default": 0.0, "value": 0.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_MIN_AREA", "default": "50.0", "value": 50.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_PART_MIN_AREA", "default": 10.0, "value": 10.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_REDUCE_THRESHOLD", "default": 200.0, "value": 200.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_REDUCE_RATE", "default": 0.5, "value": 0.5, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_REDUCE_CHECK_TOUCH", "default": False, "value": False, "type": "bool", "description": "", "changed": False }, { "Name": "BUILDING_NEVER_SKIP_LEVELS", "default": 6, "value": 6, "type": "int" }, { "Name": "BUILDING_SIMPLIFY_TOLERANCE_LINE", "default": 1.0, "value": 1.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_SIMPLIFY_TOLERANCE_AWAY", "default": 2.5, "value": 2.5, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_FORCE_EUROPEAN_INNER_CITY_STYLE", "default": False, "value": False, "type": "bool", "description": "", "changed": False } ] }, { "Group_Name": "LOD", "Options": [ { "Name": "LOD_ALWAYS_DETAIL_BELOW_AREA", "default": 150, "value": 150, "type": "int", "description": "", "changed": False }, { "Name": "LOD_ALWAYS_DETAIL_ABOVE_AREA", "default": 500, "value": 500, "type": "int", "description": "", "changed": False }, { "Name": "LOD_ALWAYS_DETAIL_BELOW_LEVELS", "default": 6, "value": 6, "type": "int", "description": "", "changed": False }, { "Name": "LOD_ALWAYS_DETAIL_ABOVE_LEVELS", "default": 3, "value": 3, "type": "int", "description": "", "changed": False }, { "Name": "LOD_PERCENTAGE_DETAIL", "default": 0.5, "value": 0.5, "type": "float", "description": "", "changed": False } ] }, { "Group_Name": "Roofs", "Options": [ { "Name": "BUILDING_COMPLEX_ROOFS", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "BUILDING_COMPLEX_ROOFS_MIN_LEVELS", "default": 1, "value": 1, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_COMPLEX_ROOFS_MAX_LEVELS", "default": 5, "value": 5, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_COMPLEX_ROOFS_MAX_AREA", "default": 1600, "value": 1600, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_COMPLEX_ROOFS_MIN_RATIO_AREA", "default": 600, "value": 600, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_SKEL_MAX_NODES", "default": 10, "value": 10, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_ROOF_SHAPE_RATIO", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "BUILDING_ROOF_SIMPLIFY_TOLERANCE", "default": 0.5, "value": 0.5, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_SKEL_ROOFS_MIN_ANGLE", "default": 10, "value": 10, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_SKEL_ROOFS_MAX_ANGLE", "default": 50, "value": 50, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_SKILLION_ROOF_MAX_HEIGHT", "default": 2.0, "value": 2.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_SKEL_ROOF_MAX_HEIGHT", "default": 6.0, "value": 6.0, "type": "float", "description": "", "changed": False } ] }, { "Group_Name": "Overlap Check", "Options": [ { "Name": "OVERLAP_CHECK_CONVEX_HULL", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "OVERLAP_CHECK_CH_BUFFER_STATIC", "default": 0.0, "value": 0.0, "type": "float", "description": "", "changed": False }, { "Name": "OVERLAP_CHECK_CH_BUFFER_SHARED", "default": 0.0, "value": 0.0, "type": "float", "description": "", "changed": False }, { "Name": "OVERLAP_CHECK_CONSIDER_SHARED", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "OVERLAP_CHECK_BRIDGE_MIN_REMAINING", "default": 10, "value": 10, "type": "int", "description": "", "changed": False } ] }, { "Group_Name": "Rectify", "Options": [ { "Name": "RECTIFY_ENABLED", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "RECTIFY_90_TOLERANCE", "default": 0.1, "value": 0.1, "type": "float", "description": "", "changed": False }, { "Name": "RECTIFY_MAX_90_DEVIATION", "default": 7.0, "value": 7.0, "type": "float", "description": "", "changed": False }, { "Name": "RECTIFY_MAX_DRAW_SAMPLE", "default": 20.0, "value": 20.0, "type": "float", "description": "", "changed": False }, { "Name": "RECTIFY_SEED_SAMPLE", "default": True, "value": True, "type": "bool", "description": "", "changed": False } ] }, { "Group_Name": "Light Effects", "Options": [ { "Name": "OBSTRUCTION_LIGHT_MIN_LEVELS", "default": 15, "value": 15, "type": "int", "description": "", "changed": False }, { "Name": "BUILDING_FAKE_AMBIENT_OCCLUSION", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "BUILDING_FAKE_AMBIENT_OCCLUSION_HEIGHT", "default": 6.0, "value": 6.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILDING_FAKE_AMBIENT_OCCLUSION_VALUE", "default": 0.6, "value": 0.6, "type": "float", "description": "", "changed": False } ] }, { "Group_Name": "Would Be Building", "Options": [] }, { "Group_Name": "Linear Objects", "Options": [ { "Name": "BRIDGE_MIN_LENGTH", "default": 20.0, "value": 20.0, "type": "float", "description": "", "changed": False }, { "Name": "MIN_ABOVE_GROUND_LEVEL", "default": 0.01, "value": 0.01, "type": "float", "description": "", "changed": False }, { "Name": "HIGHWAY_TYPE_MIN", "default": 4, "value": 4, "type": "int", "description": "", "changed": False }, { "Name": "POINTS_ON_LINE_DISTANCE_MAX", "default": 1000, "value": 1000, "type": "int", "description": "", "changed": False }, { "Name": "MAX_SLOPE_ROAD", "default": 0.08, "value": 0.08, "type": "float", "description": "", "changed": False }, { "Name": "MAX_SLOPE_*", "default": 0.08, "value": 0.08, "type": "float", "description": "", "changed": False }, { "Name": "USE_TRAM_LINE", "default": False, "value": False, "type": "bool", "description": "", "changed": False } ] }, { "Group_Name": "Road Lighting", "Options": [ { "Name": "BUILT_UP_AREA_LIT_BUFFER", "default": 100.0, "value": 100.0, "type": "float", "description": "", "changed": False }, { "Name": "BUILT_UP_AREA_LIT_HOLES_MIN_AREA", "default": 100000.0, "value": 100000.0, "type": "float", "description": "", "changed": False } ] }, { "Group_Name": "Textures", "Options": [ { "Name": "ATLAS_SUFFIX", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "TEXTURES_ROOFS_NAME_EXCLUDE", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "TEXTURES_FACADES_NAME_EXCLUDE", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "TEXTURES_ROOFS_PROVIDE_EXCLUDE", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "TEXTURES_FACADES_PROVIDE_EXCLUDE", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "TEXTURES_REGIONS_EXPLICIT", "default": "", "value": "", "type": "str", "description": "", "changed": False }, { "Name": "TEXTURES_EMPTY_LM_RGB_VALUE", "default": 35, "value": 35, "type": "int", "description": "", "changed": False }, ] }, { "Group_Name": "Details", "Options": [ { "Name": "C2P_PROCESS_POWERLINES", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "C2P_PROCESS_WIND_TURBINES", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "C2P_PROCESS_STORAGE_TANKS", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "C2P_PROCESS_CHIMNEYS", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "C2P_PROCESS_POWERLINES_MINOR", "default": False, "value": False, "type": "bool", "description": "", "changed": False }, { "Name": "C2P_PROCESS_AERIALWAYS", "default": False, "value": False, "type": "bool", "description": "", "changed": False }, { "Name": "C2P_PROCESS_OVERHEAD_LINES", "default": False, "value": False, "type": "bool", "description": "", "changed": False }, { "Name": "C2P_PROCESS_STREETLAMPS", "default": False, "value": False, "type": "bool", "description": "", "changed": False }, { "Name": "DETAILS_PROCESS_PIERS", "default": True, "value": True, "type": "bool", "description": "", "changed": False }, { "Name": "DETAILS_PROCESS_PLATFORMS", "default": True, "value": True, "type": "bool", "description": "", "changed": False } ] } ] class colors: SELECTED = '\033[30;42m' HIGHLIGHT = '\033[30;46m' ERR = '\033[37;41m' HELP_HIGHLIGHT = '\033[96m' NOT_SAVED = '\033[33m' ENDC = '\033[0m' try: print("Reading ini file...") ini_file = open("projects/" + project + "/params.ini", "r") for line in ini_file: for group in options: for option in group["Options"]: ret = re.search(option["Name"] + " = (.*)", line) if ret: value = ret.group(1).replace("'", "") value = value.replace('"', "") if option["type"] == "bool": if value == "False": option["value"] = False else: option["value"] = True elif option["type"] == "str": option["value"] = str(value) elif option["type"] == "float": option["value"] = float(value) elif option["type"] == "int": option["value"] = int(value) except: print("A problem occured while reading the params.ini file...") sys.exit(1) finally: ini_file.close() try: print("Reading descriptions file...") # lines has to have following structure: # ;;description # All lines not matching this format are considered comments and are ignored # Group number is the number it has in the options array # Parameter number is the number it has in the "Options" array desc_file = open("descriptions", "r") for line in desc_file: ret = re.search("^(.*);(.*);(.*)$", line) if ret: options[int(ret.group(1))]["Options"][int(ret.group(2))]["description"] = ret.group(3).replace('\\n', '\n') except: print("A problem occured while reading the descriptions file...") ret = input("Do you want to continue without it (descriptions will not be available)? (y/n)") if ret != "y": sys.exit(0) finally: desc_file.close() line_sel = 1 mode = "group-select" selected_group = "" err_msg = "" saved = True while True: os.system('clear') print(colors.ERR + err_msg + colors.ENDC) draw_line = 1 while draw_line < int(console_height) - 2: line = " " try: if mode == "group-select": line += options[draw_line - 1]["Group_Name"] line = fill_space(line, 24) opt_count = len(options[draw_line - 1]["Options"]) if opt_count < 10: line += " " line += str(opt_count) elif mode == "option-select": line += options[selected_group]["Options"][draw_line - 1]["Name"] line = fill_space(line, 46) value = str(options[selected_group]["Options"][draw_line - 1]["value"]) if options[selected_group]["Options"][draw_line - 1]["changed"]: value = colors.NOT_SAVED + value + colors.ENDC elif options[selected_group]["Options"][draw_line - 1]["value"] != options[selected_group]["Options"][draw_line - 1]["default"]: value = colors.HELP_HIGHLIGHT + value + colors.ENDC line += value except IndexError: pass if line_sel == draw_line: line = colors.SELECTED + line + colors.ENDC print(line) draw_line += 1 # Status and help line print("h" + colors.HIGHLIGHT + "Help " + colors.ENDC + "j" + colors.HIGHLIGHT + "Down " + colors.ENDC + "k" + colors.HIGHLIGHT + "Up " + colors.ENDC + "Enter" + colors.HIGHLIGHT + "Select" + colors.ENDC + "q" + colors.HIGHLIGHT + "Quit " + colors.ENDC + "s" + colors.HIGHLIGHT + "Save " + colors.ENDC) key = ord(getch()) # print(key) # sleep(1) if key == 27 or key == 113: # ESC or Q if saved: break else: ret = input("You have made changes but you haven't saved. Do you really want to quit? (y/n): ") if ret == "y": break elif key == 13: # Enter if mode == "group-select": mode = "option-select" selected_group = line_sel - 1 line_sel = 1 elif mode == "option-select": if options[selected_group]["Options"][line_sel - 1]["type"] == "bool": options[selected_group]["Options"][line_sel - 1]["value"] = not options[selected_group]["Options"][line_sel - 1]["value"] err_msg = "" saved = False options[selected_group]["Options"][line_sel - 1]["changed"] = True elif options[selected_group]["Options"][line_sel - 1]["type"] == "int": ret = input("Please enter a new value of type INT for '" + options[selected_group]["Options"][line_sel - 1]["Name"] + "': ") search = re.search("^[0-9]*$", ret) if search: options[selected_group]["Options"][line_sel - 1]["value"] = int(ret) err_msg = "" saved = False options[selected_group]["Options"][line_sel - 1]["changed"] = True else: err_msg = "'" + ret + "' is no valid value for type INT" elif options[selected_group]["Options"][line_sel - 1]["type"] == "float": ret = input("Please enter a new value of type FLOAT for '" + options[selected_group]["Options"][line_sel - 1]["Name"] + "': ") search = re.search("^[0-9]*\.[0-9]*$|^[0-9]*$", ret) if search: options[selected_group]["Options"][line_sel - 1]["value"] = float(ret) err_msg = "" saved = False options[selected_group]["Options"][line_sel - 1]["changed"] = True else: err_msg = "'" + ret + "' is no valid value for type FLOAT" elif options[selected_group]["Options"][line_sel - 1]["type"] == "str": options[selected_group]["Options"][line_sel - 1]["value"] = input("Please enter a new value of type STRING for '" + options[selected_group]["Options"][line_sel - 1]["Name"] + "': ") err_msg = "" saved = False options[selected_group]["Options"][line_sel - 1]["changed"] = True elif key == 106: # J if mode == "group-select": if line_sel < len(options): line_sel += 1 elif mode == "option-select": if line_sel < len(options[selected_group]["Options"]): line_sel += 1 elif key == 107: # K if line_sel > 1: line_sel -= 1 elif key == 104: # H os.system('clear') print(colors.HELP_HIGHLIGHT + "This program is released as part of easy-osm2city.") print("See the LICENSE file for more information." + colors.ENDC) print("\nIn group select mode you can select a group of parameters.") print("The groups are similar as the groups on the official docs site.") print("\nIn option select mode you can edit the parameters value.") print("\nControls:") print(colors.HELP_HIGHLIGHT + " j: " + colors.ENDC + "Scroll down") print(colors.HELP_HIGHLIGHT + " k: " + colors.ENDC + "Scroll up") print(colors.HELP_HIGHLIGHT + " b: " + colors.ENDC + "Go back to group view") print(colors.HELP_HIGHLIGHT + " Enter: " + colors.ENDC + "In group view, select group") print(" On bool value, toggle True/False") print(" On other values, bring up dialog to change value") print(colors.HELP_HIGHLIGHT + " r: " + colors.ENDC + "Resets the selected parameter to the default value") print(colors.HELP_HIGHLIGHT + " d: " + colors.ENDC + "Shows parameter description") print(colors.HELP_HIGHLIGHT + " s: " + colors.ENDC + "Saves the changes to the params.ini file") print(colors.HELP_HIGHLIGHT + " ESC q: " + colors.ENDC + "Exit the program. Ask if should be exited if changes were not saved") print("Press any key to return.") getch() elif key == 100: # D if mode == "option-select": os.system('clear') print(options[selected_group]["Options"][line_sel - 1]["description"]) print(colors.HELP_HIGHLIGHT + "\n\n current value: " + colors.ENDC + str(options[selected_group]["Options"][line_sel - 1]["value"])) print(colors.HELP_HIGHLIGHT + " default value: " + colors.ENDC + str(options[selected_group]["Options"][line_sel - 1]["default"])) print(colors.HELP_HIGHLIGHT + "\nPress any key to return." + colors.ENDC) getch() elif key == 98: # B if mode == "option-select": mode = "group-select" line_sel = selected_group + 1 err_msg = "" elif key == 115: # S ini_file = open("projects/" + project + "/params.ini", "w") for group in options: written = False for option in group["Options"]: # Write only non default values to file. if option["default"] != option["value"]: if option["type"] == "str": ini_file.write(option["Name"] + ' = "' + str(option["value"]) + '"\n') else: ini_file.write(option["Name"] + " = " + str(option["value"]) + "\n") written = True if written: ini_file.write("\n") # make the file more readable by seperating in blocks ini_file.close() saved = True for i in range(0, len(options)): for j in range(0, len(options[i]["Options"])): options[i]["Options"][j]["changed"] = False elif key == 114: # R if mode == "option-select": options[selected_group]["Options"][line_sel - 1]["value"] = options[selected_group]["Options"][line_sel - 1]["default"]