diff --git a/colorizer.py b/colorizer.py new file mode 100644 index 0000000..2e43393 --- /dev/null +++ b/colorizer.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 +# ------------------------------------------------------------------- +# colorizer.py +# A class providing functions to adjust any image to a specific +# background color. Similar to GIMP's colorize function. +# ------------------------------------------------------------------- + + +from PIL import Image + +class mstr_colorizer: + def __init__(self, baseimg): + self._baseimg = baseimg + self._basepix = self._baseimg.load() + self._desaturate() + + def _desaturate(self): + grs = Image.new("RGBA", (self._baseimg.width, self._baseimg.height)) + pix = grs.load() + for y in range(0, self._baseimg.height): + for x in range(0, self._baseimg.width): + cl = self._basepix[x,y] + sum = cl[0]+cl[1]+cl[2] + gr = int(sum/3) + c = (gr,gr,gr,cl[3]) + pix[x,y] = c + + self._grs = grs + + + # img is the background, which can be larger than the image to colorize + # xpos and ypos is where the image should appear + def _colorizeToImage(self, img, xpos, ypos): + colorized = Image.new("RGBA", (self._grs.width, self._grs.height)) + clrpix = colorized.load() + imgpix = img.load() + grs = self._grs.load() + + for y in range(0, self._grs.height): + for x in range(0, self._grs.width): + if xpos+x >= 0 and xpos+x < img.width and ypos+y >= 0 and ypos+y < img.height: + bgpx = imgpix[xpos+x, ypos+y] + grpx = grs[x,y] + gr = grpx[0] + av = (gr/255) * 1.2 + + r = bgpx[0] + int(av * bgpx[0]) + g = bgpx[1] + int(av * bgpx[1]) + b = bgpx[2] + int(av * bgpx[2]) + + colrz = (r,g,b,grpx[3]) + clrpix[x,y] = colrz + + return colorized + diff --git a/defines.py b/defines.py index 7e411be..36aa7ce 100644 --- a/defines.py +++ b/defines.py @@ -46,7 +46,7 @@ mstr_clear_cache = True # Whether or not you want to see progress of the tool as it walks on. # High recommendation to leave this on. -mstr_show_log = True +mstr_show_log = False # Should a pseudo shadow be rendered on certain elements? @@ -56,7 +56,7 @@ mstr_show_log = True # You can, however, disable the shadow rendering layer here. mstr_shadow_enabled = True mstr_shadow_strength = 0.65 -mstr_shadow_shift = 16 +mstr_shadow_shift = 4 mstr_shadow_floor_h = 2.8 # 2.5m ceiling height + 30cm concrete per floor # The tags that cast shadows mstr_shadow_casters = [ @@ -132,9 +132,9 @@ mstr_ortho_layers = [ ("barrier", "hedge", "natural", "heath"), ("landuse", "vineyard", "landuse", "meadow"), ("natural", "bare_rock", "natural", "bare_rock"), - ("highway", "track", 3), - ("highway", "path", 3), - ("highway", "footway", 4), + ("highway", "track", 1), + ("highway", "path", 1), + ("highway", "footway", 1), ("leisure", "park", "leisure", "green"), ("leisure", "dog_park", "leisure", "green"), ("leisure", "garden", "leisure", "green"), @@ -145,20 +145,20 @@ mstr_ortho_layers = [ ("landuse", "farmyard", "landuse", "farmland"), ("landuse", "military", "landuse", "residential-boundary"), # Z-Order 2 - ("highway", "service", 6), - ("highway", "residential", 12), - ("highway", "primary", 25), - ("highway", "secondary", 13), - ("highway", "tertiary", 20), - ("highway", "unclassified", 17), - ("highway", "living_street", 12), - ("waterway", "stream", 2), + ("highway", "service", 2), + ("highway", "residential", 4), + ("highway", "primary", 6), + ("highway", "secondary", 3), + ("highway", "tertiary", 5), + ("highway", "unclassified", 4), + ("highway", "living_street", 3), + ("waterway", "stream", 1), ("amenity", "parking", "amenity", "parking"), ("amenity", "school", "amenity", "school"), ("leisure", "nature_reserve", "landuse", "forest"), ("landuse", "forest", "landuse", "forest"), ("natural", "wood", "natural", "wood"), - ("natural", "tree_row", 22), + ("natural", "tree_row", 5), ("natural", "wetland", "natural", "wetland"), ("natural", "scrub", "natural", "scrub"), ("natural", "heath", "natural", "heath"), @@ -168,13 +168,13 @@ mstr_ortho_layers = [ ("natural", "bay", "natural", "beach"), ("natural", "beach", "natural", "beach"), ("leisure", "swimming_pool", "natural", "water"), - ("highway", "pedestrian", 4), + ("highway", "pedestrian", 1), # Z-Order 4 - ("highway", "motorway", 32), - ("railway", "rail", 5), + ("highway", "motorway", 8), + ("railway", "rail", 2), # Z-Order 5 - ("aeroway", "taxiway", 42), - ("aeroway", "runway", 80), + ("aeroway", "taxiway", 11), + ("aeroway", "runway", 20), ("building", "detached", "building", "common"), ("building", "church", "building", "common"), ("building", "hotel", "building", "industrial"), @@ -199,7 +199,7 @@ mstr_ortho_layers = [ ("water", "pond", "natural", "water"), ("water", "river", "natural", "water"), ("natural", "water", "natural", "water"), - ("waterway", "river", 10), + ("waterway", "river", 3), ("place", "sea", "natural", "sea"), ("place", "ocean", "natural", "sea") ] @@ -208,51 +208,51 @@ mstr_ortho_layers = [ # Blur values for the single masks of the ortho layers mstr_mask_blur = [ # Z-Order 0 - ("landuse", "residential", 20), - ("boundary", "administrative", 20), + ("landuse", "residential", 5), + ("boundary", "administrative", 5), # Z-Order 1 - ("landuse", "grass", 12), - ("landuse", "cemetery", 12), - ("landuse", "greenfield", 12), - ("landuse", "orchard", 12), - ("landuse", "meadow", 12), - ("landuse", "construction", 5), + ("landuse", "grass", 3), + ("landuse", "cemetery", 3), + ("landuse", "greenfield", 3), + ("landuse", "orchard", 3), + ("landuse", "meadow", 3), + ("landuse", "construction", 1), ("barrier", "hedge", 1), - ("landuse", "recreation_ground", 20), - ("landuse", "vineyard", 12), - ("natural", "grassland", 12), - ("natural", "wetland", 30), - ("natural", "scrub", 15), - ("natural", "heath", 15), - ("leisure", "park", 15), - ("leisure", "golf_course", 25), - ("leisure", "dog_park", 15), - ("leisure", "garden", 20), - ("leisure", "sports_centre", 5), - ("leisure", "pitch", 2), + ("landuse", "recreation_ground", 5), + ("landuse", "vineyard", 3), + ("natural", "grassland", 3), + ("natural", "wetland", 7), + ("natural", "scrub", 4), + ("natural", "heath", 4), + ("leisure", "park", 4), + ("leisure", "golf_course", 6), + ("leisure", "dog_park", 4), + ("leisure", "garden", 5), + ("leisure", "sports_centre", 2), + ("leisure", "pitch", 1), ("leisure", "playground", 2), - ("landuse", "farmland", 10), - ("landuse", "farmyard", 10), + ("landuse", "farmland", 3), + ("landuse", "farmyard", 3), # Z-Order 2 - ("landuse", "forest", 12), - ("leisure", "nature_reserve", 12), - ("natural", "wood", 12), - ("natural", "tree_row", 12), - ("landuse", "military", 15), + ("landuse", "forest", 3), + ("leisure", "nature_reserve", 3), + ("natural", "wood", 3), + ("natural", "tree_row", 3), + ("landuse", "military", 4), # Z-Order 3 - ("natural", "bare_rock", 25), - ("natural", "water", 4), - ("natural", "bay", 30), - ("natural", "beach", 30), - ("water", "lake", 10), - ("water", "pond", 10), - ("water", "river", 10), - ("leisure", "swimming_pool", 10), - ("waterway", "river", 10), - ("waterway", "stream", 4), + ("natural", "bare_rock", 6), + ("natural", "water", 1), + ("natural", "bay", 7), + ("natural", "beach", 7), + ("water", "lake", 3), + ("water", "pond", 3), + ("water", "river", 3), + ("leisure", "swimming_pool", 3), + ("waterway", "river", 3), + ("waterway", "stream", 2), ("amenity", "parking", 1), ("amenity", "school", 1), - ("highway", "pedestrian", 12), + ("highway", "pedestrian", 3), # Z-Order 4 ("highway", "motorway", 0.5), ("highway", "primary", 0.5), @@ -262,10 +262,10 @@ mstr_mask_blur = [ ("highway", "living_street", 0.5), ("highway", "residential", 0.5), ("highway", "service", 0.5), - ("highway", "footway", 2), - ("highway", "track", 2), - ("highway", "path", 2), - ("railway", "rail", 2), + ("highway", "footway", 1), + ("highway", "track", 1), + ("highway", "path", 1), + ("railway", "rail", 1), # Z-Order 5 ("aeroway", "taxiway", 2), ("aeroway", "runway", 2), diff --git a/defines_zl18.py b/defines_zl18.py new file mode 100644 index 0000000..31e6f07 --- /dev/null +++ b/defines_zl18.py @@ -0,0 +1,446 @@ + +# ------------------------------------------------------------------- +# 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 +# ------------------------------------------------------------------- +# defines.py +# Variables we need in all functions. Each one is documented below. +# ------------------------------------------------------------------- + +# Your data folder - meaning where the cache is stored, and where the finished +# tiles will go. This is also the folder where the image generation sources are +# stored. +#mstr_datafolder = "M:/Developer/Projects/orthographic/" +mstr_datafolder = "D:/Developer/Projects/orthographic/" +#mstr_datafolder = "/home/marcus/Developer/Projects/orthographic/" +# Switched to Linux, so path is amended + + +# API endpoint to acquire OSM data (bonus: I have my own) +#mstr_osm_endpoint = "https://marstr.online/osm/v1/" +mstr_osm_endpoint = "http://localhost/og.php" + +# Define the texture resolution you want to have your photos at. +mstr_photores = 2048 + +# Radius of zoom level 18 aerials around airports with ICAO code +# Value is in tiles - not in km +# +# Value = 5: +# ##### +# ##### +# ##X## +# ##### +# ##### +# +# The "X" is the tile with the airport +mstr_airport_radius = 5 + +# Clear cache after generating a complete tile? +mstr_clear_cache = True +# Removes the masks etc after a tile has been generated. Strong recommendation to set this +# to True. + +# Whether or not you want to see progress of the tool as it walks on. +# High recommendation to leave this on. +mstr_show_log = False + + +# Should a pseudo shadow be rendered on certain elements? +# The sun is usually somewhere when a photo is taken during the day. Therefore, +# some kind of shadow is cast in a direction, but this depends on a lot of factors. +# 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_shift = 16 +mstr_shadow_floor_h = 2.8 # 2.5m ceiling height + 30cm concrete per floor +# The tags that cast shadows +mstr_shadow_casters = [ + ("landuse", "forest"), + ("leisure", "nature_reserve"), + ("natural", "wood") +] + + +# Whether or not to generate X-Plane Scenery files +mstr_xp_genscenery = True + +# Generate normal maps for X-Plane scenery? +# Strong recommendation: yes +mstr_xp_scn_normalmaps = True + +# Paths to required X-Plane scenery tools +mstr_xp_meshtool = "/home/marcus/Developer/Projects/orthographic/bin/MeshTool" +mstr_xp_ddstool = "/home/marcus/Developer/Projects/orthographic/bin/DDSTool" +mstr_xp_dsftool = "/home/marcus/Developer/Projects/orthographic/bin/DSFTool" +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 +mstr_xp_ortho_location = "/home/marcus/Data/Sim/Simulator/orthographic/" + +# 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 +# good-looking orthos in the simulator. +mstr_xp_normal_maps = [ + ("landuse", "farmland"), + ("landuse", "meadow"), + ("landuse", "orchard"), + ("landuse", "forest"), + ("natural", "wetland"), + ("natural", "bare_rock"), + ("natural", "scrub"), + ("natural", "heath"), + ("natural", "sand"), + ("natural", "desert"), + ("leisure", "nature_reserve"), + ("building", "*") +] + +# How much of a tile we need for each zoom level. The higher +# the zoom level, the smaller the area to generate a mask of - but also +# higher detail. +mstr_zl_16 = 0.064 +mstr_zl_17 = 0.048 +mstr_zl_18 = 0.016 +mstr_zl_19 = 0.008 + + +# The layers we will process, from bottom to top. +# Tag, and value. +# Strong recommendation NOT to alter this list - +# generating the orthos depends on the pool of source +# material I provide for these layers. If you add your own +# OSM tag, and there is no source material, the script will +# most likely crash. +mstr_ortho_layers = [ + # Z-Order 0 + #("boundary", "administrative", "admin_level", ["8", "9", "10", "12"], "landuse", "residential-boundary"), + # Z-Order 1 + ("landuse", "residential", "landuse", "residential-boundary"), + ("landuse", "grass", "landuse", "grass"), + ("landuse", "cemetery", "landuse", "grass"), + ("landuse", "recreation_ground", "landuse", "meadow"), + ("leisure", "golf_course", "leisure", "golf_course"), + ("landuse", "greenfield", "landuse", "grass"), + ("landuse", "orchard", "landuse", "meadow"), + ("landuse", "meadow", "landuse", "meadow"), + ("landuse", "construction", "landuse", "construction"), + ("natural", "grassland", "landuse", "meadow"), + ("barrier", "hedge", "natural", "heath"), + ("landuse", "vineyard", "landuse", "meadow"), + ("natural", "bare_rock", "natural", "bare_rock"), + ("highway", "track", 3), + ("highway", "path", 3), + ("highway", "footway", 4), + ("leisure", "park", "leisure", "green"), + ("leisure", "dog_park", "leisure", "green"), + ("leisure", "garden", "leisure", "green"), + ("leisure", "sports_centre", "leisure", "green"), + ("leisure", "pitch", "leisure", "green"), + ("leisure", "playground", "leisure", "green"), + ("landuse", "farmland", "landuse", "farmland"), + ("landuse", "farmyard", "landuse", "farmland"), + ("landuse", "military", "landuse", "residential-boundary"), + # Z-Order 2 + ("highway", "service", 6), + ("highway", "residential", 12), + ("highway", "primary", 25), + ("highway", "secondary", 13), + ("highway", "tertiary", 20), + ("highway", "unclassified", 17), + ("highway", "living_street", 12), + ("waterway", "stream", 2), + ("amenity", "parking", "amenity", "parking"), + ("amenity", "school", "amenity", "school"), + ("leisure", "nature_reserve", "landuse", "forest"), + ("landuse", "forest", "landuse", "forest"), + ("natural", "wood", "natural", "wood"), + ("natural", "tree_row", 22), + ("natural", "wetland", "natural", "wetland"), + ("natural", "scrub", "natural", "scrub"), + ("natural", "heath", "natural", "heath"), + ("natural", "sand", "natural", "sand"), + ("natural", "desert", "natural", "desert"), + # Z-Order 3 + ("natural", "bay", "natural", "beach"), + ("natural", "beach", "natural", "beach"), + ("leisure", "swimming_pool", "natural", "water"), + ("highway", "pedestrian", 4), + # Z-Order 4 + ("highway", "motorway", 32), + ("railway", "rail", 5), + # Z-Order 5 + ("aeroway", "taxiway", 42), + ("aeroway", "runway", 80), + ("building", "detached", "building", "common"), + ("building", "church", "building", "common"), + ("building", "hotel", "building", "industrial"), + ("building", "farm", "building", "industrial"), + ("building", "semidetached_house", "building", "common"), + ("building", "apartments", "building", "common"), + ("building", "civic", "building", "common"), + ("building", "garage", "building", "industrial"), + ("building", "office", "building", "office"), + ("building", "retail", "building", "industrial"), + ("building", "industrial", "building", "industrial"), + ("building", "house", "building", "house"), + ("building", "terrace", "building", "industrial"), + ("building", "hangar", "building", "industrial"), + ("building", "school", "building", "common"), + ("building", "kindergarten", "building", "kindergarten"), + ("building", "public", "building", "public"), + ("building", "commercial", "building", "commercial"), + ("building", "warehouse", "building", "warehouse"), + ("building", "yes", "building", "common"), + ("water", "lake", "natural", "water"), + ("water", "pond", "natural", "water"), + ("water", "river", "natural", "water"), + ("natural", "water", "natural", "water"), + ("waterway", "river", 10), + ("place", "sea", "natural", "sea"), + ("place", "ocean", "natural", "sea") +] + + +# Blur values for the single masks of the ortho layers +mstr_mask_blur = [ + # Z-Order 0 + ("landuse", "residential", 20), + ("boundary", "administrative", 20), + # Z-Order 1 + ("landuse", "grass", 12), + ("landuse", "cemetery", 12), + ("landuse", "greenfield", 12), + ("landuse", "orchard", 12), + ("landuse", "meadow", 12), + ("landuse", "construction", 5), + ("barrier", "hedge", 1), + ("landuse", "recreation_ground", 20), + ("landuse", "vineyard", 12), + ("natural", "grassland", 12), + ("natural", "wetland", 30), + ("natural", "scrub", 15), + ("natural", "heath", 15), + ("leisure", "park", 15), + ("leisure", "golf_course", 25), + ("leisure", "dog_park", 15), + ("leisure", "garden", 20), + ("leisure", "sports_centre", 5), + ("leisure", "pitch", 2), + ("leisure", "playground", 2), + ("landuse", "farmland", 10), + ("landuse", "farmyard", 10), + # Z-Order 2 + ("landuse", "forest", 12), + ("leisure", "nature_reserve", 12), + ("natural", "wood", 12), + ("natural", "tree_row", 12), + ("landuse", "military", 15), + # Z-Order 3 + ("natural", "bare_rock", 25), + ("natural", "water", 4), + ("natural", "bay", 30), + ("natural", "beach", 30), + ("water", "lake", 10), + ("water", "pond", 10), + ("water", "river", 10), + ("leisure", "swimming_pool", 10), + ("waterway", "river", 10), + ("waterway", "stream", 4), + ("amenity", "parking", 1), + ("amenity", "school", 1), + ("highway", "pedestrian", 12), + # Z-Order 4 + ("highway", "motorway", 0.5), + ("highway", "primary", 0.5), + ("highway", "secondary", 0.5), + ("highway", "tertiary", 0.5), + ("highway", "unclassified", 0.5), + ("highway", "living_street", 0.5), + ("highway", "residential", 0.5), + ("highway", "service", 0.5), + ("highway", "footway", 2), + ("highway", "track", 2), + ("highway", "path", 2), + ("railway", "rail", 2), + # Z-Order 5 + ("aeroway", "taxiway", 2), + ("aeroway", "runway", 2), + ("building", "detached", 1), + ("building", "church", 1), + ("building", "hotel", 1), + ("building", "farm", 1), + ("building", "semidetached_house", 1), + ("building", "apartments", 1), + ("building", "civic", 1), + ("building", "garage", 1), + ("building", "office", 1), + ("building", "retail", 1), + ("building", "industrial", 1), + ("building", "house", 1), + ("building", "terrace", 1), + ("building", "hangar", 1), + ("building", "school", 1), + ("building", "kindergarten", 1), + ("building", "public", 1), + ("building", "commercial", 1), + ("building", "warehouse", 1), + ("building", "yes", 0), + ("place", "sea", 1), + ("place", "ocean", 1) +] + + +# Base colors for different building types +mstr_building_base_colors = [ + ("detached", [ + "#693333", "#592b2b", "#513434", "#4a1e1e", "#362626", + "#534136", "#4d3424", "#534b45", "#553724", "#574c45", + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("church", [ + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("hotel", [ + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("farm", [ + "#693333", "#592b2b", "#513434", "#4a1e1e", "#362626", + "#534136", "#4d3424", "#534b45", "#553724", "#574c45" + ] + ), + ("semidetached_house", [ + "#693333", "#592b2b", "#513434", "#4a1e1e", "#362626", + "#534136", "#4d3424", "#534b45", "#553724", "#574c45", + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("apartments", [ + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("civic", [ + "#7b848b", "#5d6d7b", "#5e646a", "#454d53", "#585b5e", + "#877a78", "#797372", "#797372", "#6f5550", "#7c7574" + ] + ), + ("garage", [ + "#693333", "#592b2b", "#513434", "#4a1e1e", "#362626", + "#523731", "#46484e", "#33353a", "#3a3733", "#5d5a57" + ] + ), + ("office", [ + "#403a33", "#4f4b46", "#413629", "#574c3f", "#3a2e21", + "#aaaeb6", "#939cac", "#8a919d", "#a0b9bf", "#8d999b", + "#49575a", "#273d43", "#313a3c", "#484f50", "#212d30" + ] + ), + ("retail", [ + "#403a33", "#4f4b46", "#413629", "#574c3f", "#3a2e21", + "#aaaeb6", "#939cac", "#8a919d", "#a0b9bf", "#8d999b", + "#49575a", "#273d43", "#313a3c", "#484f50", "#212d30" + ] + ), + ("industrial", [ + "#939fa2", "#728080", "#9eafaf", "#4f6061", "#96b2b6", + "#b2b398", "#878868", "#989888", "#bdb79c", "#959386" + ] + ), + ("house", [ + "#693333", "#592b2b", "#513434", "#4a1e1e", "#362626", + "#534136", "#4d3424", "#534b45", "#553724", "#574c45", + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("terrace", [ + "#86898c", "#5e656c", "#6d6868", "#6d6c68", "#46443d", + "#3d4546", "#6b7071", "#716b70", "#738684", "#868073" + ] + ), + ("hangar", [ + "#403a33", "#4f4b46", "#413629", "#574c3f", "#3a2e21", + "#aaaeb6", "#939cac", "#8a919d", "#a0b9bf", "#8d999b", + "#49575a", "#273d43", "#313a3c", "#484f50", "#212d30" + ] + ), + ("school", [ + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("kindergarten", [ + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("public", [ + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("commercial", [ + "#403a33", "#4f4b46", "#413629", "#574c3f", "#3a2e21", + "#aaaeb6", "#939cac", "#8a919d", "#a0b9bf", "#8d999b", + "#49575a", "#273d43", "#313a3c", "#484f50", "#212d30" + ] + ), + ("yes", [ + "#373942", "#40424a", "#363b4f", "#2c2d32", "#444651", + "#454545", "#39393b", "#4b4b4c", "#363638", "#525252" + ] + ), + ("warehouse", [ + "#403a33", "#4f4b46", "#413629", "#574c3f", "#3a2e21", + "#aaaeb6", "#939cac", "#8a919d", "#a0b9bf", "#8d999b", + "#49575a", "#273d43", "#313a3c", "#484f50", "#212d30" + ] + ), +] + + +# Base colors to add some details to all kinds of buildings +mstr_building_detail_colors = [ + (136, 132, 86), # Some kind of moss + (136, 90, 86), # Some kind of rust or darkening + (154, 154, 154), # Other random details + (97, 97, 97) # Square or line details, alpha-blended +] + + +mstr_completion_colors = [ + ("+50+000", [ + (48,63,34), + (81,95,64), + (105,100,64), + (91,105,72), + (78,69,41), + (116,113,78), + (90,94,69), + (58,68,40), + (57,72,41), + (93,103,76), + (139,142,111), + (92,102,73), + (71,86,55), + (103,105,91), + (96,78,56), + (143,141,113), + (107,108,74), + (41,56,34), + (51,63,41), + (137,137,107) + ] + ) +] diff --git a/layergen.py b/layergen.py index ee4faba..60c6d33 100644 --- a/layergen.py +++ b/layergen.py @@ -26,6 +26,7 @@ from tileinfo import * from osmxml import * from functions import * from resourcegen import * +from colorizer import * ImageFile.LOAD_TRUNCATED_IMAGES = True @@ -57,6 +58,10 @@ class mstr_layergen: self._maxlng = maxlatlng[1] mstr_msg("layergen", "Maximum latitude and longitude tile numbers received") + # Set zoom level + def set_zoomlevel(self, zl): + self._zoomlevel = zl + # Set latlng folder def set_latlng_folder(self, latlngfld): self._latlngfld = latlngfld @@ -248,6 +253,9 @@ class mstr_layergen: nc = (tp[0] - diff, tp[1] - diff, tp[2] - diff, tp[3]) treepx[x, y] = nc + if self._zoomlevel == 16: + tree = tree.resize((int(tree.width/4), int(tree.height/4)), resample=Image.Resampling.BILINEAR) + return tree @@ -278,6 +286,11 @@ class mstr_layergen: # We need to differentiate that. if (self._isline == False and self._tag != "building") or (self._is_completion == True): + + # The Perlin map + perlin_map = Image.open(mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/perlin/" + self._tag + "_" + self._value + ".png") + perlin_pix = perlin_map.load() + # Determine where we get the source material from root_folder = mstr_datafolder + "textures/" for s in mstr_ortho_layers: @@ -286,45 +299,6 @@ class mstr_layergen: fld_sub = len(s)-1 root_folder = root_folder + s[fld_main] + "/" + s[fld_sub] - # Determine which sources to use. - src = self.findLayerSource() - srcstr = "" - for s in range(len(src)): - srcstr = srcstr + str(src[s]) - if s < len(src)-1: - srcstr = srcstr + "," - - # Failsafe - if srcstr == "0": - srcstr = "" - numbers = list(range(1, 16)) - src = random.sample(numbers, 5) - for s in range(len(src)): - srcstr = srcstr + str(src[s]) - if s < len(src)-1: - srcstr = srcstr + "," - - # Patch and border sources. There can only be one for each. - brd_src = None - ptc_src = [] - - # Find this layer's predetermined contrast - lyr_contrast = self.findLayerContrast(srcstr) - if lyr_contrast != 0: - mstr_msg("layergen", "Applying contrast value: " + str(lyr_contrast)) - - # Should this not exist yet, we need to create it - #if os.path.isfile(gensrc_ptc) == False: - rg = mstr_resourcegen(self._tag, self._value, src) - rg.setLayerContrast(int(lyr_contrast)) - lyr_res = rg.gensource() - - # Open the images - ptc_src.append(lyr_res[0]) # Used to be an array, so let's keep it - brd_src = lyr_res[1] - - mstr_msg("layergen", "Layer sources selected") - # Generate an edge mask from the original osm_edge = mask.filter(ImageFilter.FIND_EDGES) osm_edge = osm_edge.filter(ImageFilter.MaxFilter) @@ -336,12 +310,17 @@ class mstr_layergen: imgd = ImageDraw.Draw(mask) # Walk through a grid of 100x100 - on the edge image - for y in range(0, mask.height, int(mask.height/100)): - for x in range(0, mask.width, int(mask.width/100)): + fordiv = 100 + if self._zoomlevel == 16: fordiv = 25 + for y in range(0, mask.height, int(mask.height/fordiv)): + for x in range(0, mask.width, int(mask.width/fordiv)): px = epx[x,y] if px[3] == 255: rx = randrange(24,60) ry = randrange(24,60) + if self._zoomlevel == 16: + rx = randrange(6, 15) + ry = randrange(6, 15) f = randrange(1,10) # Randomize the found locations a little @@ -368,22 +347,58 @@ class mstr_layergen: mask = mask.filter(ImageFilter.BoxBlur(radius=i[2])) break - # Begin producing a largely random image - samples = 250 # <- We need this in a moment - for i in range(samples): - imgid = 0 - if len(ptc_src) == 1: imgid = 0 - if len(ptc_src) >= 2: - 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) - b = layer.height - int(ptc_src[imgid].height / 2) - layer.alpha_composite( ptc_src[imgid], ( randrange(l, r), randrange(t, b) ) ) - mstr_msg("layergen", "Layer image generated") + # Find starting point in the Perlin map + prln_step = 0 + if self._zoomlevel == 16: prln_step = 80 + if self._zoomlevel == 18: prln_step = 20 + prln_x = (self._lng_number-1) * prln_step + prln_y = perlin_map.height - (self._lat_number * prln_step) - 1 - # We now need to add the seamless border - layer.alpha_composite( brd_src ) + # First, fill the image with the base colors from the Perlin map, + # plus some variation + prln_pix_step = int(layer.width / prln_step) + for y in range(0, layer.height): + for x in range(0, layer.width): + xp = prln_x + int(x/prln_pix_step) + yp = prln_y + int(y/prln_pix_step) + if xp == perlin_map.width: xp = perlin_map.width - 1 + if yp == perlin_map.height: yp = perlin_map.height - 1 + pc = perlin_pix[xp,yp] + df = randrange(0, 6) + lc = (pc[0]+df, pc[1]+df, pc[2]+df, 255) + #lc = (pc[0], pc[1], pc[2], 255) + layer_pix[x,y] = lc + clrz_layer = layer.copy() + + # ------------------------------------------ + # Begin producing a largely random image + samples = 0 + if self._zoomlevel == 18: samples = 250 + if self._zoomlevel == 16: samples = 2000 + + txts = glob.glob(mstr_datafolder + "textures/" + self._tag + "/" + self._value + "/*.png") + ptc_src = Image.open(txts[randrange(0, len(txts))]) + if self._zoomlevel == 16: ptc_src = ptc_src.resize((250,250), resample=Image.Resampling.BILINEAR) + clrz = mstr_colorizer(ptc_src) + + tmp_layer = Image.new("RGBA", (self._imgsize, self._imgsize)) + + for i in range(samples): + xp = randrange(-125, 1924) + yp = randrange(-125, 1924) + ptc = clrz._grs.rotate(randrange(0, 360), expand=True) + tmp_layer.alpha_composite(ptc, (xp,yp)) + + # Add the seamless border + brd = Image.open(mstr_datafolder + "textures/" + self._tag + "/" + self._value + "/brd.png") + brd_clrz = mstr_colorizer(brd) + tmp_layer.alpha_composite(brd_clrz._grs) + + tmp_layer.putalpha(51) + layer.alpha_composite(tmp_layer) + mstr_msg("layergen", "Layer image generated") + + #--------------------------------------------- # Here we need to do some magic to make some features look more natural if (self._tag == "landuse" and self._value == "meadow") or ( @@ -391,7 +406,7 @@ class mstr_layergen: self._tag == "natural" and self._value == "heath") or ( self._tag == "landuse" and self._value == "cemetery") or ( self._tag == "landuse" and self._value == "residential"): - amt = randrange(5, 21) + amt = randrange(150, 301) masks = glob.glob(mstr_datafolder + "textures/tile/completion/*.png") patchtags = [ ["landuse", "meadow"], @@ -400,52 +415,14 @@ class mstr_layergen: ["natural", "scrub"] ] for i in range(1, amt + 1): - pick = randrange(0, len(masks)) - patchmask = Image.open(masks[pick]) - patchmask = patchmask.rotate(randrange(0, 360), expand=True) - - # Make sure patch is within bounds - if patchmask.width > self._imgsize or patchmask.height > self._imgsize: - patchmask = patchmask.resize((mstr_photores, mstr_photores), Image.Resampling.BILINEAR) - - patchpix = patchmask.load() - # Pick from possible tags and values for the patches - numbers = list(range(1, 16)) - src = random.sample(numbers, 5) - - patchpick = randrange(0, len(patchtags)) - ctr = randrange(1, 4) - rg = mstr_resourcegen(patchtags[patchpick][0], patchtags[patchpick][1], src) - rg.setLayerContrast(ctr) - ptch = rg.gensource() - - # Generate a full size of the source - ptc_full = Image.new("RGBA", (mstr_photores, mstr_photores)) - - # Generate the source image - for p in range(1, 201): - rx = randrange(0 - int(ptch[0].width / 2), ptc_full.width - int(ptch[0].width / 2)) - ry = randrange(0 - int(ptch[0].height / 2), ptc_full.height - int(ptch[0].height / 2)) - ptc_full.alpha_composite(ptch[0], dest=(rx, ry)) - - rg_img = ptc_full - rg_pix = rg_img.load() - - # The patch to be used in the layer - layerpatch = Image.new("RGBA", (patchmask.width, patchmask.height)) - lp_pix = layerpatch.load() - for y in range(0, patchmask.height): - for x in range(0, patchmask.width): - ptc_msk = patchpix[x,y] - if ptc_msk[3] > 0: - oc = rg_pix[x,y] - nc = ( oc[0], oc[1], oc[2], ptc_msk[3] ) - lp_pix[x,y] = nc - - #layerpatch = layerpatch.rotate(randrange(0, 360), expand=True) - - lx = randrange(self._imgsize - layerpatch.width) - ly = randrange(self._imgsize - layerpatch.height) + layerpatch = Image.open(mstr_datafolder + "textures/tile/completion_color/p" + str(randrange(1,14)) + ".png") + if self._zoomlevel == 16: + lpw = int(layerpatch.width/4) + lph = int(layerpatch.height/4) + layerpatch = layerpatch.resize((lpw,lph), resample=Image.Resampling.BILINEAR) + layerpatch = layerpatch.rotate(randrange(0, 360), expand=True) + lx = randrange(0, mstr_photores-layerpatch.width) + ly = randrange(0, mstr_photores-layerpatch.height) layer.alpha_composite(layerpatch, (lx, ly)) @@ -481,7 +458,6 @@ class mstr_layergen: # Layer complete mstr_msg("layergen", "Layer image completed.") - # Let's try our hand at pseudo shadows if mstr_shadow_enabled == True: if mstr_shadow_shift >= 2: @@ -713,7 +689,10 @@ class mstr_layergen: ptc_src = [] for p in range(0, len(res)): - ptc_src.append(Image.open(mstr_datafolder + "textures/" + self._tag + "/" + self._value + "/" + str(res[p]) + ".png")) + ptc_img = Image.open(mstr_datafolder + "textures/" + self._tag + "/" + self._value + "/" + str(res[p]) + ".png") + if self._zoomlevel == 16: + ptc_img = ptc_img.resize((250,250), resample=Image.Resampling.BILINEAR) + ptc_src.append(ptc_img) # Set some contrast lyr_contrast = 0.85 @@ -724,7 +703,9 @@ class mstr_layergen: #brd_src = bldg_src[1] # Begin producing a largely random image - samples = 250 # <- We need this in a moment + samples = 0 + if self._zoomlevel == 18: samples = 250 + if self._zoomlevel == 16: samples = 2000 # <- We need this in a moment for i in range(samples): imgid = 0 if len(ptc_src) == 1: imgid = 0 @@ -770,7 +751,9 @@ class mstr_layergen: shadow = Image.open(fn) # Add some random trees - for t in range(0, 5000): + treeamt = 5000 + if self._zoomlevel == 18: treeamt = 35000 + for t in range(0, treeamt+1): loc_x = randrange(0, self._imgsize) loc_y = randrange(0, self._imgsize) shf_val = 21 @@ -868,4 +851,4 @@ class mstr_layergen: if abs(numbers[1]) >= 10 and numbers[0] <= 99: fstr = fstr + "0" + str(numbers[1]) if abs(numbers[1]) >= 100 : fstr = fstr + str(numbers[1]) - return fstr + return fstr() diff --git a/og.py b/og.py index 53a5186..ce483ba 100644 --- a/og.py +++ b/og.py @@ -46,36 +46,38 @@ cli = False pbf = False prep = False -if len(sys.argv) == 4: - cli = True +if __name__ == '__main__': -# Only if we find enough arguments, proceed. -if cli: - lat = int(sys.argv[1]) - lng = int(sys.argv[2]) + if len(sys.argv) == 4: + cli = True - mstr_msg("_main", "Beginning tile generation process.") + # Only if we find enough arguments, proceed. + if cli: + lat = int(sys.argv[1]) + lng = int(sys.argv[2]) - # Create the class and init values - og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd(), prep) + mstr_msg("_main", "Beginning tile generation process.") - # Prepare a tile - if sys.argv[3] == "prepare": - og._prepareTile() + # Create the class and init values + og = mstr_orthographic(lat, lng, mstr_datafolder, os.getcwd(), prep) - # Generate orthos - if sys.argv[3] != "prepare" and sys.argv[3] != "xpscenery": - og._generateOrthos_mt(int(sys.argv[3])) + # Prepare a tile + if sys.argv[3] == "prepare": + og._prepareTile() - # Build the terrain mesh and assign ground textures - if sys.argv[3] == "xpscenery": - og.generate_xp_scenery() + # Generate orthos + if sys.argv[3] != "prepare" and sys.argv[3] != "xpscenery": + og._generateOrthos_mt(int(sys.argv[3])) + + # Build the terrain mesh and assign ground textures + if sys.argv[3] == "xpscenery": + og.generate_xp_scenery() -if cli == False and pbf == False: - mstr_msg("_main", "Please provide Latitude and Longitude. Exiting.") - print ("") + if cli == False and pbf == False: + mstr_msg("_main", "Please provide Latitude and Longitude. Exiting.") + print ("") diff --git a/orthographic.py b/orthographic.py index 5c29ebd..f2dda2c 100644 --- a/orthographic.py +++ b/orthographic.py @@ -32,6 +32,7 @@ class mstr_orthographic: # Constructor of class. Takes longitude and latitude. def __init__(self, lat, lng, outfolder, pwd, prep=False): self._zoomlevel = mstr_zl_16 + self._zoomlevelfactor = 16 self._lat = lat self._long = lng self._output = outfolder @@ -152,26 +153,103 @@ class mstr_orthographic: top_lat = cur_tile_y + # Creste necessary folders + def _createFolders(self): + # Create the _cache folder, should it not exist. + # Temporary images for the ortho tile generation go here + if not os.path.exists(self._output + "/_cache"): + os.makedirs(self._output + "/_cache") + mstr_msg("orthographic", "Created _cache folder.") + + # Generate the Tiles/lat-lng folder for the finished tile + if not os.path.exists(self._output + "/z_orthographic"): + os.makedirs(self._output + "/z_orthographic") + mstr_msg("orthographic", "Created z_orthographic folder") + + # Generate the orthos folder + if not os.path.exists(self._output + "/z_orthographic/orthos"): + os.makedirs(self._output + "/z_orthographic/orthos") + mstr_msg("orthographic", "Created tile orthos folder") + if not os.path.exists(self._output + "/z_orthographic/orthos" + self._latlngfld): + os.makedirs(self._output + "/z_orthographic/orthos/" + self._latlngfld, exist_ok=True) + + # Generate the database folder + if not os.path.exists(self._output + "/z_orthographic/data"): + os.makedirs(self._output + "/z_orthographic/data") + mstr_msg("orthographic", "Created tile database folder") + if not os.path.exists(self._output + "/z_orthographic/data/" + self._latlngfld): + os.makedirs(self._output + "/z_orthographic/data/" + self._latlngfld) + if not os.path.exists(self._output + "/z_orthographic/data/" + self._latlngfld + "/osm"): + os.makedirs(self._output + "/z_orthographic/data/" + self._latlngfld + "/osm") + if not os.path.exists(self._output + "/z_orthographic/data/" + self._latlngfld + "/perlin"): + os.makedirs(self._output + "/z_orthographic/data/" + self._latlngfld + "/perlin") + + # X-Plane specific + if mstr_xp_genscenery == True: + btnum = self.find_earthnavdata_number() + btstr = self.latlng_folder(btnum) + if not os.path.exists(self._output + "/z_orthographic/terrain"): + os.makedirs(self._output + "/z_orthographic/terrain") + mstr_msg("orthographic", "[X-Plane] Created terrain files folder") + if not os.path.exists(self._output + "/z_orthographic/terrain/" + self._latlngfld): + os.makedirs(self._output + "/z_orthographic/terrain/" + self._latlngfld) + if not os.path.exists(self._output + "/z_orthographic/Earth nav data"): + os.makedirs(self._output + "/z_orthographic/Earth nav data") + mstr_msg("orthographic", "[X-Plane] Created Earth nav folder") + if not os.path.exists(self._output + "/z_orthographic/Earth nav data/" + btstr): + os.makedirs(self._output + "/z_orthographic/Earth nav data/" + btstr) + if mstr_xp_scn_normalmaps == True: + if not os.path.exists(self._output + "/z_orthographic/normals"): + os.makedirs(self._output + "/z_orthographic/normals") + mstr_msg("orthographic", "[X-Plane] created tile normal maps folder") + if not os.path.exists(self._output + "/z_orthographic/normals/" + self._latlngfld): + os.makedirs(self._output + "/z_orthographic/normals/" + self._latlngfld) + + # Start the multi-threaded build of all orthos # amtsmt = AmountSimultaneous - so how many orthos you want to # generate at the same time. You may need to fine tune this value # so that you don't overload your machine. def _generateOrthos_mt(self, amtsmt): - # Need to know maximum values first + + # Step 1 + # Determine values: + # We need to know the highest possible latitude and longitude tile numbers, + # in case we render at the edge bb_lat = self._lat bb_lng = self._long bb_lat_edge = self._lat+self._vstep bb_lng_edge = self._long+self._zoomlevel + cur_tile_x = 1 + cur_tile_y = 1 mlat = 1 mlng = 1 while bb_lat < self._lat + 1: bb_lat = bb_lat + self._vstep + if bb_lat >= self._lat + 1: + bb_lat = bb_lat - self._zoomlevel + break mlat = mlat+1 while bb_lng < self._long + 1: bb_lng = bb_lng + self._zoomlevel + if bb_lng >= self._long + 1: + bb_lng = bb_lng - self._zoomlevel + break mlng = mlng+1 mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng)) - maxlatlng = [ mlat, mlng ] + + # Step 2 + # Create folders and generate all perlin noise maps + self._createFolders() + """ + for l in mstr_ortho_layers: + if l[0] != "highway" and l[0] != "building": + mstr_important_msg("orthographic", "Generating Perlin map for " + l[0] + ":" + l[1]) + prln = mstr_perlin(l[0], l[1], mlat, mlng, 16) + pmap = prln._generatePerlinMap() + fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/perlin/" + l[0] + "_" + l[1] + ".png" + pmap.save(fn) + """ # For completion layers numbers = list(range(1, 16)) @@ -224,6 +302,7 @@ class mstr_orthographic: bb_lng_edge = self._long + ((grid_lng-1)*self._zoomlevel) + self._zoomlevel osmxml = mstr_osmxml() + osmxml.setLatLngFld(self._latlngfld) osmxml.adjust_bbox(bb_lat, bb_lng, bb_lat_edge, bb_lng_edge) osmxml.acquire_osm(grid_lat, grid_lng) @@ -263,7 +342,8 @@ class mstr_orthographic: lg = mstr_layergen(layer[0], layer[1], self._lat, grid_lat, self._long, grid_lng, layer[2]) lg.set_max_latlng_tile(maxlatlng) lg.set_latlng_folder(self._latlngfld) - lg.open_tile_info() + lg.set_zoomlevel(self._zoomlevelfactor) + #lg.open_tile_info() lyr = lg.genlayer(mask, osmxml) photolayers.append(lyr) if (layer[0] == "natural" and layer[1] == "water") or (layer[0] == "water" and layer[1] == "lake") or (layer[0] == "water" and layer[1] == "pond") or (layer[0] == "water" and layer[1] == "river") or (layer[0] == "waterway" and layer[1] == "river"): @@ -281,6 +361,7 @@ class mstr_orthographic: mstr_msg("orthographic", "Generating ortho photo") pg = mstr_photogen(self._lat, self._long, grid_lat, grid_lng, maxlatlng[0], maxlatlng[1]) pg.setLayerNames(layers) + pg.setZoomLevel(self._zoomlevelfactor) pg.genphoto(photolayers, waterlayers, cpl) mstr_important_msg("orthographic", "Ortho photo " + str(grid_lat)+"_"+str(grid_lng)+" generated!") @@ -497,7 +578,7 @@ class mstr_orthographic: mstr_important_msg("orthographic", "Generating Perlin map for " + l[0] + ":" + l[1]) prln = mstr_perlin(l[0], l[1], mlat, mlng, 16) pmap = prln._generatePerlinMap() - fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/perlin/perlin_" + l[0] + "_" + l[1] + ".png" + fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/perlin/" + l[0] + "_" + l[1] + ".png" pmap.save(fn) diff --git a/perlin.py b/perlin.py index a141f88..799a953 100644 --- a/perlin.py +++ b/perlin.py @@ -90,7 +90,7 @@ class mstr_perlin: # Find base color depending on tag/value def _findBaseColor(self): idx = -1 - for b in range(len(self._basecolors)): + for b in range(len(self._bc)): if self._bc[b][0] == self._tag and self._bc[b][1] == self._value: idx = b break diff --git a/photogen.py b/photogen.py index 5b3f573..df32899 100644 --- a/photogen.py +++ b/photogen.py @@ -45,6 +45,11 @@ class mstr_photogen: def setLayerNames(self, names): self._lyrnames = names + + # Set zoomlevel + def setZoomLevel(self, zl): + self._zoomlevel = zl + # This puts it all together. Bonus: AND saves it. def genphoto(self, layers, waterlayers, cpl): @@ -205,6 +210,8 @@ class mstr_photogen: #self._tile = ImageEnhance.Contrast(self._tile).enhance(0.1) # This we can save accordingly. + self._tile.show() + exit() self._tile.save(mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + ".png") # Now we convert this into a DDS @@ -324,6 +331,9 @@ class mstr_photogen: nc = (tp[0] - diff, tp[1] - diff, tp[2] - diff, tp[3]) treepx[x, y] = nc + if self._zoomlevel == 16: + tree = tree.resize((int(tree.width/4), int(tree.height/4)), resample=Image.Resampling.BILINEAR) + return tree @@ -370,7 +380,8 @@ class mstr_photogen: lyr[0] == "leisure" and lyr[1] == "park"): trees = Image.new("RGBA", (self._imgsize, self._imgsize)) amt = 4000 - if lyr[1] == "cemetery": amt = 20000 + if self._zoomlevel == 16: amt = 60000 + if lyr[1] == "cemetery": amt = 60000 lyrmask = layers[curlyr].load() # We can use the layer image as alpha mask for i in range(1, amt + 1): lx = randrange(0, self._imgsize)