]> marstr Code Repo - orthographic/commitdiff
XML handling moved completely into memory for performance. Initial commit of tileprep...
authorMarcus Str. <marcus@marstr.online>
Tue, 12 Nov 2024 11:44:22 +0000 (12:44 +0100)
committerMarcus Str. <marcus@marstr.online>
Tue, 12 Nov 2024 11:44:22 +0000 (12:44 +0100)
osmxml.py
tileprep.py [new file with mode: 0644]

index c80dc4bd86341a46888d2ae4979d69c32aadb422..7349ea3b103db2c159c6ff33ecd3728e9d7fc994 100644 (file)
--- a/osmxml.py
+++ b/osmxml.py
@@ -19,13 +19,10 @@ from defines import *
 from log import *\r
 \r
 class mstr_osmxml:\r
-    def __init__(self, lat, lng):\r
-        self._latv = lat\r
-        self._lngv = lng\r
-        self._lat = lat\r
-        self._lng = lng\r
-        self._curB_lat = lat + mstr_zl_18\r
-        self._curB_lng = lng + mstr_zl_18\r
+    def __init__(self):\r
+        #self._xmlfn = mstr_datafolder + "_cache/tile_" + str(lat) + "-" + str(v) + "_" + str(lng) + "-" + str(h) + ".xml"\r
+        self._xmldata = None\r
+        self._xmlcontent = ""\r
 \r
     \r
     # Adjust bbox for when this class should persost, but acquire data for a different bbox\r
@@ -61,7 +58,7 @@ class mstr_osmxml:
         mstr_msg("osmxml", "Acquiring OSM data for " + str(self._lat)+","+str(self._lng)+" - "+str(self._curB_lat)+","+str(self._curB_lng))\r
         \r
         # We will use our self-hosted API for this.\r
-        while os.path.isfile(mstr_datafolder + "_cache/tile.xml") == False:\r
+        while os.path.isfile(self._xmlfn) == False:\r
             data = { \r
                 "bbox": { \r
                         "lat": str(self._lat),\r
@@ -76,26 +73,27 @@ class mstr_osmxml:
                     }\r
             r = requests.post(mstr_osm_endpoint, json=data)\r
 \r
-            xmlf = mstr_datafolder + "_cache/tile.xml"\r
-            if os.path.isfile(xmlf):\r
-                os.remove(xmlf)\r
-            with open(xmlf, 'wb') as textfile:\r
-                textfile.write(r.content)\r
+            self._xmlcontent = r.content\r
+\r
+            #if os.path.isfile(self._xmlfn):\r
+            #    os.remove(self._xmlfn)\r
+            #with open(self._xmlfn, 'wb') as textfile:\r
+            #    textfile.write(r.content)\r
 \r
             # 1 second delay in case the request fails\r
-            if os.path.isfile(mstr_datafolder + "_cache/tile.xml") == False:\r
+            if self._xmlcontent == "":\r
+            #if os.path.isfile(self._xmlfn) == False:\r
                 sleep(1)\r
-            \r
-        # Provide the object directly\r
-        if asobject == True:\r
-            xml_doc = xml.dom.minidom.parse("_cache/tile.xml")\r
-            return xml_doc\r
+        \r
+        # Store the content in memory\r
+        self._xmldata = xml.dom.minidom.parse(self._xmlcontent)\r
+        self._xmlcontent = "" # Clear\r
 \r
 \r
     # Get all nodes from the specified OSM file\r
-    def acquire_nodes(self, xmlfile):\r
-        xml_doc = xml.dom.minidom.parse(xmlfile)\r
-        nodedata = xml_doc.getElementsByTagName("node")\r
+    def acquire_nodes(self):\r
+        #xml_doc = xml.dom.minidom.parse(xmlfile)\r
+        nodedata = self._xmldata.getElementsByTagName("node")\r
         nodes = []\r
         for node in nodedata:\r
             p = (node.getAttribute("id"), node.getAttribute("lat"), node.getAttribute("lon"))\r
@@ -104,9 +102,9 @@ class mstr_osmxml:
     \r
 \r
     # Get all waypoint data\r
-    def acquire_waypoint_data(self, xmlfile):\r
-        xml_doc = xml.dom.minidom.parse(xmlfile)\r
-        wpdata = xml_doc.getElementsByTagName("way")\r
+    def acquire_waypoint_data(self):\r
+        #xml_doc = xml.dom.minidom.parse(xmlfile)\r
+        wpdata = self._xmldata.getElementsByTagName("way")\r
         wps = []\r
         for wp in wpdata:\r
             nddata = wp.getElementsByTagName("nd")\r
@@ -125,10 +123,10 @@ class mstr_osmxml:
 \r
     # Checks if there is an airport or many airports with an ICAO code in the\r
     # supplied XML data chunk\r
-    def find_icao_codes(self, xmlfile):\r
+    def find_icao_codes(self):\r
         icao = []\r
-        xml_doc = xml.dom.minidom.parse(xmlfile)\r
-        wpdata = xml_doc.getElementsByTagName("way")\r
+        #xml_doc = xml.dom.minidom.parse(xmlfile)\r
+        wpdata = self._xmldata.getElementsByTagName("way")\r
         for wp in wpdata:\r
             tags = wp.getElementsByTagName("tag")\r
             for tag in tags:\r
@@ -144,11 +142,11 @@ class mstr_osmxml:
     # If no surface type is specified, the runway will be rendered similar\r
     # to how motorways are rendered.\r
     # This gets called only if some ICAO code was found\r
-    def find_runway_surface(self, xmlfile):\r
+    def find_runway_surface(self):\r
         surface = ""\r
         wpid = ""\r
-        xml_doc = xml.dom.minidom.parse(xmlfile)\r
-        wpdata = xml_doc.getElementsByTagName("way")\r
+        #xml_doc = xml.dom.minidom.parse(xmlfile)\r
+        wpdata = self._xmldata.getElementsByTagName("way")\r
         for wp in wpdata:\r
             tags = wp.getElementsByTagName("tag")\r
             for tag in tags:\r
@@ -174,8 +172,8 @@ class mstr_osmxml:
     # Find the levels of a building (for shadow rendering)\r
     def find_building_levels(self, way_id):\r
         lvl = 2 # Standard if we don't find anything else\r
-        xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
-        wpdata = xml_doc.getElementsByTagName("way")\r
+        #xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
+        wpdata = self._xmldata.getElementsByTagName("way")\r
         for wp in wpdata:\r
             if wp.getAttribute("id") == way_id:\r
                 tags = wp.getElementsByTagName("tag")\r
@@ -190,8 +188,8 @@ class mstr_osmxml:
     # Find minimum level of a building\r
     def find_building_minlevel(self, way_id):\r
         lvl = 0 # Standard if we don't find anything else\r
-        xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
-        wpdata = xml_doc.getElementsByTagName("way")\r
+        #xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
+        wpdata = self._xmldata.getElementsByTagName("way")\r
         for wp in wpdata:\r
             if wp.getAttribute("id") == way_id:\r
                 tags = wp.getElementsByTagName("tag")\r
@@ -206,8 +204,8 @@ class mstr_osmxml:
     # Find the roof shape\r
     def find_building_roof_shape(self, way_id):\r
         rs = "flat" # Standard if we don't find anything else\r
-        xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
-        wpdata = xml_doc.getElementsByTagName("way")\r
+        #xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
+        wpdata = self._xmldata.getElementsByTagName("way")\r
         for wp in wpdata:\r
             if wp.getAttribute("id") == way_id:\r
                 tags = wp.getElementsByTagName("tag")\r
@@ -222,8 +220,8 @@ class mstr_osmxml:
     # Find the roof levels\r
     def find_building_roof_levels(self, way_id):\r
         lvl = 0 # Standard if we don't find anything else\r
-        xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
-        wpdata = xml_doc.getElementsByTagName("way")\r
+        #xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")\r
+        wpdata = self._xmldata.getElementsByTagName("way")\r
         for wp in wpdata:\r
             if wp.getAttribute("id") == way_id:\r
                 tags = wp.getElementsByTagName("tag")\r
@@ -243,9 +241,9 @@ class mstr_osmxml:
     # the relations and then scan through the outer and inner way IDs.\r
     #\r
     # Get all relation entries of importance.\r
-    def acquire_relations(self, xmlfile):\r
-        xml_doc = xml.dom.minidom.parse(xmlfile)\r
-        rlxml = xml_doc.getElementsByTagName("relation")\r
+    def acquire_relations(self):\r
+        #xml_doc = xml.dom.minidom.parse(xmlfile)\r
+        rlxml = self._xmldata.getElementsByTagName("relation")\r
         rls = []\r
         for rl in rlxml:\r
             rldata  = rl.getElementsByTagName("member")\r
diff --git a/tileprep.py b/tileprep.py
new file mode 100644 (file)
index 0000000..8de6c39
--- /dev/null
@@ -0,0 +1,139 @@
+
+# -------------------------------------------------------------------
+# 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
+# -------------------------------------------------------------------
+# tileprep.py
+# This prepares a zoom level 18 tile for ortho generation. This class
+# performs the edge detection, selects the required sources for each
+# layer, and stores this information into files which are then used
+# by layergen.
+# -------------------------------------------------------------------
+
+import glob
+from random import randrange
+from PIL import Image
+from osmxml import *
+from maskgen import *
+from defines import *
+from functions import *
+from log import *
+from tileinfo import *
+
+class mstr_tileprep:
+
+    # For this to work we need some fundamentals.
+    def __init__(self, lat, lng, v, h, tag, value, mask, is_completion):
+        self._lat       = lat
+        self._lng       = lng
+        self._tile_h    = h
+        self._tile_v    = v
+        self._tag       = tag
+        self._value     = value
+        self._edges     = ""   # To be filled by _edgeDetect call
+        self._source    = -1
+        self._mask      = mask
+        latlngfld = xplane_latlng_folder([lat, lng])
+        self._tileinfo  = mstr_tileinfo(lat, lng, v, h, latlngfld)
+        
+        # Special case for the final step of an ortho
+        self._is_completion = is_completion
+
+
+    # Prepare the tile accordingly
+    def _prepareTile(self):
+        # Load the mask pixels
+        mp = self._mask.load()
+        imgsize = self._mask.width
+
+        # Run scan
+
+        # Check if pixels touch the borders of the image, and if so -
+        # make a not of that in the database.
+        at=False
+        ar=False
+        ab=False
+        al=False
+
+        # Top scan
+        for i in range(0, imgsize-1):
+            p = mp[i,0]
+            if p[3] > 0:
+                at=True
+                break
+       
+        # Right scan
+        for i in range(0, imgsize-1):
+            p = mp[imgsize-1,i]
+            if p[3] > 0:
+                ar=True
+                break
+        
+        # Bottom scan
+        for i in range(0, imgsize-1):
+            p = mp[i,imgsize-1]
+            if p[3] > 0:
+                ab=True
+                break
+        
+        # Left scan
+        for i in range(0, imgsize-1):
+            p = mp[1,i]
+            if p[3] > 0:
+                al=True
+                break
+        
+        # Construct adjacency string
+        adjstr = ""
+        if at==True: adjstr = adjstr + "t"
+        if ar==True: adjstr = adjstr + "r"
+        if ab==True: adjstr = adjstr + "b"
+        if al==True: adjstr = adjstr + "l"
+
+        # Now find out of there is a source from any adjacent tile
+        adjtiles = findAdjacentTilesTo(self._tile_v, self._tile_h)
+        sat = []
+        sar = []
+        sab = []
+        sal = []
+        if self._is_completion == False:
+            if at == True: sat = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[0][0], adjtiles[0][1], self._tag, self._value) # Top
+            if ar == True: sar = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[1][0], adjtiles[1][1], self._tag, self._value) # Right
+            if ab == True: sab = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[2][0], adjtiles[2][1], self._tag, self._value) # Bottom
+            if al == True: sal = self._tileinfo.get_adjacency_for_tag_and_value(adjtiles[3][0], adjtiles[3][1], self._tag, self._value) # Left
+        if self._is_completion == True:
+            if at == True: sat = self._tileinfo.get_adjacency_for_completion(adjtiles[0][0], adjtiles[0][1]) # Top
+            if ar == True: sar = self._tileinfo.get_adjacency_for_completion(adjtiles[1][0], adjtiles[1][1]) # Right
+            if ab == True: sab = self._tileinfo.get_adjacency_for_completion(adjtiles[2][0], adjtiles[2][1]) # Bottom
+            if al == True: sal = self._tileinfo.get_adjacency_for_completion(adjtiles[3][0], adjtiles[3][1]) # Left
+
+        if self._source == -1 and len(sat) == 2: self._source = sat[0]
+        if self._source == -1 and len(sar) == 2: self._source = sar[0]
+        if self._source == -1 and len(sab) == 2: self._source = sab[0]
+        if self._source == -1 and len(sal) == 2: self._source = sal[0]
+
+        # If there was nothing in the info still, we need to select some source
+        if self._source == -1:
+            tx = mstr_datafolder + "textures/" + self._tag + "/" + self._value + "/brd/b*.png"
+            lst = glob.glob(tx)
+            if len(lst) == 1: self._source = 1
+            if len(lst) >= 2: self._source = randrange(1, len(lst)+1)
+
+
+        # Store into DB - but only if there is something to store
+        if adjstr != "":
+            if self._is_completion == False:
+                r = self._tileinfo.get_adjacency_for_tag_and_value(self._tile_v, self._tile_h, self._tag, self._value)
+                if len(r) == 0:
+                    self._tileinfo.add_adjacency_data(self._tile_v, self._tile_h, self._tag, self._value, self._source, adjstr)
+                    mstr_msg("tileprep", "Adjacency info stored in database")
+
+            if self._is_completion == True:
+                r = self._tileinfo.get_adjacency_for_completion(self._tile_v, self._tile_h)
+                if len(r) == 0:
+                    self._tileinfo.add_completion_data(self._tile_v, self._tile_h, self._tag, self._value, self._source, adjstr)
+                    mstr_msg("tileprep", "Adjacency info for completion stored in database")
+