Code for Zoom Level 16, and Perlin-map based ortho generation. Not entirely complete, but initial test working correctly. Liners for some elements need corrections.

This commit is contained in:
Marcus Str. 2025-01-26 21:57:50 +01:00
parent c584aa0838
commit 05265e8136
8 changed files with 781 additions and 197 deletions

61
colorizer.py Normal file
View File

@ -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

View File

@ -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),

446
defines_zl18.py Normal file
View File

@ -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)
]
)
]

View File

@ -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()

44
og.py
View File

@ -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 ("")

View File

@ -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)

View File

@ -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

View File

@ -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)