]> marstr Code Repo - orthographic/commitdiff
Removed wedgen in favor of the initial code for a class that generates X-Plane scener...
authormarstr <marcus@marstr.online>
Sat, 7 Sep 2024 18:50:39 +0000 (20:50 +0200)
committermarstr <marcus@marstr.online>
Sat, 7 Sep 2024 18:50:39 +0000 (20:50 +0200)
defines.py
layergen.py
maskgen.py
orthographic.py
photogen.py
tiledb.py
tilegen.py
xp_dsfgen.py [new file with mode: 0644]

index f2a756c30870e8944a599fc20724fbfabc7e7824..fc77d12bcdceafc8918077cdab2379988ad15862 100644 (file)
@@ -66,6 +66,14 @@ mstr_shadow_casters = [
 ]
 
 
+# Whether or not to generate X-Plane Scenery files
+mstr_xp_genscenery = True
+
+# X-Plane specific
+mstr_xp_dsftool = "M:/Developer/Projects/orthographic/bin/DSFTool.exe"
+mstr_xp_ddstool = "M:/Developer/Projects/orthographic/bin/DDSTool.exe"
+mstr_xp_folder  = "M:/Flight Sim/Simulator/11/"
+
 # How much of a tile we need for each zoom level. The higher
 # the zoom level, the smaller the area to generate a mask of - but also
 # higher detail.
index d3e157ed014bdee9784c52b73336d81c1b484137..fdd993223d14ee985ea918486e5372a88c8574d5 100644 (file)
@@ -61,7 +61,7 @@ class mstr_layergen:
     # other effects.
     def genborder(self, edgemask, tag, value):
         layer = Image.new("RGBA", (self._imgsize, self._imgsize))
-        root_folder = mstr_datafolder + "Textures/" + tag + "/" + value
+        root_folder = mstr_datafolder + "textures/" + tag + "/" + value
 
         # Determine which sources we use
         brd = glob.glob(root_folder + "/brd/b*.png")
@@ -136,7 +136,7 @@ class mstr_layergen:
 
         if (self._isline == False and self._tag != "building") or (self._is_completion == True):
             # Determine where we get the our source material from
-            root_folder = mstr_datafolder + "Textures/"
+            root_folder = mstr_datafolder + "textures/"
             for s in mstr_ortho_layers:
                 if s[0] == self._tag and s[1] == self._value:
                     fld_main = len(s)-2
@@ -179,7 +179,7 @@ class mstr_layergen:
                     self._tag   = al[0][2]
                     self._value = al[0][3]
 
-                root_folder = mstr_datafolder + "Textures/"
+                root_folder = mstr_datafolder + "textures/"
                 for s in mstr_ortho_layers:
                     if s[0] == self._tag and s[1] == self._value:
                         fld_main = len(s)-2
@@ -261,7 +261,7 @@ class mstr_layergen:
                         self._tag   = ald[0][2]
                         self._value = ald[0][3]
 
-                    root_folder = mstr_datafolder + "Textures/"
+                    root_folder = mstr_datafolder + "textures/"
                     for s in mstr_ortho_layers:
                         if s[0] == self._tag and s[1] == self._value:
                             fld_main = len(s)-2
@@ -373,7 +373,7 @@ class mstr_layergen:
                 amt = randrange(1,5)
                 for i in range(1, amt+1):
                     ptc = randrange(1, 14)
-                    img = Image.open(mstr_datafolder + "Textures/tile/completion/p" + str(ptc)+".png")
+                    img = Image.open(mstr_datafolder + "textures/tile/completion/p" + str(ptc)+".png")
                     lx = randrange( int(layer.width/20), layer.width - (int(layer.width/20)) - img.width ) 
                     ly = randrange( int(layer.width/20), layer.width - (int(layer.width/20)) - img.height )
                     layer.alpha_composite( img, (lx, ly) )
@@ -692,7 +692,7 @@ class mstr_layergen:
                                             for i in range(1, numtrees+1):
                                                 # Pick some file
                                                 pick = str(randrange(1, 11))
-                                                tree = Image.open(mstr_datafolder + "Textures/building/area/p" + pick + ".png")
+                                                tree = Image.open(mstr_datafolder + "textures/building/area/p" + pick + ".png")
                                                 # Do a correction for the location if needed
                                                 if shf_x < 1: shf_x = 1
                                                 if shf_y < 1: shf_y = 1
index 4a0d0b436e69aef556e2a0afd5899b8cb3805ac4..c3153604c5fefdd8b2d451a8ff84c3089a8a895a 100644 (file)
@@ -43,9 +43,7 @@ class mstr_maskgen:
         self._vstep = vstep
         self._scale = 1 / math.cos(math.radians(self._box[0]))
         self._isline = isline
-        self._tiledb = mstr_tiledb(self._box[0], self._box[2])
-        if xml != None:
-            self._xml = xml
+
         #mstr_msg("maskgen", "Intialized mask gen.")
 
 
@@ -68,6 +66,11 @@ class mstr_maskgen:
                 latlng.append(float(i[2]))
                 break
         return latlng
+    
+
+    # Only needed if X-Plane scenery is built
+    def _set_xpscenery_datagroup(self, dg):
+        self._xpdg = dg
 
 
     # Builds the required mask
@@ -139,9 +142,6 @@ class mstr_maskgen:
                 if len(latlng) == 2:
                     # For some reason, sometimes the array is empty. Make sure we have two data points.
                     if len(latlng) == 2:
-                        # Insert a WED data point should this be a forest:
-                        if self._tag == "landuse" and self._value == "forest":
-                            self._tiledb.insert_wed_datapoint(1, 1, latlng[0], latlng[1])
 
                         # Project the pixel, and add to the polygon shape.
                         p_lat = self.project_pixel(latlng[0], bbox[1])
@@ -190,8 +190,3 @@ class mstr_maskgen:
         mask_img.save(mstr_datafolder + "_cache/" + fstr + "_" + self._tag + "-" + self._value + ".png")
         # Inform
         mstr_msg("maskgen", "Mask built.")
-
-        # Close the DB
-        self._tiledb.close_db()
-
-
index a860a019573a0631e9fccfa66165611b028c876b..4beb2bb484afcc181f3c3e090e7ad35b3c4b7d71 100644 (file)
@@ -131,6 +131,9 @@ class mstr_orthographic:
         bb_lat = self._lat
         bb_lng = self._long
 
+        # For X-Plane scenery generation
+        xp_datagroup = 1
+
         # Previously, I downloaded all XML files in one go - but to ease the
         # stress on OSM servers and my server, we will do acquire the data
         # only for the current processed part of the tile.
@@ -141,7 +144,7 @@ class mstr_orthographic:
                 mstr_msg("orthographic", "Adjusted bounding box for XML object")
 
                 # Determine what to do... maybe work was interrupted
-                if os.path.isfile(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._long) + "/Textures/" + str(cur_tile_y) + "_" + str(cur_tile_x) + ".jpg") == False:
+                if os.path.isfile(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._long) + "/textures/" + str(cur_tile_y) + "_" + str(cur_tile_x) + ".jpg") == False:
 
                     # Let the user know
                     mstr_msg("orthographic", "Generating missing orthophoto " + str(cur_tile_y) + "-" + str(cur_tile_x))
@@ -176,6 +179,8 @@ class mstr_orthographic:
 
                         # Generate the mask
                         mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2])
+                        if mstr_xp_genscenery == True:
+                            mg._set_xpscenery_datagroup(xp_datagroup)
                         mg._build_mask()
                         
                         # Generate the layer
@@ -191,6 +196,8 @@ class mstr_orthographic:
                     pg = mstr_photogen(self._lat, self._long, cur_tile_y, cur_tile_x, maxlatlng[0], maxlatlng[1])
                     pg.genphoto()
                     mstr_msg("orthographic", " -- Ortho photo generated -- ")
+                    if mstr_xp_genscenery == True:
+                        xp_datagroup = xp_datagroup + 1
                     print("")
                     print("")
 
@@ -235,10 +242,7 @@ class mstr_orthographic:
         tg.genTiles()
         mstr_msg("orthographic", "Final step completed.")
         print("")
-        print("")
         mstr_msg("orthographic", "Tile data in: " + mstr_datafolder + "/Tiles/" + str(self._lat) + "_" + self._lng)
-        mstr_msg("orthographic", "Orthos are in the Textures subfolder")
-        mstr_msg("orthographic", "X-Plane .ter's are in the terrain subfolder")
         print("")
         print("")
         mstr_msg("orthographic", "Thanks for using Orthographic! -- Best, Marcus")
index 4610d4d3efc1bb6b73988b64e3daf79d006a670e..9c19206897bb99a006bdba3758fb6ad41bda8295 100644 (file)
@@ -104,7 +104,7 @@ class mstr_photogen:
         self._tile = self._tile.resize((mstr_photores, mstr_photores), Image.Resampling.BILINEAR)
         
         # This we can save accordingly.
-        self._tile.convert('RGB').save(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/Textures/" + str(self._ty) + "_" + str(self._tx) + ".jpg", format='JPEG', subsampling=0, quality=100)
+        self._tile.convert('RGB').save(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/textures/" + str(self._ty) + "_" + str(self._tx) + ".jpg", format='JPEG', subsampling=0, quality=100)
 
 
     # This checks the final image for empty patches. Should one be
index c933b903d1af87f0d6c94562c7e477589c9ff883..021f59e5da13fadb874910f8e48df0c44613d681 100644 (file)
--- a/tiledb.py
+++ b/tiledb.py
@@ -40,8 +40,6 @@ class mstr_tiledb:
         self._conn.execute("CREATE TABLE IF NOT EXISTS tiledata (tile_v INTEGER, tile_h INTEGER, tag TEXT, value TEXT, source INTEGER, adjacent TEXT);")
         self._conn.execute("CREATE TABLE IF NOT EXISTS airports (icao TEXT, tile_v INTEGER, tile_h INTEGER, latitude REAL, longitude REAL);")
         self._conn.execute("CREATE TABLE IF NOT EXISTS completion (tile_v INTEGER, tile_h INTEGER, tag TEXT, value TEXT, source INTEGER, adjacent TEXT);")
-        self._conn.execute("CREATE TABLE IF NOT EXISTS weddata (datatype INTEGER, datagroup INTEGER, latitude REAL, longitude REAL);")
-        #mstr_msg("tiledb", "Tables created")
 
     
     # Insert data into their segments
@@ -56,12 +54,6 @@ class mstr_tiledb:
     def insert_icao(self, icao, tv, th, lat, lng):
         self._conn.execute("INSERT INTO airports VALUES ('"+icao+"', "+str(tv)+", "+str(th)+", "+str(lat)+", "+str(lng)+");")
 
-    # Inserts a data point for WED generation
-    def insert_wed_datapoint(self, dtype, dgroup, lat, lng):
-        self._conn.execute("INSERT INTO weddata VALUES( "+str(dtype)+","+str(dgroup)+","+str(lat)+","+str(lng)+" );")
-        self.commit_query()
-
-
     # Commit a query or a number of queries
     def commit_query(self):
         self._conn.commit()
@@ -133,6 +125,7 @@ class mstr_tiledb:
         
         return latlng
 
+
     # Get all tiles with detected airports (ICAO codes)
     def get_tiles_with_airports(self):
         r = self._crs.execute("SELECT * FROM airports")
index d64dc2656c389eb94a4ad390b79e32bb4f244051..92e704053651d5d974bcf10c1b82bfd09aa25cd8 100644 (file)
@@ -68,7 +68,7 @@ class mstr_tilegen:
         for lt in range(1, steps_lat):
             for ln in range(1, steps_lng):
                 # Check if we need to do something
-                if os.path.isfile(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/Textures/" + str(self._lat) + "-" + str(ln) + "_" + str(self._lng) + "-" + str(lt) + "_OG16.jpg") == False:
+                if os.path.isfile(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/textures/" + str(self._lat) + "-" + str(ln) + "_" + str(self._lng) + "-" + str(lt) + "_OG16.jpg") == False:
 
                     mstr_msg("tilegen", "Generating missing zoom level 16 ortho " + str(self._lat) + "-" + str(ln) + "_" + str(self._lng) + "-" + str(lt) + "_OG16.jpg")
 
@@ -85,7 +85,7 @@ class mstr_tilegen:
                         for j in range(0, 3):
                             # We may run into situations where ask for tiles that don't exist...
                             # Let's make sure we can continue
-                            fpath = mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/Textures/" + str(tiles[i][j][0]) + "_" + str(tiles[i][j][1]) + ".jpg"
+                            fpath = mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/textures/" + str(tiles[i][j][0]) + "_" + str(tiles[i][j][1]) + ".jpg"
                             if os.path.isfile( fpath ):
                                 tlimg = Image.open(fpath)
                                 tlimg = tlimg.resize((scaled_res,scaled_res), Image.Resampling.BILINEAR)
@@ -95,19 +95,7 @@ class mstr_tilegen:
                         ypos = ypos - scaled_res
 
                     # Now save this image
-                    zl16.save(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/Textures/" + str(self._lat) + "-" + str(ln) + "_" + str(self._lng) + "-" + str(lt) + "_OG16.jpg", format='JPEG', subsampling=0, quality=100)
-
-                    # Store a terrain file
-                    dmt = self._findWidthOfLongitude(a_lat) * mstr_zl_16
-                    ter_content = """A
-800
-TERRAIN
-
-LOAD_CENTER """ + str(a_lat) + " " + str(a_lng) + " " + str(dmt) + " " + "../Textures/" + str(self._lat) + "-" + str(ln) + "_" + str(self._lng) + "-" + str(lt) + "_OG_16.jpg"+"""
-NO_ALPHA"""
-                    with open(mstr_datafolder + "/Tiles/"+str(self._lat)+"_"+str(self._lng)+"/terrain/"+str(self._lat)+"_"+str(lt)+"-"+str(self._lng)+"-"+str(ln)+"_OG16.ter", 'w') as textfile:
-                        textfile.write(ter_content)
-                    mstr_msg("tilegen", "Wrote .ter file")
+                    zl16.save(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/textures/" + str(self._lat) + "-" + str(ln) + "_" + str(self._lng) + "-" + str(lt) + "_OG16.jpg", format='JPEG', subsampling=0, quality=100)
 
                 # Adjust
                 a_lng = a_lng + (mstr_zl_16 * 4)
@@ -156,28 +144,9 @@ NO_ALPHA"""
                         found = True
                         break
                 if found == False:
-                    os.remove(mstr_datafolder + "/Tiles/" + str(self._lat) + "_" + str(self._lng) + "/Textures/" + fn)
+                    os.remove(mstr_datafolder + "/Tiles/" + str(self._lat) + "_" + str(self._lng) + "/textures/" + fn)
         mstr_msg("tilegen", "Cleanup completed")
 
-
-        # And now for the final act of tonight's entertainment
-        mstr_msg("tilegen", "Writing .ter files for ZL18 tiles")
-
-        for k in keeping:
-            k_lat = self._lat + (k[0] * self._vstep) + (self._vstep * 0.5)
-            k_lng = self._lat + (k[1] * mstr_zl_18) + (mstr_zl_18 * 0.5)
-            k_dmt = self._findWidthOfLongitude(self._lng * mstr_zl_18)
-            k_fln = mstr_datafolder + "/Tiles/" + str(self._lat) + "_" + str(self._lng) + "/terrain/" + str(k[0]) + "_" + str(k[1]) + ".ter"
-            ter_content = """A
-800
-TERRAIN
-
-LOAD_CENTER """ + str(k_lat) + " " + str(k_lng) + " " + str(k_dmt) + " " + "../Textures/" + str(k[0]) + "_" + str(k[1]) + ".jpg"+"""
-NO_ALPHA"""
-            with open(k_fln, 'w') as textfile:
-                    textfile.write(ter_content)
-        mstr_msg("tilegen", "Wrote all .ter files for ZL18 tiles.")
-
         mstr_msg("tilegen", "Work complete.")
 
 
diff --git a/xp_dsfgen.py b/xp_dsfgen.py
new file mode 100644 (file)
index 0000000..e4ccfd8
--- /dev/null
@@ -0,0 +1,158 @@
+
+# -------------------------------------------------------------------
+# 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_dsfgen.py
+# This class is coming into play at the very end of the tile
+# generation process, and builds the DSF (Distributable Scenery
+# Format) file for X-Plane.
+#
+# For this, you will need two tools which I cannot re-distribute:
+# - DSFTool
+# - DDSTool
+#
+# You can download both of these for free from X-Plane's website.
+# Place them somewhere convenient, and point to them in the
+# xp_ variables in defines.py
+# -------------------------------------------------------------------
+
+import os
+import glob
+import math
+from random import randrange
+from log import *
+from tiledb import *
+
+class mstr_xp_dsfgen:
+    # Instantiate with Lat/Lng, as usual
+    def __init__(self, lat, lng, amtdg):
+        self._latitude = lat
+        self._longitude = lng
+        self._tiledb = mstr_tiledb(lat, lng)
+        self._tmpdsf = mstr_datafolder + "_cache/tiledsf.txt"
+        self._dsfstring = ""
+        self._amtdg = amtdg
+        mstr_msg("xp_dsfgen", "DSFgen initialized")
+        self.build_header()
+
+    
+    # Construct header of DSF txt
+    def build_header(self):
+        self._dsfstring = self._dsfstring + "PROPERTY sim/west " + str(int(self._longitude)) + "\n"
+        self._dsfstring = self._dsfstring + "PROPERTY sim/east " + str(int(self._longitude + 1)) + "\n"
+        self._dsfstring = self._dsfstring + "PROPERTY sim/south " + str(int(self._latitude)) + "\n"
+        self._dsfstring = self._dsfstring + "PROPERTY sim/north " + str(int(self._latitude+1)) + "\n"
+        self._dsfstring = self._dsfstring + "PROPERTY sim/planet earth\n"
+        self._dsfstring = self._dsfstring + "PROPERTY sim/creation_agent Orthographic\n"
+        mstr_msg("xp_dsfgen", "Header built")
+
+
+    # Let's now walk through the single polygon definitions
+    def build_polygon_defs(self):
+        mstr_msg("xp_dsfgen", "Walking through forest polygons")
+        
+        # Pick the kind of forest we work with
+        for f in range(1, self._amtdg+1):
+            #rws = self._tiledb.perform_query("SELECT * FROM xp_scenery WHERE datagroup="+str(f)+";")
+            frs = self.pick_forest_type()
+            self._dsfstring = self._dsfstring + "POLYGON_DEF lib/g8/"+frs+"\n"
+
+        # Put in the data
+        curpoly=0
+        for f in range(1, self._amtdg+1):
+            rws = self._tiledb.perform_query("SELECT * FROM xpscenery WHERE datagroup="+str(f)+";")
+            self._dsfstring = self._dsfstring + "BEGIN_POLYGON "+str(curpoly)+" 255 2\n"
+            self._dsfstring = self._dsfstring + "BEGIN_WINDING\n"
+            for r in rws:
+                self._dsfstring = self._dsfstring + "POLYGON_POINT " + str(r[2]) + " " + str(r[1]) + "\n"
+            self._dsfstring = self._dsfstring + "END_WINDING\n"
+            self._dsfstring = self._dsfstring + "END_POLYGON\n"
+            curpoly = curpoly + 1
+        
+        mstr_msg("xp_dsfgen", "Forest definitions complete")
+
+
+    # Write the text file
+    def write_dsf_txt(self):
+        with open(mstr_datafolder + "_cache/dsf.txt", 'w') as textfile:
+            textfile.write(self._dsfstring)                
+
+
+    # Convert the DSF into actual, usable data for X-Plane
+    def convert_dsf_text(self):
+        # Find separator
+        sep = ""
+        if os.name == "nt":
+            sep = "\\"
+        if os.name == "posix":
+            sep = "/"
+        
+        datafolder = mstr_datafolder.replace("/", sep)
+
+        # First, create the Earth nav data folder should it not exist
+        end_base = datafolder + "Tiles" + sep + str(self._latitude) + "_" + str(self._longitude) + sep + "Earth nav data"
+
+        # Create the appropriate rounded folder
+        end_round = self.xplane_latlng_folder(self.find_earthnavdata_number())
+        if not os.path.exists(end_base):
+            os.makedirs(end_base)
+        if not os.path.exists(end_base + sep + end_round):
+            os.makedirs(end_base + sep + end_round)
+
+        # Get the file name for the DSF
+        end_latlng = self.xplane_latlng_folder([self._latitude, self._longitude])
+
+        # Perform conversion
+        os.system(mstr_xp_dsftool + " --text2dsf " + datafolder + "_cache" + sep + "dsf.txt \"" + end_base + sep + end_round + sep + end_latlng + ".dsf\"")
+
+
+    # Find the next "by-ten" numbers for the current latitude and longitude
+    def find_earthnavdata_number(self):
+        earthnavdata = []
+        lat = abs(int(self._latitude / 10) * 10)
+        lng = abs(int(self._longitude / 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
+
+    
+    # Pick some forest type from X-Plane
+    def pick_forest_type(self):
+        ftype = 0 
+
+        # Where forests live in X-Plane.
+        rootfolder = mstr_xp_folder + "Resources/default scenery/1000 forests/"
+        forests = glob.glob(rootfolder + "mixed_*.for")
+        ftype = randrange(1, len(forests)-1)
+        fstring = forests[ftype]
+        fstring = fstring.replace(mstr_xp_folder + "Resources/default scenery/1000 forests\\", "")
+
+        return fstring
+    
+
+
+dsf = mstr_xp_dsfgen(51, 7, 1)
+dsf.build_polygon_defs()
+dsf.write_dsf_txt()
+dsf.convert_dsf_text()
\ No newline at end of file