]> marstr Code Repo - orthographic/commitdiff
Initial code to generate the WED (X-Plane World Editor) XML file at the end of all...
authormarstr <marcus@marstr.online>
Thu, 5 Sep 2024 20:49:39 +0000 (22:49 +0200)
committermarstr <marcus@marstr.online>
Thu, 5 Sep 2024 20:49:39 +0000 (22:49 +0200)
defines.py
layergen.py
maskgen.py
orthographic.py
osmxml.py
repoinfo
tiledb.py
wedgen.py [new file with mode: 0644]

index a8fc5af18388af9e1e6a7f9f75490c3e59579e93..f2a756c30870e8944a599fc20724fbfabc7e7824 100644 (file)
@@ -50,7 +50,7 @@ mstr_show_log = True
 # We will simply things to achieve good-looking results.
 # You can, however, disable the shadow rendering layer here.
 mstr_shadow_enabled  = True
-mstr_shadow_strength = 0.65
+mstr_shadow_strength = 0.85
 mstr_shadow_shift    = 24
 # The tags that cast shadows
 mstr_shadow_casters = [
@@ -169,15 +169,15 @@ mstr_mask_blur = [
     ("landuse", "greenfield", 30),
     ("landuse", "orchard", 30),
     ("landuse", "meadow", 30),
-    ("barrier", "hedge", 12),
-    ("landuse", "recreation_ground", 30),
+    ("barrier", "hedge", 5),
+    ("landuse", "recreation_ground", 20),
     ("landuse", "vineyard", 30),
     ("natural", "grassland", 30),
     ("natural", "wetland", 30),
-    ("natural", "scrub", 30),
-    ("natural", "heath", 30),
+    ("natural", "scrub", 20),
+    ("natural", "heath", 20),
     ("leisure", "park", 30),
-    ("leisure", "golf_course", 35),
+    ("leisure", "golf_course", 25),
     ("leisure", "dog_park", 35),
     ("leisure", "garden", 20),
     ("leisure", "sports_centre", 5),
@@ -190,15 +190,15 @@ mstr_mask_blur = [
     ("landuse", "military", 30),
     # Z-Order 3
     ("natural", "bare_rock", 25),
-    ("natural", "water", 20),
+    ("natural", "water", 4),
     ("natural", "bay", 30),
     ("natural", "beach", 30),
-    ("water", "lake", 20),
-    ("water", "pond", 20),
-    ("water", "river", 20),
+    ("water", "lake", 10),
+    ("water", "pond", 10),
+    ("water", "river", 10),
     ("waterway", "river", 10),
     ("waterway", "stream", 10),
-    ("amenity", "parking", 10),
+    ("amenity", "parking", 3),
     ("highway", "pedestrian", 12),
     # Z-Order 4
     ("highway", "motorway", 5),
index 664514acd06b0fb798ef06f035e8adae7b0ea5d1..d3e157ed014bdee9784c52b73336d81c1b484137 100644 (file)
@@ -68,7 +68,7 @@ class mstr_layergen:
         src = -1
         if len(brd) == 1: src=1
         if len(brd) >= 2:
-            src = randrange(1, len(brd))
+            src = randrange(1, len(brd)+1)
         ptc = glob.glob(root_folder + "/ptc/b" + str(src) + "_p*.png")
         
         # Load in the sources to work with
@@ -84,7 +84,7 @@ class mstr_layergen:
             imgid = 0
             if len(ptc_src) == 1: imgid = 0
             if len(ptc_src) >= 2:
-                imgid = randrange(1, len(ptc_src)) - 1
+                imgid = randrange(1, len(ptc_src)+1) - 1
             l = 0 - int(ptc_src[imgid].width / 2)
             r = layer.width - int(ptc_src[imgid].width / 2)
             t = 0 - int(ptc_src[imgid].height / 2)
@@ -294,7 +294,7 @@ class mstr_layergen:
             if src == -1:
                 if len(brd) == 1: src=1
                 if len(brd) >= 2:
-                    src = randrange(1, len(brd))
+                    src = randrange(1, len(brd)+1)
             
             ptc = glob.glob(root_folder + "/ptc/b" + str(src) + "_p*.png")
             
@@ -359,7 +359,7 @@ class mstr_layergen:
                 imgid = 0
                 if len(ptc_src) == 1: imgid = 0
                 if len(ptc_src) >= 2:
-                    imgid = randrange(1, len(ptc_src)) - 1
+                    imgid = randrange(1, len(ptc_src)+1) - 1
                 l = 0 - int(ptc_src[imgid].width / 2)
                 r = layer.width - int(ptc_src[imgid].width / 2)
                 t = 0 - int(ptc_src[imgid].height / 2)
@@ -371,7 +371,7 @@ class mstr_layergen:
             # Here we need to do some magic to make some features look more natural
             if (self._tag == "landuse" and self._value == "meadow") or (self._tag == "natural" and self._value == "grassland") or (self._tag == "natural" and self._value == "heath"):
                 amt = randrange(1,5)
-                for i in range(1, amt):
+                for i in range(1, amt+1):
                     ptc = randrange(1, 14)
                     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 ) 
@@ -409,6 +409,12 @@ class mstr_layergen:
                     layer_border = self.genborder(osm_edge, "landuse", "meadow")
                     layer_comp.alpha_composite(layer_border)
 
+            # Edges for waters
+            if self._tag == "natural" and self._value == "water":
+                osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)
+                osm_edge = osm_edge.filter(ImageFilter.MaxFilter)
+                osm_edge = osm_edge.filter(ImageFilter.GaussianBlur(radius=2))
+                layer_comp.alpha_composite(osm_edge)
 
             # Store layer
             if self._is_completion == False:
@@ -612,6 +618,10 @@ class mstr_layergen:
                             
                             # Find a color range
                             d = randrange(1,21)
+                            # Bring in some variety by making the one or other pixel darker
+                            dp = randrange(1, 3)
+                            if dp == 2:
+                                d = d + 20
                             # Adjust this pixel
                             c = (bld_clr[cidx][1]-d, bld_clr[cidx][2]-d, bld_clr[cidx][3]-d, 255)
                             # Set pixel
@@ -641,7 +651,7 @@ class mstr_layergen:
                                 shf_x2 = x-randrange(1, 21)
                                 shf_y2 = y-randrange(1, 21)
                                 if shf_x <= self._imgsize-1 and shf_x >= 0 and shf_y <= self._imgsize-1 and shf_y >= 0:
-                                    st = random.uniform(0.85, 1.0)
+                                    st = random.uniform(0.65, 0.85)
                                     ca = 255 * st
                                     aa = int(ca)
                                     d = randrange(1,26)
@@ -653,7 +663,8 @@ class mstr_layergen:
                     layer_comp = details
                     # New edge
                     osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)
-                    osm_edge = osm_edge.filter(ImageFilter.GaussianBlur(radius=1))
+                    osm_edge = osm_edge.filter(ImageFilter.MaxFilter)
+                    osm_edge = osm_edge.filter(ImageFilter.GaussianBlur(radius=2))
                     # Blur the image
                     layer_comp = layer_comp.filter(ImageFilter.GaussianBlur(radius=1))
                     osm_edge.alpha_composite(layer_comp)
@@ -678,7 +689,7 @@ class mstr_layergen:
                                         if shf_x > 0 and shf_x < self._imgsize and shf_y > 0 and shf_y < self._imgsize:
                                             # Pick a number of trees to place
                                             numtrees = randrange(1, 16)
-                                            for i in range(1, numtrees):
+                                            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")
@@ -704,11 +715,11 @@ class mstr_layergen:
                     for y in range(self._imgsize-1):
                         for x in range(self._imgsize-1):
                             m = mask_pix[x,y]
-                            shf_x = x + randrange(1, mstr_shadow_shift)
-                            shf_x2 = x + randrange(1, mstr_shadow_shift)
+                            shf_x = x + randrange(1, mstr_shadow_shift + 1)
+                            shf_x2 = x + randrange(1, mstr_shadow_shift + 1)
                             if shf_x <= self._imgsize-1 and shf_x >= 0 and shf_x2 <= self._imgsize-1 and shf_x2 >= 0:
                                 a = mask_pix[x,y][3]
-                                st = random.uniform(0.45, mstr_shadow_strength)
+                                st = random.uniform(0.6, mstr_shadow_strength)
                                 ca = a * st
                                 aa = int(ca)
                                 shadow_pix[shf_x, y] = (0,0,0,aa)
index 52a992b29922356fad70fada43fa560cbedc80c6..4a0d0b436e69aef556e2a0afd5899b8cb3805ac4 100644 (file)
@@ -27,6 +27,7 @@ from defines import *
 from log import *
 from PIL import Image, ImageFilter, ImageDraw, ImagePath
 from random import randrange
+from tiledb import *
 import random
 
 class mstr_maskgen:
@@ -42,6 +43,9 @@ 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.")
 
 
@@ -135,6 +139,10 @@ 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])
                         p_lng = self.project_pixel(latlng[1], bbox[3])
@@ -183,4 +191,7 @@ class mstr_maskgen:
         # Inform
         mstr_msg("maskgen", "Mask built.")
 
+        # Close the DB
+        self._tiledb.close_db()
+
 
index 34a331c69b7a0c944e306d6b13c453e8e6108492..a860a019573a0631e9fccfa66165611b028c876b 100644 (file)
@@ -98,12 +98,6 @@ class mstr_orthographic:
             os.makedirs(self._output + "/Tiles/"+str(self._lat)+"_"+str(self._long))
             mstr_msg("orthographic", "Created Tiles sub folder: " +str(self._lat)+"_"+str(self._long))
         
-        # Note down diameter of entire tile
-        #tile_dm = round(self._findWidthOfLongitude(), 3)
-        #mstr_msg("orthographic", "Tile diameter: " + str(tile_dm) + "m")
-        #dm_of_18 = round(tile_dm * mstr_zl_18, 3)
-        #mstr_msg("orthographic", "Diameter of ZL 18 tile: " + str(dm_of_18) + "m")
-
         # The tile is constructed of many smaller parts. We walk through the
         # smallest possible, from which the bigger ones are later built.
         bb_lat = self._lat
@@ -181,7 +175,7 @@ class mstr_orthographic:
                         mstr_msg("orthographic", "Processing layer " + str(curlyr) + " of " + str(len(layers)))
 
                         # Generate the mask
-                        mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2] )
+                        mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2])
                         mg._build_mask()
                         
                         # Generate the layer
index 2a43a39fbf0d731b36dae9f21cd7eec8cff14a0b..faf529aadee8427e32fd9e723edc7fa159417b3f 100644 (file)
--- a/osmxml.py
+++ b/osmxml.py
@@ -37,7 +37,7 @@ class mstr_osmxml:
     
 
     # Acquire XMLs in chunks, then store them
-    def acquire_osm(self, v, h):
+    def acquire_osm(self, v, h, asobject=False):
         mstr_msg("osmxml", "Acquiring OSM data for " + str(self._lat)+","+str(self._lng)+" - "+str(self._curB_lat)+","+str(self._curB_lng))
         
         # We will use our self-hosted API for this.
@@ -55,12 +55,17 @@ class mstr_osmxml:
                 }
         r = requests.post(mstr_osm_endpoint, json=data)
 
-        xml = mstr_datafolder + "_cache/tile.xml"
-        if os.path.isfile(xml):
-            os.remove(xml)
-        with open(xml, 'wb') as textfile:
+        xmlf = mstr_datafolder + "_cache/tile.xml"
+        if os.path.isfile(xmlf):
+            os.remove(xmlf)
+        with open(xmlf, 'wb') as textfile:
             textfile.write(r.content)
 
+        # Provide the object directly
+        if asobject == True:
+            xml_doc = xml.dom.minidom.parse("_cache/tile.xml")
+            return xml_doc
+
 
     # Get all nodes from the specified OSM file
     def acquire_nodes(self, xmlfile):
@@ -108,7 +113,7 @@ class mstr_osmxml:
                     icao.append(v)
         # Return list of found airports
         return icao
-    
+        
 
     # Finds the surface type of a runway in the current data chunk.
     # If no surface type is specified, the runway will be rendered similar
@@ -139,7 +144,7 @@ class mstr_osmxml:
 
         # Return the found surface type
         return surface
-
+    
 
     # It turns out that some features hide themselves in the relations section.
     # I figured this out during testing, and almost going insane over the
@@ -163,4 +168,5 @@ class mstr_osmxml:
                         tgd.append(i.getAttribute("k"))
                         tgd.append(i.getAttribute("v"))
                     rls.append((rp.getAttribute("ref"), tgd))
-        return rls
\ No newline at end of file
+        return rls
+    
\ No newline at end of file
index e50ef2fd28c0df9e0fd259f73969790890eaf65e..4b37800c2dac110c312de857a312cd7bed149496 100644 (file)
--- a/repoinfo
+++ b/repoinfo
@@ -40,6 +40,8 @@ Orthographic does its very best to approximate good-looking ortho photos, as if
 
 It is, at the end of the day, for a flight simulator. Orthographic can strike the right balance between accuracy of the part of the world you fly in, and looks.
 
+Prior to publishing, there have been many iterations and hours of testing the generation of buildings. While the current state is far from perfect, it is as best I can make it - and I think we can agree that it looks good enough... for flight simulators. With the right tools in place, buildings will most likely be placed on many of these locations anyways.
+
 
 [section]Where it excels[/section]
 
@@ -67,12 +69,15 @@ Apart from that I am aware that the code is most likely not the best and can be
 
 - Current Python version
 - Pillow for Python
+- SQLite must be available to Python. On Windows it is by default, you may need to install the sqlite3 developer libraries on your distribution, then compile Python to automatically avail of this built-in module.
+
 
 [section]Configuration[/section]
 
 - Open the file defines.py
 - Change mstr_datafolder to where you want the data of Orthographic to be stored and placed
 - Change mstr_airport_radius to an amount in zoom level 18 tiles. Areas around airports with ICAO code will get aerials with zoom level 18. I recommend to leave the default at 5
+- You can turn console logging on or off by changing the variable mstr_show_log
 
 In this file you can also define how large you want the final products to be - you can choose between 4k resolution (2048px) or 16k resolution (4096). The choice you make here should be based on 1) how much detail you want, and 2) how much VRAM your GPU has. The larger a texture, the more VRAM is needed, and the more processing is required by the GPU. I personally go with 2048, as I have a RTX2060, so my VRAM is limited. When at altitudes of 10,000 feet and above, you will most definitely not notice the difference between 4096 and 2048.
 
@@ -82,6 +87,8 @@ Change the mstr_photores variable to 4096 if you want the maximum possible.
 
 Just a note: 4096 also uses 4x more hard drive space. 4k uses about 2MB per image, where as 16k uses about 8-10MB per image. This may not seem much for one image, but keep in mind we are talking about quite a considerable number of images. To get an idea - if you have it, look into any folder of an Ortho4XP tile in your X-Plane folder, and check the size of the "textures" folder.
 
+Also in defines.py, you will find the single layers, along with their corresponding mask blurring values. It is my strong recommendation NOT to change these values, as I have taken a lot of time to fine-tune these values.
+
 
 [section]Running![/section]
 
index 805c3cd200fcda27b1fec61415add13b12e3635f..c933b903d1af87f0d6c94562c7e477589c9ff883 100644 (file)
--- a/tiledb.py
+++ b/tiledb.py
@@ -40,6 +40,7 @@ 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")
 
     
@@ -55,6 +56,11 @@ 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):
diff --git a/wedgen.py b/wedgen.py
new file mode 100644 (file)
index 0000000..d22aeae
--- /dev/null
+++ b/wedgen.py
@@ -0,0 +1,61 @@
+
+# -------------------------------------------------------------------
+# 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
+# -------------------------------------------------------------------
+# wedgen.py
+# Generates the XML for X-Plane's WED, which you need to use the
+# orthos in the flight simulator.
+# -------------------------------------------------------------------
+
+import xml.dom.minidom
+from defines import *
+from log import *
+from tiledb import *
+
+
+class mstr_wedgen:
+    def __init__(self, lat, lng):
+        self._lat = lat
+        self._lng = lng
+        self._curID = 2
+        self._tiledb = mstr_tiledb(lat, lng)
+
+
+    # Creates the XML file for WED in one go
+    def create_WED_XML(self):
+
+        # Generates a very basic skeleton for the World Editor
+        root = xml.dom.minidom.Document()
+        xmlobj = root.createElement("doc")
+        root.appendChild(xmlobj)
+
+        objs = root.createElement("objects")
+        xmlobj.appendChild(objs)
+
+        rtobj = root.createElement("object")
+        rtobj.setAttribute("class", "WED_Root")
+        rtobj.setAttribute("id", "1")
+        rtobj.setAttribute("parent_id", "0")
+
+        chdobj = root.createElement("children")
+        rtobj.appendChild(chdobj)
+
+        hiera = root.createElement("hierarchy")
+        hiera.setAttribute("name", "root")
+        rtobj.appendChild(hiera)
+
+        # We now open the DB and check for everything that needs to be added,
+        # predominantely forests
+        
+
+        # I believe this needs to be in
+        prefs = root.createElement("prefs")
+        xmlobj.appendChild(prefs)
+
+        file_handle = open(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/earth.wed.xml","w")
+        root.writexml(file_handle)
+        file_handle.close()
\ No newline at end of file