Last minor adjustments and a needed feature for PBF generation (needed for a later stage)

This commit is contained in:
Marcus Str. 2024-10-18 21:48:24 +02:00
parent 817d65639d
commit bd2594221d
6 changed files with 171 additions and 29 deletions

View File

@ -75,6 +75,7 @@ mstr_xp_scn_normalmaps = True
mstr_xp_meshtool = "/home/marcus/Developer/Projects/orthographic/bin/MeshTool"
mstr_xp_ddstool = "/home/marcus/Developer/Projects/orthographic/bin/DDSTool"
mstr_xp_xessrc = "https://dev.x-plane.com/update/misc/MeshTool/"
mstr_xp_floor_height = 2.8 # 2.5m ceiling height + 30cm concrete per floor
# If you set the above to true, you can define for which features you
# want to generate normal maps for. The below is my recommendation for

View File

@ -1123,7 +1123,7 @@ class mstr_layergen:
ptc_src.append(Image.open(p))
osm_mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + ".png" )
lyr_mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" )
#lyr_mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" )
for i in mstr_mask_blur:
if i[0] == self._tag and i[1] == self._value:
@ -1147,12 +1147,12 @@ class mstr_layergen:
adj_image.alpha_composite( brd_src )
lyr_pix = lyr_mask.load()
#lyr_pix = lyr_mask.load()
for y in range(self._imgsize):
for x in range(self._imgsize):
if lyr_pix[x, y][3] > 0:
if mask_pix[x, y][3] > 0:
rgb=adj_pix[x,y]
a=lyr_pix[x,y]
a=mask_pix[x,y]
adj_pix[x, y] = ( rgb[0], rgb[1], rgb[2], a[3])
# Up until here we mimiced the exact same behavior as layergen. However, now

View File

@ -13,12 +13,6 @@
# that this part of the code is the most crucial one, as the other
# classes involved rely on what this code is doing, and by extension,
# generating.
#
# The PNG generated will be used in this progression:
# - Generate mask from OSM (here)
# - Generate colored photo layer from this mask, for example for
# landuse: forest
# - Compile actual satellite aerial
# -------------------------------------------------------------------
import math
@ -83,9 +77,7 @@ class mstr_maskgen:
# Builds the required mask
def _build_mask(self):
# Generate empty image
imgsize = 0
if mstr_photores == 2048: imgsize=2048
if mstr_photores == 4096: imgsize=6000
imgsize = 2048
mask_img = Image.new("RGBA", (imgsize, imgsize))
tilexml = mstr_datafolder + "_cache/tile.xml"
@ -119,7 +111,7 @@ class mstr_maskgen:
for d in way:
if d[0] == w[0]:
if self._tag == "building" and bld_levels == 0:
bld_levels = xml.find_building_levels(tilexml, w[0])
bld_levels = xml.find_building_levels(w[0])
nd.append(d[1])
frs.append(nd)
# Scout through relations as these also make up map data
@ -160,9 +152,7 @@ class mstr_maskgen:
p_lng = self.project_pixel(latlng[1], bbox[3])
pixlat = 0
pixlng = 0
pr = 0
if mstr_photores == 2048: pr = 2048
if mstr_photores == 4096: pr = 6000
pr = 2048
# Draw pixels in direction according to latitude and longitude positions -

24
og.py
View File

@ -22,7 +22,7 @@ from defines import *
print(" ")
print(" ---------------------------------------------------------------- ")
print(" ORTHOGRAPHIC: An ortho-photo generator, using real world data.")
print(" Developed by MarStrMind - Code available on marstr.online")
print(" Developed by MarStr - Code available on marstr.online")
print(" ---------------------------------------------------------------- ")
print(" ")
@ -30,9 +30,14 @@ print(" ")
# Evaluate CLI arguments and process tile.
cli = False
pbf = False
if len(sys.argv) == 3:
cli = True
if len(sys.argv) == 4:
pbf = True
# Only if we find enough arguments, proceed.
if cli == True:
lat = int(sys.argv[1])
@ -44,10 +49,23 @@ if cli == True:
og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd())
og._buildTile()
else:
# Only if we find enough arguments, proceed.
if pbf == True:
lat = int(sys.argv[1])
lng = int(sys.argv[2])
pbf = sys.argv[3]
if pbf == "pbf":
# Create the class and init values
og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd())
og._generateData()
if cli == False and pbf == False:
mstr_msg("_main", "Please provide Latitude and Longitude. Exiting.")
print ("")
# * No Space Shuttle or SpaceShipOne needed to deploy.
# * No Space Shuttle or SpaceShipOne needed to deploy.

View File

@ -77,6 +77,74 @@ class mstr_orthographic:
return round(dm * 1000, 3)
# In this case we only want to acquire PBF for a latitude and longitude. Normally
# not needed for standard ortho generation.
def _generateData(self):
# 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
bb_lng = self._long
bb_lat_edge = self._lat+self._vstep
bb_lng_edge = self._long+mstr_zl_18
cur_tile_x = 1
cur_tile_y = 1
osmxml = mstr_osmxml(0,0)
mstr_msg("orthographic", "Set initial coordinates and bounding box for OSM acquisition")
# The highest encountered tile numbers
# This is needed to produce the zoom level 16 images
top_lat = 1
top_lng = 1
# We need to know the highest possible latitude and longitude tile numbers,
# in case we render at the edge
mlat = 1
mlng = 1
while bb_lat < self._lat + 1:
bb_lat = bb_lat + self._vstep
mlat = mlat+1
while bb_lng < self._long + 1:
bb_lng = bb_lng + mstr_zl_18
mlng = mlng+1
mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng))
maxlatlng = [ mlat, mlng ]
# Reset these two
bb_lat = self._lat
bb_lng = self._long
# 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.
for lat_grid in range(1, maxlatlng[0]+1):
for lng_grid in range(1, maxlatlng[1]+1):
# Adjust bounding box
osmxml.adjust_bbox(bb_lat, bb_lng, bb_lat_edge, bb_lng_edge)
osmxml.generate_osm(cur_tile_y, cur_tile_x) # <- This acquires current OSM info
# Adjust longitude coordinates
cur_tile_x = cur_tile_x+1
bb_lng = bb_lng + mstr_zl_18
bb_lng_edge = bb_lng_edge + mstr_zl_18
mstr_msg("orthographic", "Adjustment of longitude performed")
# Adjust peak longitude tile number
if cur_tile_x > top_lng:
top_lng = cur_tile_x
# Adjust latitude and all other values when we get here
cur_tile_y = cur_tile_y+1
cur_tile_x = 1
bb_lng = self._long
bb_lng_edge = self._long + mstr_zl_18
bb_lat = bb_lat + self._vstep
bb_lat_edge = bb_lat_edge + self._vstep
mstr_msg("orthographic", "Adjustment of latitude performed")
# Adjust peak latitude number
if cur_tile_y > top_lat:
top_lat = cur_tile_y
# Builds and processes the tile with everything required, in one call.
def _buildTile(self):
mstr_msg("orthographic", "Beginning construction of tile")
@ -163,9 +231,6 @@ 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.
@ -363,4 +428,4 @@ class mstr_orthographic:
lng = abs(int(self._long / 10) * 10)
earthnavdata.append(lat)
earthnavdata.append(lng)
return earthnavdata
return earthnavdata

View File

@ -34,7 +34,27 @@ class mstr_osmxml:
self._lng = round(lng, 4)
self._curB_lat = round(lat_e, 4)
self._curB_lng = round(lng_e, 4)
def generate_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.
data = {
"bbox": {
"lat": str(self._lat),
"lng": str(self._lng),
"lat_b": str(self._curB_lat),
"lng_b": str(self._curB_lng)
},
"tile_lat": str(self._lat),
"tile_lng": str(self._lng),
"square_lat": str(v),
"square_lng": str(h),
"as_pbf": "true"
}
r = requests.post(mstr_osm_endpoint, json=data)
# Acquire XMLs in chunks, then store them
def acquire_osm(self, v, h, asobject=False):
@ -65,7 +85,7 @@ class mstr_osmxml:
# 1 second delay in case the request fails
if os.path.isfile(mstr_datafolder + "_cache/tile.xml") == False:
sleep(1)
# Provide the object directly
if asobject == True:
xml_doc = xml.dom.minidom.parse("_cache/tile.xml")
@ -152,9 +172,9 @@ class mstr_osmxml:
# Find the levels of a building (for shadow rendering)
def find_building_levels(self, xmlfile, way_id):
lvl = 3 # Standard if we don't find anything else
xml_doc = xml.dom.minidom.parse(xmlfile)
def find_building_levels(self, way_id):
lvl = 2 # Standard if we don't find anything else
xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")
wpdata = xml_doc.getElementsByTagName("way")
for wp in wpdata:
if wp.getAttribute("id") == way_id:
@ -167,6 +187,54 @@ class mstr_osmxml:
break
return lvl
# Find minimum level of a building
def find_building_minlevel(self, way_id):
lvl = 0 # Standard if we don't find anything else
xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")
wpdata = xml_doc.getElementsByTagName("way")
for wp in wpdata:
if wp.getAttribute("id") == way_id:
tags = wp.getElementsByTagName("tag")
for tag in tags:
a = tag.getAttribute("k")
v = tag.getAttribute("v")
if a == "building:min_level":
lvl = int(v)
break
return lvl
# Find the roof shape
def find_building_roof_shape(self, way_id):
rs = "flat" # Standard if we don't find anything else
xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")
wpdata = xml_doc.getElementsByTagName("way")
for wp in wpdata:
if wp.getAttribute("id") == way_id:
tags = wp.getElementsByTagName("tag")
for tag in tags:
a = tag.getAttribute("k")
v = tag.getAttribute("v")
if a == "roof:shape":
rs = v
break
return rs
# Find the roof levels
def find_building_roof_levels(self, way_id):
lvl = 0 # Standard if we don't find anything else
xml_doc = xml.dom.minidom.parse(mstr_datafolder + "_cache/tile.xml")
wpdata = xml_doc.getElementsByTagName("way")
for wp in wpdata:
if wp.getAttribute("id") == way_id:
tags = wp.getElementsByTagName("tag")
for tag in tags:
a = tag.getAttribute("k")
v = tag.getAttribute("v")
if a == "roof:levels":
lvl = int(v)
break
return lvl
# It turns out that some features hide themselves in the relations section.
# I figured this out during testing, and almost going insane over the
@ -191,4 +259,4 @@ class mstr_osmxml:
tgd.append(i.getAttribute("v"))
rls.append((rp.getAttribute("ref"), tgd))
return rls