182 lines
7.1 KiB
Python
182 lines
7.1 KiB
Python
|
|
||
|
# -------------------------------------------------------------------
|
||
|
# 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):
|
||
|
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)
|
||
|
|
||
|
|
||
|
# Individual testing
|
||
|
#scn = mstr_xp_scenery(51, 7, 101, 64, 0.010102, "+51+007")
|
||
|
#scn.acquire_xes_data()
|
||
|
#scn.build_mesh_script()
|
||
|
#scn.build_mesh()
|