mstr_xp_meshtool = "/home/marcus/Developer/Projects/orthographic/bin/MeshTool"\r
mstr_xp_ddstool = "/home/marcus/Developer/Projects/orthographic/bin/DDSTool"\r
mstr_xp_xessrc = "https://dev.x-plane.com/update/misc/MeshTool/"\r
+mstr_xp_floor_height = 2.8 # 2.5m ceiling height + 30cm concrete per floor\r
\r
# If you set the above to true, you can define for which features you\r
# want to generate normal maps for. The below is my recommendation for\r
ptc_src.append(Image.open(p))\r
\r
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" )\r
- 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" )\r
+ #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" )\r
\r
for i in mstr_mask_blur:\r
if i[0] == self._tag and i[1] == self._value:\r
\r
adj_image.alpha_composite( brd_src )\r
\r
- lyr_pix = lyr_mask.load()\r
+ #lyr_pix = lyr_mask.load()\r
for y in range(self._imgsize):\r
for x in range(self._imgsize):\r
- if lyr_pix[x, y][3] > 0:\r
+ if mask_pix[x, y][3] > 0:\r
rgb=adj_pix[x,y]\r
- a=lyr_pix[x,y]\r
+ a=mask_pix[x,y]\r
adj_pix[x, y] = ( rgb[0], rgb[1], rgb[2], a[3])\r
\r
# Up until here we mimiced the exact same behavior as layergen. However, now\r
# that this part of the code is the most crucial one, as the other\r
# classes involved rely on what this code is doing, and by extension,\r
# generating.\r
-# \r
-# The PNG generated will be used in this progression:\r
-# - Generate mask from OSM (here)\r
-# - Generate colored photo layer from this mask, for example for\r
-# landuse: forest\r
-# - Compile actual satellite aerial\r
# -------------------------------------------------------------------\r
\r
import math\r
# Builds the required mask\r
def _build_mask(self):\r
# Generate empty image\r
- imgsize = 0\r
- if mstr_photores == 2048: imgsize=2048\r
- if mstr_photores == 4096: imgsize=6000\r
+ imgsize = 2048\r
mask_img = Image.new("RGBA", (imgsize, imgsize))\r
\r
tilexml = mstr_datafolder + "_cache/tile.xml"\r
for d in way:\r
if d[0] == w[0]:\r
if self._tag == "building" and bld_levels == 0:\r
- bld_levels = xml.find_building_levels(tilexml, w[0])\r
+ bld_levels = xml.find_building_levels(w[0])\r
nd.append(d[1])\r
frs.append(nd)\r
# Scout through relations as these also make up map data\r
p_lng = self.project_pixel(latlng[1], bbox[3])\r
pixlat = 0\r
pixlng = 0\r
- pr = 0\r
- if mstr_photores == 2048: pr = 2048\r
- if mstr_photores == 4096: pr = 6000\r
+ pr = 2048\r
\r
# Draw pixels in direction according to latitude and longitude positions -\r
\r
print(" ")\r
print(" ---------------------------------------------------------------- ")\r
print(" ORTHOGRAPHIC: An ortho-photo generator, using real world data.")\r
-print(" Developed by MarStrMind - Code available on marstr.online")\r
+print(" Developed by MarStr - Code available on marstr.online")\r
print(" ---------------------------------------------------------------- ")\r
print(" ")\r
\r
# Evaluate CLI arguments and process tile.\r
\r
cli = False\r
+pbf = False\r
+\r
if len(sys.argv) == 3:\r
cli = True\r
\r
+if len(sys.argv) == 4:\r
+ pbf = True\r
+\r
# Only if we find enough arguments, proceed.\r
if cli == True:\r
lat = int(sys.argv[1])\r
og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd())\r
og._buildTile()\r
\r
-else:\r
+\r
+# Only if we find enough arguments, proceed.\r
+if pbf == True:\r
+ lat = int(sys.argv[1])\r
+ lng = int(sys.argv[2])\r
+ pbf = sys.argv[3]\r
+\r
+ if pbf == "pbf":\r
+ # Create the class and init values\r
+ og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd())\r
+ og._generateData()\r
+\r
+\r
+if cli == False and pbf == False:\r
mstr_msg("_main", "Please provide Latitude and Longitude. Exiting.")\r
print ("")\r
\r
\r
\r
-# * No Space Shuttle or SpaceShipOne needed to deploy.
\ No newline at end of file
+# * No Space Shuttle or SpaceShipOne needed to deploy.\r
return round(dm * 1000, 3)\r
\r
\r
+ # In this case we only want to acquire PBF for a latitude and longitude. Normally\r
+ # not needed for standard ortho generation.\r
+ def _generateData(self):\r
+ # The tile is constructed of many smaller parts. We walk through the\r
+ # smallest possible, from which the bigger ones are later built.\r
+ bb_lat = self._lat\r
+ bb_lng = self._long\r
+ bb_lat_edge = self._lat+self._vstep\r
+ bb_lng_edge = self._long+mstr_zl_18\r
+ cur_tile_x = 1\r
+ cur_tile_y = 1\r
+ osmxml = mstr_osmxml(0,0)\r
+ mstr_msg("orthographic", "Set initial coordinates and bounding box for OSM acquisition")\r
+\r
+ # The highest encountered tile numbers\r
+ # This is needed to produce the zoom level 16 images\r
+ top_lat = 1\r
+ top_lng = 1\r
+\r
+ # We need to know the highest possible latitude and longitude tile numbers,\r
+ # in case we render at the edge\r
+ mlat = 1\r
+ mlng = 1\r
+ while bb_lat < self._lat + 1:\r
+ bb_lat = bb_lat + self._vstep\r
+ mlat = mlat+1\r
+ while bb_lng < self._long + 1:\r
+ bb_lng = bb_lng + mstr_zl_18\r
+ mlng = mlng+1\r
+ mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng))\r
+ maxlatlng = [ mlat, mlng ]\r
+\r
+ # Reset these two\r
+ bb_lat = self._lat\r
+ bb_lng = self._long\r
+\r
+ # Previously, I downloaded all XML files in one go - but to ease the\r
+ # stress on OSM servers and my server, we will do acquire the data\r
+ # only for the current processed part of the tile.\r
+ for lat_grid in range(1, maxlatlng[0]+1):\r
+ for lng_grid in range(1, maxlatlng[1]+1):\r
+ # Adjust bounding box\r
+ osmxml.adjust_bbox(bb_lat, bb_lng, bb_lat_edge, bb_lng_edge)\r
+ \r
+ osmxml.generate_osm(cur_tile_y, cur_tile_x) # <- This acquires current OSM info\r
+ \r
+ # Adjust longitude coordinates\r
+ cur_tile_x = cur_tile_x+1\r
+ bb_lng = bb_lng + mstr_zl_18\r
+ bb_lng_edge = bb_lng_edge + mstr_zl_18\r
+ mstr_msg("orthographic", "Adjustment of longitude performed")\r
+ # Adjust peak longitude tile number\r
+ if cur_tile_x > top_lng:\r
+ top_lng = cur_tile_x\r
+ \r
+ # Adjust latitude and all other values when we get here\r
+ cur_tile_y = cur_tile_y+1\r
+ cur_tile_x = 1\r
+ bb_lng = self._long\r
+ bb_lng_edge = self._long + mstr_zl_18\r
+ bb_lat = bb_lat + self._vstep\r
+ bb_lat_edge = bb_lat_edge + self._vstep\r
+ mstr_msg("orthographic", "Adjustment of latitude performed")\r
+ # Adjust peak latitude number\r
+ if cur_tile_y > top_lat:\r
+ top_lat = cur_tile_y\r
+\r
+\r
# Builds and processes the tile with everything required, in one call.\r
def _buildTile(self):\r
mstr_msg("orthographic", "Beginning construction of tile")\r
bb_lat = self._lat\r
bb_lng = self._long\r
\r
- # For X-Plane scenery generation\r
- xp_datagroup = 1\r
-\r
# Previously, I downloaded all XML files in one go - but to ease the\r
# stress on OSM servers and my server, we will do acquire the data\r
# only for the current processed part of the tile.\r
lng = abs(int(self._long / 10) * 10)\r
earthnavdata.append(lat)\r
earthnavdata.append(lng)\r
- return earthnavdata
\ No newline at end of file
+ return earthnavdata\r
self._lng = round(lng, 4)\r
self._curB_lat = round(lat_e, 4)\r
self._curB_lng = round(lng_e, 4)\r
+\r
\r
+ def generate_osm(self, v, h, asobject=False):\r
+ 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
+ data = { \r
+ "bbox": { \r
+ "lat": str(self._lat),\r
+ "lng": str(self._lng),\r
+ "lat_b": str(self._curB_lat),\r
+ "lng_b": str(self._curB_lng)\r
+ },\r
+ "tile_lat": str(self._lat),\r
+ "tile_lng": str(self._lng),\r
+ "square_lat": str(v),\r
+ "square_lng": str(h),\r
+ "as_pbf": "true"\r
+ }\r
+ r = requests.post(mstr_osm_endpoint, json=data)\r
+\r
\r
# Acquire XMLs in chunks, then store them\r
def acquire_osm(self, v, h, asobject=False):\r
# 1 second delay in case the request fails\r
if os.path.isfile(mstr_datafolder + "_cache/tile.xml") == False:\r
sleep(1)\r
-\r
+ \r
# Provide the object directly\r
if asobject == True:\r
xml_doc = xml.dom.minidom.parse("_cache/tile.xml")\r
\r
\r
# Find the levels of a building (for shadow rendering)\r
- def find_building_levels(self, xmlfile, way_id):\r
- lvl = 3 # Standard if we don't find anything else\r
- xml_doc = xml.dom.minidom.parse(xmlfile)\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
for wp in wpdata:\r
if wp.getAttribute("id") == way_id:\r
break\r
return lvl\r
\r
+ # 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
+ for wp in wpdata:\r
+ if wp.getAttribute("id") == way_id:\r
+ tags = wp.getElementsByTagName("tag")\r
+ for tag in tags:\r
+ a = tag.getAttribute("k")\r
+ v = tag.getAttribute("v")\r
+ if a == "building:min_level":\r
+ lvl = int(v)\r
+ break\r
+ return lvl\r
+ \r
+ # 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
+ for wp in wpdata:\r
+ if wp.getAttribute("id") == way_id:\r
+ tags = wp.getElementsByTagName("tag")\r
+ for tag in tags:\r
+ a = tag.getAttribute("k")\r
+ v = tag.getAttribute("v")\r
+ if a == "roof:shape":\r
+ rs = v\r
+ break\r
+ return rs\r
+ \r
+ # 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
+ for wp in wpdata:\r
+ if wp.getAttribute("id") == way_id:\r
+ tags = wp.getElementsByTagName("tag")\r
+ for tag in tags:\r
+ a = tag.getAttribute("k")\r
+ v = tag.getAttribute("v")\r
+ if a == "roof:levels":\r
+ lvl = int(v)\r
+ break\r
+ return lvl\r
+ \r
\r
# It turns out that some features hide themselves in the relations section.\r
# I figured this out during testing, and almost going insane over the\r
tgd.append(i.getAttribute("v"))\r
rls.append((rp.getAttribute("ref"), tgd))\r
return rls\r
-
\ No newline at end of file
+ \r