# ------------------------------------------------------------------- # ORTHOGRAPHIC # Your personal aerial satellite. Always on. At any altitude.* # Developed by MarStrMind # License: Open Software License 3.0 # Up to date version always on marstr.online # ------------------------------------------------------------------- # xp_scenery.py # This class builds an elevation mesh from provided DEM data, and # generates all required files that are needed to provide a usable # scenery package for X-Plane. # ------------------------------------------------------------------- import os import math import urllib.request from defines import * from log import * class mstr_xp_scenery: # Set required variables def __init__(self, lat, lng, mlat, mlng, vstep, latlngfld): mstr_msg("xp_scenery", "[X-Plane] Scenery generator instantiated") self._lat = lat self._lng = lng self._mlat = mlat self._mlng = mlng self._vstep = vstep self._latlngfld = latlngfld self._demfn = self.build_dem_filename() # Build the correct file name for the elevation model def build_dem_filename(self, xes=False): fn = "" if self._lat > 0: fn = fn + "N" else: fn = fn + "S" if abs(self._lat) < 10: fn = fn + "0" + str(self._lat) if abs(self._lat) >= 10 and abs(self._lat) <= 90: fn = fn + str(self._lat) if self._lng > 0: fn = fn + "E" else: fn = fn + "W" if abs(self._lng) < 10: fn = fn + "00" + str(self._lng) if abs(self._lng) >= 10 and abs(self._lng) <= 99: fn = fn + "0" + str(self._lng) if abs(self._lng) >= 100 : fn = fn + str(self._lng) if xes == False: fn = fn + ".hgt" if xes == True: fn = fn + ".xes" mstr_msg("xp_scenery", "[X-Plane] DEM file name constructed: " + fn) return fn # Generate the mesh script for the ortho photos def build_mesh_script(self): scr = mstr_datafolder + "z_orthographic/data/meshscript.txt" # Before we blast all these lines into the file, we need to make sure they do not exist already write_lines = True if os.path.isfile(scr) == True: fnlines = [] with open(scr) as textfile: fnlines = textfile.readlines() for line in fnlines: l = line.split(" ") if l[2] == str(self._lng) and l[3] == str(self._lat): write_lines = False break else: open(scr, 'a').close() # If we did not find the initial corner coordinate in the script, we can go ahead if write_lines == True: mstr_msg("xp_scenery", "[X-Plane] Writing mesh script file") # We basically run through all tiles and note down the position of the orthos # as needed by X-Plane. cur_lat = self._lat cur_lng = self._lng for lat in range(1, self._mlat+1): for lng in range(1, self._mlng+1): # The '1' after 'ORTHOPHOTO' defines we want water underneath transparent parts of the DDS texture/ortho. # This ensures that even if the mesh does not include information for there being a water body, # we will get 100% correct representation of the water bodies. scrtxt = "ORTHOPHOTO 1 " + str(cur_lng) + " " + str(cur_lat) + " " + str(round(cur_lng+mstr_zl_18, 6)) + " " + str(cur_lat) + " " + str(round(cur_lng+mstr_zl_18, 6)) + " " + str(round(cur_lat+self._vstep, 6)) + " " + str(cur_lng) + " " + str(round(cur_lat+self._vstep, 6)) + " terrain/" + self._latlngfld + "/" + str(lat) + "_" + str(lng) + ".ter\n" with open(scr, 'a') as textfile: textfile.write(scrtxt) cur_lng = round(cur_lng + mstr_zl_18, 6) cur_lng = self._lng cur_lat = round(cur_lat + self._vstep, 6) mstr_msg("xp_scenery", "[X-Plane] Mesh script completed") # Find the next "by-ten" numbers for the current latitude and longitude def find_earthnavdata_number(self): earthnavdata = [] lat = abs(int(self._lat / 10) * 10) lng = abs(int(self._lng / 10) * 10) earthnavdata.append(lat) earthnavdata.append(lng) return earthnavdata # Construct an X-Plane compatible folder name for latitude and longitude def xplane_latlng_folder(self, numbers): fstr = "" if numbers[0] >= 0: fstr = "+" if numbers[0] < 0: fstr = "-" if abs(numbers[0]) < 10: fstr = fstr + "0" + str(numbers[0]) if abs(numbers[0]) >= 10 and numbers[0] <= 90: fstr = fstr + str(numbers[0]) if numbers[1] >= 0: fstr = fstr + "+" if numbers[1] < 0: fstr = fstr + "-" if abs(numbers[1]) < 10: fstr = fstr + "00" + str(numbers[1]) if abs(numbers[1]) >= 10 and numbers[0] <= 99: fstr = fstr + "0" + str(numbers[1]) if abs(numbers[1]) >= 100 : fstr = fstr + str(numbers[1]) return fstr # Acquires the elevation data as needed - # you can either acquire the older STRM set with lower resolution, # or a modern LIDAR scan with higher resolution. However, the # LIDAR files are much larger and generates a more taxing mesh. # If you are testing, acquire the low resolution file. # Both versions are coming from my repository at marstr.online . def acquire_elevation_data(self): mstr_msg("xp_scenery", "[X-Plane] Acquiring DEM model data") url = "https://marstr.online/dem/" url = url + self._demfn urllib.request.urlretrieve(url, mstr_datafolder + "_cache/" + self._demfn) mstr_msg("xp_scenery", "[X-Plane] DEM data acquired") # Download the X-Plane definition file from my server def acquire_xes_data(self): mstr_msg("xp_scenery", "[X-Plane] Acquiring XES file") url = "https://marstr.online/xes/" xesfn = self.build_dem_filename(True) xp_folder = self.xplane_latlng_folder([self._lat, self._lng]) url = url + xp_folder + ".xes" urllib.request.urlretrieve(url, mstr_datafolder + "_cache/" + xesfn) mstr_msg("xp_scenery", "[X-Plane] XES data acquired") # This builds the entire mesh in one go def build_mesh(self): mstr_msg("xp_scenery", "[X-Plane] Building DSF mesh") end_bt = self.find_earthnavdata_number() btlfn = str(self.xplane_latlng_folder(end_bt)) xp_folder = self.xplane_latlng_folder([self._lat, self._lng]) scr = mstr_datafolder + "z_orthographic/data/meshscript.txt" wd = mstr_datafolder + "z_orthographic/data" dsf = mstr_datafolder + "z_orthographic/Earth nav data/" + btlfn + "/" + xp_folder xesfn = self.build_dem_filename(True) # The main command to build the mesh cmd = mstr_xp_meshtool + " \"" + scr + "\" \"" + mstr_datafolder + "_cache/" + xesfn + "\"" + " \"" + mstr_datafolder + "_cache/" + self._demfn + "\" \"" + wd + "\" \"" + dsf + ".dsf\"" os.system(cmd) mstr_msg("xp_scenery", "[X-Plane] Mesh construction complete") # This generates all .ter files def build_ter_files(self): mstr_msg("xp_scenery", "[X-Plane] Generating and writing terrain (.ter) files") cur_lat = self._lat cur_lng = self._lng xp_folder = self.xplane_latlng_folder([self._lat, self._lng]) for lat in range(1, self._mlat+1): for lng in range(1, self._mlng+1): terstr = "" terstr = terstr + "A\n" terstr = terstr + "800\n" terstr = terstr + "TERRAIN\n" terstr = terstr + "\n" terstr = terstr + "BASE_TEX_NOWRAP ../orthos/"+xp_folder+"/"+str(lat)+"_"+str(lng)+".dds\n" if mstr_xp_scn_normalmaps == True: terstr = terstr + "TEXTURE_NORMAL ../normals/"+xp_folder+"/"+str(lat)+"_"+str(lng)+".dds\n" terfln = mstr_datafolder + "z_orthographic/terrain/"+xp_folder+"/"+str(lat)+"_"+str(lng)+".ter" with open(terfln, 'w') as textfile: textfile.write(terstr) mstr_msg("xp_scenery", "[X-Plane] Terrain files written")