diff --git a/defines.py b/defines.py index a746dac..11c56e0 100644 --- a/defines.py +++ b/defines.py @@ -150,6 +150,8 @@ mstr_ortho_layers = [ ("natural", "wetland", "natural", "wetland"), ("natural", "scrub", "natural", "scrub"), ("natural", "heath", "natural", "heath"), + ("natural", "sand", "natural", "sand"), + ("natural", "desert", "natural", "desert"), # Z-Order 3 ("natural", "water", "natural", "water"), ("natural", "bay", "natural", "beach"), @@ -180,7 +182,9 @@ mstr_ortho_layers = [ ("building", "terrace", "building", "industrial"), ("building", "hangar", "building", "industrial"), ("building", "school", "building", "common"), - ("building", "yes", "building", "common") + ("building", "yes", "building", "common"), + ("place", "sea", "natural", "sea"), + ("place", "ocean", "natural", "sea") ] @@ -257,5 +261,7 @@ mstr_mask_blur = [ ("building", "terrace", 1), ("building", "hangar", 1), ("building", "school", 1), - ("building", "yes", 1) + ("building", "yes", 1), + ("place", "sea", 1), + ("place", "ocean", 1) ] diff --git a/layergen.py b/layergen.py index fd334af..2dde29c 100644 --- a/layergen.py +++ b/layergen.py @@ -38,8 +38,6 @@ class mstr_layergen: self._longitude = lng self._lng_number = lngnum self._layerborder = -1 - self._tiledb = mstr_tiledb(lat, lng) - self._tiledb.create_tables() self._is_completion = is_completion # Define layer size depending on what is wanted self._imgsize = 0 @@ -58,6 +56,11 @@ class mstr_layergen: def set_latlng_folder(self, latlngfld): self._latlngfld = latlngfld + # Open DB + def open_db(self): + self._tiledb = mstr_tiledb(self._latitude, self._longitude, self._latlngfld) + self._tiledb.create_tables() + # This generates a "border" image, for example farmland usually has a small space of grass # before the actual crop of farm field itself. This generates this "border" layer, # and returns it. @@ -355,8 +358,9 @@ class mstr_layergen: # on what they are. for i in mstr_mask_blur: if i[0] == self._tag and i[1] == self._value: - osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2])) - break + if self._tag != "place" and (self._value != "sea" or self._value != "ocean"): + osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2])) + break # Begin producing a largely random image samples = 250 # <- We need this in a moment @@ -414,13 +418,6 @@ 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: layer_comp.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" ) @@ -430,6 +427,20 @@ class mstr_layergen: mstr_msg("layergen", "Layer image finalized and saved.") + # Depending on if scenery for XP should be made, AND if normal maps should be made, we would + # need to make them at this exact point + if mstr_xp_genscenery == True: + if mstr_xp_generate_normal_maps == True: + nm = False + for n in mstr_xp_normal_maps: + if n[0] == self._tag and n[1] == self._value: + nm = True + break + if nm == True: + nrm = mstr_xp_normalmap(self._latitude, self._longitude, self._tag, self._value, self._lat_number, self._lng_number, self._latlngfld) + nrm.build_normalmap() + + # Let's try our hand at pseudo shadows if mstr_shadow_enabled == True: if mstr_shadow_shift >= 2: @@ -759,6 +770,12 @@ class mstr_layergen: # need to make them at this exact point if mstr_xp_genscenery == True: if mstr_xp_generate_normal_maps == True: - nrm = mstr_xp_normalmap(self._latitude, self._longitude, self._tag, self._value, self._lat_number, self._lng_number, self._latlngfld) - nrm.build_normalmap() + nm = False + for n in mstr_xp_normal_maps: + if n[0] == self._tag and n[1] == self._value: + nm = True + break + if nm == True: + nrm = mstr_xp_normalmap(self._latitude, self._longitude, self._tag, self._value, self._lat_number, self._lng_number, self._latlngfld) + nrm.build_normalmap() diff --git a/orthographic.py b/orthographic.py index 09024db..c692a16 100644 --- a/orthographic.py +++ b/orthographic.py @@ -20,6 +20,7 @@ from layergen import * from photogen import * from osmxml import * from tilegen import * +from xp_dsfgen import * # The main class which handles the rest @@ -110,18 +111,18 @@ class mstr_orthographic: mstr_msg("orthographic", "Created Tiles sub folder: " + self._latlngfld) # Generate the orthos folder - if not os.path.exists(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/orthos"): - os.makedirs(self._output + "/Tiles/z_orthograpic_" + self._latlngfld +"/orthos") + if not os.path.exists(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/orthos"): + os.makedirs(self._output + "/Tiles/z_orthographic_" + self._latlngfld +"/orthos") mstr_msg("orthographic", "Created tile orthos folder") if mstr_xp_genscenery == True: - if not os.path.exists(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/terrain"): - os.makedirs(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/terrain") + if not os.path.exists(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/terrain"): + os.makedirs(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/terrain") mstr_msg("orthographic", "Created X-Plane tile terrain folder") if mstr_xp_generate_normal_maps == True: - if not os.path.exists(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/normals"): - os.makedirs(self._output + "/Tiles/z_orthograpic_" + self._latlngfld + "/normals") + if not os.path.exists(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/normals"): + os.makedirs(self._output + "/Tiles/z_orthographic_" + self._latlngfld + "/normals") mstr_msg("orthographic", "Created X-Plane tile normals folder") # The tile is constructed of many smaller parts. We walk through the @@ -170,7 +171,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/" + self._latlngfld + "/orthos/" + str(cur_tile_y) + "_" + str(cur_tile_x) + ".jpg") == False: + if os.path.isfile(mstr_datafolder + "Tiles/" + self._latlngfld + "/orthos/" + str(cur_tile_y) + "_" + str(cur_tile_x) + ".dds") == False: # Let the user know mstr_msg("orthographic", "Generating missing orthophoto " + str(cur_tile_y) + "-" + str(cur_tile_x)) @@ -203,6 +204,7 @@ class mstr_orthographic: lg = mstr_layergen(layer[0], layer[1], self._lat, cur_tile_y, self._long, cur_tile_x, layer[2]) lg.set_max_latlng_tile(maxlatlng) lg.set_latlng_folder(self._latlngfld) + lg.open_db() lg.genlayer() curlyr = curlyr+1 mstr_msg("orthographic", "All layers created") @@ -210,11 +212,9 @@ class mstr_orthographic: # We should have all layers now. # Snap a photo with our satellite :) mstr_msg("orthographic", "Generating ortho photo") - pg = mstr_photogen(self._lat, self._long, cur_tile_y, cur_tile_x, maxlatlng[0], maxlatlng[1]) + 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("") @@ -254,6 +254,21 @@ class mstr_orthographic: mstr_msg("orthographic", "Generation of all tiles completed!") + + # Complete scenery + if mstr_xp_genscenery == True: + dsf = mstr_xp_dsfgen(self._lat, self._long, mlat, mlng, self._vstep) + dsf.build_dsf_for_tile() + mstr_msg("orthographic", "X-Plane scenery completed") + + mstr_msg("orthographic", "Final step completed.") + mstr_msg("orthographic", "Tile data in: " + self._output + "/Tiles/z_orthographic_" + self._latlngfld) + print("") + mstr_msg("orthographic", "Thanks for using Orthographic! -- Best, Marcus") + print("") + + # Let's leave this out for the moment + """ mstr_msg("orthographic", "Generating ZL16 tiles and keeping airport tiles") tg = mstr_tilegen(self._lat, self._lng, self._vstep, top_lat, top_lng) tg.genTiles() @@ -264,6 +279,7 @@ class mstr_orthographic: print("") mstr_msg("orthographic", "Thanks for using Orthographic! -- Best, Marcus") print("") + """ diff --git a/photogen.py b/photogen.py index 99b8bb0..51c9778 100644 --- a/photogen.py +++ b/photogen.py @@ -88,6 +88,8 @@ class mstr_photogen: # Generate the layer as if it were part of the OSM data lg = mstr_layergen(tag, value, self._lat, self._ty, self._lng, self._tx, False, is_completion=True) lg.set_max_latlng_tile(self._maxlatlng) + lg.set_latlng_folder(self._latlngfld) + lg.open_db() lg.genlayer() # Load the image @@ -100,6 +102,16 @@ class mstr_photogen: self._tile = completion + # There may be some tiles that have a larger sea or even an ocean in them - these need to be + # removed from the final tile + ocean_pix = self._tile.load() + for y in range(self._tile.width): + for x in range(self._tile.height): + p = ocean_pix[x,y] + if p[0] == 255 and p[1] == 0 and p[2] == 255: + t = (0,0,0,0) + ocean_pix[x,y] = t + # We are now in posession of the final image. # Scale to correct size. @@ -107,15 +119,18 @@ class mstr_photogen: # This we can save accordingly. self._tile.convert('RGB').save(mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".png") + # Now we convert this into a DDS with image.Image(filename=mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".png") as img: img.compression = "dxt1" - img.save(mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".dds") + img.save(filename=mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".dds") # TODO: CUT OUT OCEANS AND SEAS IN DDS os.remove(mstr_datafolder + "Tiles/z_orthographic_" + self._latlngfld + "/orthos/" + str(self._ty) + "_" + str(self._tx) + ".png") + + # This checks the final image for empty patches. Should one be # found, we will generate something to fill the gap. If this is # the case, we will also note this in the database for the tile, diff --git a/tiledb.py b/tiledb.py index 021f59e..b05c1ae 100644 --- a/tiledb.py +++ b/tiledb.py @@ -18,13 +18,14 @@ from functions import * from log import * class mstr_tiledb: - def __init__(self, lat, lng): + def __init__(self, lat, lng, latlngfld): # Note coords self._latitude = lat self._longitude = lng + self._latlngfld = latlngfld # The db file will be created, should it not exist - self._conn = sqlite3.connect(mstr_datafolder + "Tiles/" + str(self._latitude) + "_" + str(self._longitude) + "/data.db") + self._conn = sqlite3.connect(mstr_datafolder + "Tiles/z_orthographic_" + latlngfld + "/data.db") self._crs = self._conn.cursor() #mstr_msg("tiledb", "Database object initiated") diff --git a/xp_dsfgen.py b/xp_dsfgen.py index 4ee910a..d764230 100644 --- a/xp_dsfgen.py +++ b/xp_dsfgen.py @@ -209,8 +209,3 @@ class mstr_xp_dsfgen: self.convert_dsf_text() mstr_msg("xp_dsfgen", "[X-Plane] DSF for tile completed") - - -# Testing -dsf = mstr_xp_dsfgen(51, 7, 101, 63, 0.01010) -dsf.build_dsf_for_tile() \ No newline at end of file diff --git a/xp_normalmap.py b/xp_normalmap.py index 3f1850b..56996c3 100644 --- a/xp_normalmap.py +++ b/xp_normalmap.py @@ -42,7 +42,7 @@ class mstr_xp_normalmap: # then provide it def load_layer(self): qtr = int(mstr_photores / 4) - image = Image.open(mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._lng) + "_" + self._tag + "_" + self._value + "_layer.png") + image = Image.open(mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._tv) + "_" + str(self._lng) + "-" + str(self._th) + "_" + self._tag + "-" + self._value + "_layer.png") image = image.resize((qtr,qtr), Image.Resampling.LANCZOS) mstr_msg("xp_normalmap", "[X-Plane] Layer image loaded") return image @@ -50,13 +50,16 @@ class mstr_xp_normalmap: # A few mathematical calls we need # -------------------------------------------------------- - def intensity(pixel): + def intensity(self, pixel): avg = (pixel[0] + pixel[1] + pixel[2]) / 3 - pavg = 255.0 / avg + if avg > 0: + pavg = 255.0 / avg + else: + pavg = 0 return pavg - def clamp(px, mpx): + def clamp(self, px, mpx): if px > mpx-1: return mpx-1 else: @@ -66,11 +69,11 @@ class mstr_xp_normalmap: return px - def map_component(px): + def map_component(self, px): return (px + 1.0) * (255.0 / 2.0) - def normalize_vector(v): + def normalize_vector(self, v): vc = np.array([v[0], v[1], v[2]]) norm = np.linalg.norm(vc) nv = vc / norm @@ -81,7 +84,8 @@ class mstr_xp_normalmap: # The Big Mac. Generate the normal map def generate_normal_map_for_layer(self, image): mstr_msg("xp_normalmap", "[X-Plane] Beginning normal map generation") - nmp = Image.new("RGBA", (image.width, image.height), (128,128,255,255)) + #nmp = Image.new("RGBA", (image.width, image.height), (128,128,255,255)) + nmp = Image.new("RGBA", (image.width, image.height), (0,0,0,0)) org = image.load() nmp_pix = nmp.load() @@ -93,44 +97,49 @@ class mstr_xp_normalmap: a = v * 255.0 alpha = int(a) + # Let's try some shenanigans - for y in range(image.width): - for x in range(image.height): - # Neighboring pixels - px_t = org[ self.clamp(x, image.width), self.clamp(y+1, image.height) ] - px_tr = org[ self.clamp(x+1, image.width), self.clamp(y+1, image.height) ] - px_r = org[ self.clamp(x+1, image.width), self.clamp(y, image.height) ] - px_br = org[ self.clamp(x+1, image.width), self.clamp(y+1, image.height) ] - px_b = org[ self.clamp(x, image.width), self.clamp(y-1, image.height) ] - px_bl = org[ self.clamp(x-1, image.width), self.clamp(y-1, image.height) ] - px_l = org[ self.clamp(x-1, image.width), self.clamp(y, image.height) ] - px_tl = org[ self.clamp(x-1, image.width), self.clamp(y+1, image.height) ] + w = image.width + h = image.height + for y in range(h): + for x in range(w): + p = org[x,y] + if p[3] > 0: # Only do something if there is something to do in layer + # Neighboring pixels + px_t = org[ self.clamp(x, w), self.clamp(y+1, h) ] + px_tr = org[ self.clamp(x+1, w), self.clamp(y+1, h) ] + px_r = org[ self.clamp(x+1, w), self.clamp(y, h) ] + px_br = org[ self.clamp(x+1, w), self.clamp(y+1, h) ] + px_b = org[ self.clamp(x, w), self.clamp(y-1, h) ] + px_bl = org[ self.clamp(x-1, w), self.clamp(y-1, h) ] + px_l = org[ self.clamp(x-1, w), self.clamp(y, h) ] + px_tl = org[ self.clamp(x-1, w), self.clamp(y+1, h) ] - # Intensities of pixels - it_t = self.intensity(px_t) - it_tr = self.intensity(px_tr) - it_r = self.intensity(px_r) - it_br = self.intensity(px_br) - it_b = self.intensity(px_b) - it_bl = self.intensity(px_bl) - it_l = self.intensity(px_l) - it_tl = self.intensity(px_tl) + # Intensities of pixels + it_t = self.intensity(px_t) + it_tr = self.intensity(px_tr) + it_r = self.intensity(px_r) + it_br = self.intensity(px_br) + it_b = self.intensity(px_b) + it_bl = self.intensity(px_bl) + it_l = self.intensity(px_l) + it_tl = self.intensity(px_tl) - # Sobel filter - dx = (it_tr + 2.0 * it_r + it_br) - (it_tl + 2.0 * it_l + it_bl) - dy = (it_bl + 2.0 * it_b + it_br) - (it_tl + 2.0 * it_t + it_tr) - dz = 10 # This is usually a good value for strength - v = (dx, dy, dz) - nrm = self.normalize_vector(v) - - # Invert height for our Orthos - if nrm[1] > 0: - nrm[1] = 0 - (abs(nrm[1])) - else: - nrm[1] = abs(nrm[1]) + # Sobel filter + dx = (it_tr + 2.0 * it_r + it_br) - (it_tl + 2.0 * it_l + it_bl) + dy = (it_bl + 2.0 * it_b + it_br) - (it_tl + 2.0 * it_t + it_tr) + dz = 10 # This is usually a good value for strength + v = (dx, dy, dz) + nrm = self.normalize_vector(v) + + # Invert height for our Orthos + if nrm[1] > 0: + nrm[1] = 0 - (abs(nrm[1])) + else: + nrm[1] = abs(nrm[1]) - # Set pixel - nmp_pix[x,y] = (int(self.map_component(nrm[0])), int(self.map_component(nrm[1])), int(self.map_component(nrm[2])), alpha) + # Set pixel + nmp_pix[x,y] = (int(self.map_component(nrm[0])), int(self.map_component(nrm[1])), int(self.map_component(nrm[2])), alpha) mstr_msg("xp_normalmap", "[X-Plane] Normal map generated") return nmp