Added textures. Changes to shadow generation. Refinements to layers and blurs. Made sure roads appear correctly. Removed streams for inland cutting as those are barely visible on actual satellite images. Proper testing to be done, close to public release.
35
defines.py
@ -22,7 +22,7 @@ mstr_datafolder = "/home/marcus/Data/Developer/Projects/orthographic/"
|
|||||||
mstr_osm_endpoint = "https://marstr.online/osm/v1/"
|
mstr_osm_endpoint = "https://marstr.online/osm/v1/"
|
||||||
|
|
||||||
# Define the texture resolution you want to have your photos at.
|
# Define the texture resolution you want to have your photos at.
|
||||||
mstr_photores = 2048 # <- Change this to 4096 for 16k resolution
|
mstr_photores = 2048
|
||||||
|
|
||||||
# Radius of zoom level 18 aerials around airports with ICAO code
|
# Radius of zoom level 18 aerials around airports with ICAO code
|
||||||
# Value is in tiles - not in km
|
# Value is in tiles - not in km
|
||||||
@ -54,20 +54,13 @@ mstr_show_log = True
|
|||||||
# You can, however, disable the shadow rendering layer here.
|
# You can, however, disable the shadow rendering layer here.
|
||||||
mstr_shadow_enabled = True
|
mstr_shadow_enabled = True
|
||||||
mstr_shadow_strength = 0.65
|
mstr_shadow_strength = 0.65
|
||||||
mstr_shadow_shift = 15
|
mstr_shadow_shift = 16
|
||||||
|
mstr_shadow_floor_h = 2.8 # 2.5m ceiling height + 30cm concrete per floor
|
||||||
# The tags that cast shadows
|
# The tags that cast shadows
|
||||||
mstr_shadow_casters = [
|
mstr_shadow_casters = [
|
||||||
("landuse", "forest"),
|
("landuse", "forest"),
|
||||||
("leisure", "nature_reserve"),
|
("leisure", "nature_reserve"),
|
||||||
("natural", "wood"),
|
("natural", "wood")
|
||||||
("natural", "tree_row"),
|
|
||||||
("building", "semidetached_house"),
|
|
||||||
("building", "apartments"),
|
|
||||||
("building", "garage"),
|
|
||||||
("building", "office"),
|
|
||||||
("building", "retail"),
|
|
||||||
("building", "industrial"),
|
|
||||||
("building", "yes")
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -248,19 +241,19 @@ mstr_mask_blur = [
|
|||||||
("water", "river", 10),
|
("water", "river", 10),
|
||||||
("leisure", "swimming_pool", 10),
|
("leisure", "swimming_pool", 10),
|
||||||
("waterway", "river", 10),
|
("waterway", "river", 10),
|
||||||
("waterway", "stream", 2),
|
("waterway", "stream", 4),
|
||||||
("amenity", "parking", 1),
|
("amenity", "parking", 1),
|
||||||
("amenity", "school", 1),
|
("amenity", "school", 1),
|
||||||
("highway", "pedestrian", 12),
|
("highway", "pedestrian", 12),
|
||||||
# Z-Order 4
|
# Z-Order 4
|
||||||
("highway", "motorway", 2),
|
("highway", "motorway", 1),
|
||||||
("highway", "primary", 2),
|
("highway", "primary", 1),
|
||||||
("highway", "secondary", 2),
|
("highway", "secondary", 1),
|
||||||
("highway", "tertiary", 2),
|
("highway", "tertiary", 1),
|
||||||
("highway", "unclassified", 2),
|
("highway", "unclassified", 1),
|
||||||
("highway", "living_street", 2),
|
("highway", "living_street", 1),
|
||||||
("highway", "residential", 2),
|
("highway", "residential", 1),
|
||||||
("highway", "service", 2),
|
("highway", "service", 1),
|
||||||
("highway", "footway", 2),
|
("highway", "footway", 2),
|
||||||
("highway", "track", 2),
|
("highway", "track", 2),
|
||||||
("highway", "path", 2),
|
("highway", "path", 2),
|
||||||
@ -395,8 +388,6 @@ mstr_building_base_colors = [
|
|||||||
]
|
]
|
||||||
),
|
),
|
||||||
("yes", [
|
("yes", [
|
||||||
"#693333", "#592b2b", "#513434", "#4a1e1e", "#362626",
|
|
||||||
"#534136", "#4d3424", "#534b45", "#553724", "#574c45",
|
|
||||||
"#373942", "#40424a", "#363b4f", "#2c2d32", "#444651",
|
"#373942", "#40424a", "#363b4f", "#2c2d32", "#444651",
|
||||||
"#454545", "#39393b", "#4b4b4c", "#363638", "#525252"
|
"#454545", "#39393b", "#4b4b4c", "#363638", "#525252"
|
||||||
]
|
]
|
||||||
|
@ -86,3 +86,8 @@ def in_circle(center_x, center_y, radius, x, y):
|
|||||||
return square_dist <= radius ** 2
|
return square_dist <= radius ** 2
|
||||||
|
|
||||||
|
|
||||||
|
# Find meters per pixel. Requires width of tile in meters
|
||||||
|
# Needed for proper approximation of shadow length per building level
|
||||||
|
def meters_per_pixel(lngwidth):
|
||||||
|
mpx = lngwidth / mstr_photores
|
||||||
|
return mpx
|
||||||
|
159
layergen.py
@ -87,11 +87,13 @@ class mstr_layergen:
|
|||||||
brd_src = Image.open(root_folder + "/brd/b" + str(src) + ".png")
|
brd_src = Image.open(root_folder + "/brd/b" + str(src) + ".png")
|
||||||
ptc_src = []
|
ptc_src = []
|
||||||
for p in ptc:
|
for p in ptc:
|
||||||
ptc_src.append(Image.open(p))
|
pimg = Image.open(p)
|
||||||
|
pimg = pimg.rotate(randrange(0, 360), expand=True)
|
||||||
|
ptc_src.append(pimg)
|
||||||
mstr_msg("layergen", "Border sources selected")
|
mstr_msg("layergen", "Border sources selected")
|
||||||
|
|
||||||
# Begin producing a largely random image
|
# Begin producing a largely random image
|
||||||
samples = 250 # <- We need this in a moment
|
samples = 250 # <- We need this in a moment
|
||||||
for i in range(samples):
|
for i in range(samples):
|
||||||
imgid = 0
|
imgid = 0
|
||||||
if len(ptc_src) == 1: imgid = 0
|
if len(ptc_src) == 1: imgid = 0
|
||||||
@ -455,13 +457,28 @@ class mstr_layergen:
|
|||||||
|
|
||||||
# Add trees only in some features
|
# Add trees only in some features
|
||||||
if (self._tag == "landuse" and self._value == "cemetery") or (self._tag == "landuse" and self._value == "residential") or (self._tag == "leisure" and self._value == "park"):
|
if (self._tag == "landuse" and self._value == "cemetery") or (self._tag == "landuse" and self._value == "residential") or (self._tag == "leisure" and self._value == "park"):
|
||||||
|
trees = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
amt = 3500
|
amt = 3500
|
||||||
for i in range(1, amt+1):
|
for i in range(1, amt+1):
|
||||||
p = randrange(1, 11)
|
p = randrange(1, 16)
|
||||||
tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png")
|
tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png")
|
||||||
lx = randrange( self._imgsize - tree.width )
|
lx = randrange( self._imgsize - tree.width )
|
||||||
ly = randrange( self._imgsize - tree.height )
|
ly = randrange( self._imgsize - tree.height )
|
||||||
layer.alpha_composite(tree, (lx, ly))
|
trees.alpha_composite(tree, (lx, ly))
|
||||||
|
|
||||||
|
tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
|
tree_pix = trees.load()
|
||||||
|
shadow_pix = tree_shadow.load()
|
||||||
|
for y in range(self._imgsize):
|
||||||
|
for x in range(self._imgsize):
|
||||||
|
tp = tree_pix[x,y]
|
||||||
|
if tp[3] > 0:
|
||||||
|
sc = (0,0,0,180)
|
||||||
|
if x+8 < self._imgsize and y+5 < self._imgsize:
|
||||||
|
shadow_pix[x+8,y+5] = sc
|
||||||
|
tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2))
|
||||||
|
tree_shadow.alpha_composite(trees)
|
||||||
|
layer.alpha_composite(tree_shadow)
|
||||||
|
|
||||||
mstr_msg("layergen", "Layer image completed")
|
mstr_msg("layergen", "Layer image completed")
|
||||||
|
|
||||||
@ -527,9 +544,21 @@ class mstr_layergen:
|
|||||||
if self._tag == "landuse" and self._value == "forest":
|
if self._tag == "landuse" and self._value == "forest":
|
||||||
# The residential layer MUST exist before we reach the forest part.
|
# The residential layer MUST exist before we reach the forest part.
|
||||||
fn = mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_landuse-residential_layer.png"
|
fn = mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_landuse-residential_layer.png"
|
||||||
if os.path.isfile(fn) == True:
|
if os.path.isfile(fn):
|
||||||
rsd = Image.open(fn)
|
rsd = Image.open(fn)
|
||||||
layer_comp.alpha_composite(rsd)
|
rsd_pix = rsd.load()
|
||||||
|
forest_layer = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
|
for_pix = forest_layer.load()
|
||||||
|
for y in range(self._imgsize):
|
||||||
|
for x in range(self._imgsize):
|
||||||
|
rpix = rsd_pix[x,y]
|
||||||
|
lpix = layer_comp_pix[x,y]
|
||||||
|
if rpix[3] > 0 and lpix[3] > 0:
|
||||||
|
for_pix[x,y] = (rpix[0], rpix[1], rpix[2], rpix[3])
|
||||||
|
layer_comp.alpha_composite(forest_layer)
|
||||||
|
#if os.path.isfile(fn) == True:
|
||||||
|
# rsd = Image.open(fn)
|
||||||
|
# layer_comp.alpha_composite(rsd)
|
||||||
|
|
||||||
# Store layer
|
# Store layer
|
||||||
if self._is_completion == False:
|
if self._is_completion == False:
|
||||||
@ -563,18 +592,40 @@ class mstr_layergen:
|
|||||||
mstr_msg("layergen", "Generating shadow for layer")
|
mstr_msg("layergen", "Generating shadow for layer")
|
||||||
shadow_pix = shadow.load()
|
shadow_pix = shadow.load()
|
||||||
mask_pix = osm_mask.load()
|
mask_pix = osm_mask.load()
|
||||||
|
shf = 1
|
||||||
|
while shf < mstr_shadow_shift:
|
||||||
|
for y in range(self._imgsize):
|
||||||
|
for x in range(self._imgsize):
|
||||||
|
mp = layer_comp_pix[x,y]
|
||||||
|
if mp[3] == 255:
|
||||||
|
if x+(shf*2) < self._imgsize and y+shf < self._imgsize:
|
||||||
|
rndshd = randrange(5, 210)
|
||||||
|
shadow_pix[x+(shf*2), y+shf] = (0,0,0,rndshd)
|
||||||
|
shf = shf+1
|
||||||
|
|
||||||
|
# Tree removal
|
||||||
|
for y in range(self._imgsize):
|
||||||
|
for x in range(self._imgsize):
|
||||||
|
lp = layer_comp_pix[x,y]
|
||||||
|
if lp[3] >= 250:
|
||||||
|
shadow_pix[x,y] = (0,0,0,0)
|
||||||
|
|
||||||
|
shadow = shadow.filter(ImageFilter.GaussianBlur(radius=2))
|
||||||
|
|
||||||
|
"""
|
||||||
for y in range(self._imgsize-1):
|
for y in range(self._imgsize-1):
|
||||||
for x in range(self._imgsize-1):
|
for x in range(self._imgsize-1):
|
||||||
m = mask_pix[x,y]
|
m = mask_pix[x,y]
|
||||||
shf_x = 0
|
shf_x = 0
|
||||||
shf_x = x + mstr_shadow_shift
|
shf_x = x + mstr_shadow_shift
|
||||||
if shf_x <= self._imgsize-1:
|
if shf_x < self._imgsize:
|
||||||
a = mask_pix[x,y][3]
|
a = mask_pix[x,y][3]
|
||||||
st = 0
|
st = 0
|
||||||
st = random.uniform(0.45, mstr_shadow_strength)
|
st = random.uniform(0.45, mstr_shadow_strength)
|
||||||
ca = a * st
|
ca = a * st
|
||||||
aa = int(ca)
|
aa = int(ca)
|
||||||
shadow_pix[shf_x, y] = (0,0,0,aa)
|
shadow_pix[shf_x, y] = (0,0,0,aa)
|
||||||
|
"""
|
||||||
shadow.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer_shadow.png")
|
shadow.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer_shadow.png")
|
||||||
mstr_msg("layergen", "Shadow layer completed")
|
mstr_msg("layergen", "Shadow layer completed")
|
||||||
|
|
||||||
@ -643,7 +694,7 @@ class mstr_layergen:
|
|||||||
#self._tiledb.close_db()
|
#self._tiledb.close_db()
|
||||||
|
|
||||||
# Create a water mask we need to remove from the DDS later
|
# Create a water mask we need to remove from the DDS later
|
||||||
if (self._tag == "natural" and self._value == "water") or (self._tag == "water" and self._value == "lake") or (self._tag == "water" and self._value == "pond") or (self._tag == "water" and self._value == "river") or (self._tag == "leisure" and self._value == "swimming_pool") or (self._tag == "waterway" and self._value == "stream"):
|
if (self._tag == "natural" and self._value == "water") or (self._tag == "water" and self._value == "lake") or (self._tag == "water" and self._value == "pond") or (self._tag == "water" and self._value == "river") or (self._tag == "leisure" and self._value == "swimming_pool"):
|
||||||
mstr_msg("layergen", "Generating inland water mask")
|
mstr_msg("layergen", "Generating inland water mask")
|
||||||
inl_mask = Image.new("RGBA", (self._imgsize, self._imgsize), (0,0,0,0))
|
inl_mask = Image.new("RGBA", (self._imgsize, self._imgsize), (0,0,0,0))
|
||||||
lyr_pix = layer_comp.load()
|
lyr_pix = layer_comp.load()
|
||||||
@ -681,7 +732,6 @@ class mstr_layergen:
|
|||||||
if i[0] == self._tag and i[1] == self._value:
|
if i[0] == self._tag and i[1] == self._value:
|
||||||
osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2]))
|
osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2]))
|
||||||
break
|
break
|
||||||
osm_edge = osm_edge.filter(ImageFilter.BoxBlur(radius=1))
|
|
||||||
|
|
||||||
|
|
||||||
# And now for the Big Mac.
|
# And now for the Big Mac.
|
||||||
@ -712,14 +762,15 @@ class mstr_layergen:
|
|||||||
d = randrange(41, 61)
|
d = randrange(41, 61)
|
||||||
layer_comp_pix[x, y] = ( d,d,d,a[3] )
|
layer_comp_pix[x, y] = ( d,d,d,a[3] )
|
||||||
if self._tag == "highway" and self._value != "motorway":
|
if self._tag == "highway" and self._value != "motorway":
|
||||||
d = randrange(85, 101)
|
dr = randrange(110,121)
|
||||||
layer_comp_pix[x, y] = ( d,d,d,a[3] )
|
dg = randrange(110,121)
|
||||||
|
db = randrange(115,130)
|
||||||
|
layer_comp_pix[x, y] = ( dr,dg,db,a[3] )
|
||||||
if self._tag == "highway" and self._value == "motorway":
|
if self._tag == "highway" and self._value == "motorway":
|
||||||
d = randrange(1,20)
|
dr = randrange(77,89)
|
||||||
r = 86-d
|
dg = randrange(88,96)
|
||||||
g = 97-d
|
db = randrange(90,101)
|
||||||
b = 106-d
|
layer_comp_pix[x, y] = ( dr,dg,db,a[3] )
|
||||||
layer_comp_pix[x, y] = ( r,g,b,a[3] )
|
|
||||||
if self._tag == "waterway" and (self._value == "stream" or self._value == "river"):
|
if self._tag == "waterway" and (self._value == "stream" or self._value == "river"):
|
||||||
d = randrange(1, 15)
|
d = randrange(1, 15)
|
||||||
# Rock, grass, water
|
# Rock, grass, water
|
||||||
@ -730,7 +781,7 @@ class mstr_layergen:
|
|||||||
t = a[3]-d
|
t = a[3]-d
|
||||||
if t < 0: t = 0
|
if t < 0: t = 0
|
||||||
if e[3] > 0:
|
if e[3] > 0:
|
||||||
layer_comp_pix[x, y] = ( mats[pick-1][0], mats[pick-1][1], mats[pick-1][2], t )
|
layer_comp_pix[x, y] = ( mats[pick-1][0], mats[pick-1][1], mats[pick-1][2], 35 )
|
||||||
|
|
||||||
# A bit special here
|
# A bit special here
|
||||||
if self._tag == "building":
|
if self._tag == "building":
|
||||||
@ -757,15 +808,31 @@ class mstr_layergen:
|
|||||||
|
|
||||||
# A bit different for tree rows
|
# A bit different for tree rows
|
||||||
if self._tag == "natural" and self._value == "tree_row":
|
if self._tag == "natural" and self._value == "tree_row":
|
||||||
|
trees = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
for t in range(20001):
|
for t in range(20001):
|
||||||
lx = randrange(self._imgsize)
|
lx = randrange(self._imgsize)
|
||||||
ly = randrange(self._imgsize)
|
ly = randrange(self._imgsize)
|
||||||
a = mask_pix[lx,ly]
|
a = mask_pix[lx,ly]
|
||||||
if a[3] > 0:
|
if a[3] > 0:
|
||||||
if lx < self._imgsize and ly < self._imgsize:
|
if lx < self._imgsize and ly < self._imgsize:
|
||||||
p = randrange(1,11)
|
p = randrange(1,16)
|
||||||
tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png")
|
tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png")
|
||||||
layer_comp.alpha_composite(tree, (lx, ly))
|
trees.alpha_composite(tree, (lx, ly))
|
||||||
|
if mstr_shadow_enabled == True:
|
||||||
|
tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
|
tree_pix = trees.load()
|
||||||
|
shadow_pix = tree_shadow.load()
|
||||||
|
for y in range(self._imgsize):
|
||||||
|
for x in range(self._imgsize):
|
||||||
|
tp = tree_pix[x,y]
|
||||||
|
if tp[3] > 0:
|
||||||
|
rndshd = randrange(5, 210)
|
||||||
|
sc = (0,0,0,rndshd)
|
||||||
|
if x+8 < self._imgsize and y+5 < self._imgsize:
|
||||||
|
shadow_pix[x+8,y+5] = sc
|
||||||
|
tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2))
|
||||||
|
tree_shadow.alpha_composite(trees)
|
||||||
|
layer_comp.alpha_composite(tree_shadow)
|
||||||
|
|
||||||
# We will do some super magic here to let houses look more realistic
|
# We will do some super magic here to let houses look more realistic
|
||||||
if self._tag == "building":
|
if self._tag == "building":
|
||||||
@ -815,7 +882,7 @@ class mstr_layergen:
|
|||||||
numtrees = randrange(1, 16)
|
numtrees = randrange(1, 16)
|
||||||
for i in range(1, numtrees+1):
|
for i in range(1, numtrees+1):
|
||||||
# Pick some file
|
# Pick some file
|
||||||
pick = str(randrange(1, 11))
|
pick = str(randrange(1, 16))
|
||||||
tree = Image.open(mstr_datafolder + "textures/building/area/p" + pick + ".png")
|
tree = Image.open(mstr_datafolder + "textures/building/area/p" + pick + ".png")
|
||||||
# Do a correction for the location if needed
|
# Do a correction for the location if needed
|
||||||
if shf_x < 1: shf_x = 1
|
if shf_x < 1: shf_x = 1
|
||||||
@ -823,33 +890,39 @@ class mstr_layergen:
|
|||||||
if shf_x > self._imgsize - tree.width: shf_x = self._imgsize - tree.width - 1
|
if shf_x > self._imgsize - tree.width: shf_x = self._imgsize - tree.width - 1
|
||||||
if shf_y > self._imgsize - tree.height: shf_y = self._imgsize - tree.height - 1
|
if shf_y > self._imgsize - tree.height: shf_y = self._imgsize - tree.height - 1
|
||||||
trees.alpha_composite(tree, (shf_x, shf_y))
|
trees.alpha_composite(tree, (shf_x, shf_y))
|
||||||
trees.alpha_composite(layer_comp)
|
|
||||||
layer_comp = trees
|
|
||||||
|
|
||||||
|
if mstr_shadow_enabled == True:
|
||||||
|
tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
|
tree_pix = trees.load()
|
||||||
|
shadow_pix = tree_shadow.load()
|
||||||
|
for y in range(self._imgsize):
|
||||||
|
for x in range(self._imgsize):
|
||||||
|
tp = tree_pix[x,y]
|
||||||
|
if tp[3] > 0:
|
||||||
|
sc = (0,0,0,180)
|
||||||
|
if x+8 < self._imgsize and y+5 < self._imgsize:
|
||||||
|
shadow_pix[x+8,y+5] = sc
|
||||||
|
tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2))
|
||||||
|
tree_shadow.alpha_composite(trees)
|
||||||
|
|
||||||
|
# Save this separately, so that we can blur buildings, but not the trees
|
||||||
|
fn = mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer_building_trees.png"
|
||||||
|
|
||||||
|
if os.path.isfile(fn) == True:
|
||||||
|
extrees = Image.open(fn)
|
||||||
|
extrees.alpha_composite(tree_shadow)
|
||||||
|
extrees.save(fn)
|
||||||
|
else:
|
||||||
|
tree_shadow.save(fn)
|
||||||
|
|
||||||
|
#layer_comp.alpha_composite(tree_shadow)
|
||||||
|
#tree_shadow.alpha_composite(layer_comp)
|
||||||
|
#layer_comp = tree_shadow
|
||||||
|
|
||||||
mstr_msg("layergen", "Layer image generated")
|
mstr_msg("layergen", "Layer image generated")
|
||||||
|
|
||||||
# Building shadow
|
# Building shadow
|
||||||
if mstr_shadow_enabled == True:
|
if mstr_shadow_enabled == True:
|
||||||
if self._tag == "building":
|
|
||||||
mstr_msg("layergen", "Generating shadow for layer")
|
|
||||||
shadow = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
||||||
shadow_pix = shadow.load()
|
|
||||||
mask_pix = osm_mask.load()
|
|
||||||
for y in range(self._imgsize-1):
|
|
||||||
for x in range(self._imgsize-1):
|
|
||||||
m = mask_pix[x,y]
|
|
||||||
shf_x = x + mstr_shadow_shift
|
|
||||||
shf_y = y + (mstr_shadow_shift/2)
|
|
||||||
if shf_x < self._imgsize and shf_y < self._imgsize:
|
|
||||||
a = mask_pix[x,y][3]
|
|
||||||
st = random.uniform(0.3, mstr_shadow_strength)
|
|
||||||
ca = a * st
|
|
||||||
aa = int(ca)
|
|
||||||
shadow_pix[shf_x, shf_y] = (0,0,0,aa)
|
|
||||||
shadow = shadow.filter(ImageFilter.GaussianBlur(radius=2))
|
|
||||||
shadow.save(mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer_shadow.png")
|
|
||||||
mstr_msg("layergen", "Shadow layer completed")
|
|
||||||
|
|
||||||
# Some funnies with shadows
|
# Some funnies with shadows
|
||||||
if self._tag == "building" and (self._value == "detached" or self._value == "semidetached_house" or self._value == "apartments" or self._value == "civic" or self._value == "house" or self._value == "terrace"):
|
if self._tag == "building" and (self._value == "detached" or self._value == "semidetached_house" or self._value == "apartments" or self._value == "civic" or self._value == "house" or self._value == "terrace"):
|
||||||
@ -890,6 +963,7 @@ class mstr_layergen:
|
|||||||
# Highways and runways of any kind get some special treatment
|
# Highways and runways of any kind get some special treatment
|
||||||
if (self._tag == "highway" and self._value == "motorway") or (self._tag == "highway" and self._value == "primary") or (self._tag == "highway" and self._value == "secondary") or (self._tag == "highway" and self._value == "tertiary") or (self._tag == "aeroway" and self._value == "runway"):
|
if (self._tag == "highway" and self._value == "motorway") or (self._tag == "highway" and self._value == "primary") or (self._tag == "highway" and self._value == "secondary") or (self._tag == "highway" and self._value == "tertiary") or (self._tag == "aeroway" and self._value == "runway"):
|
||||||
# We will now add some white lines for coolness
|
# We will now add some white lines for coolness
|
||||||
|
osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)
|
||||||
mask_pix = osm_edge.load()
|
mask_pix = osm_edge.load()
|
||||||
layer_comp_pix = layer_comp.load()
|
layer_comp_pix = layer_comp.load()
|
||||||
for y in range(self._imgsize):
|
for y in range(self._imgsize):
|
||||||
@ -901,13 +975,14 @@ class mstr_layergen:
|
|||||||
layer_comp_pix[x, y] = ( w,w,w,a[3] )
|
layer_comp_pix[x, y] = ( w,w,w,a[3] )
|
||||||
|
|
||||||
if self._tag == "highway" and self._value == "residential":
|
if self._tag == "highway" and self._value == "residential":
|
||||||
|
osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)
|
||||||
mask_pix = osm_edge.load()
|
mask_pix = osm_edge.load()
|
||||||
layer_comp_pix = layer_comp.load()
|
layer_comp_pix = layer_comp.load()
|
||||||
for y in range(self._imgsize):
|
for y in range(self._imgsize):
|
||||||
for x in range(self._imgsize):
|
for x in range(self._imgsize):
|
||||||
if mask_pix[x, y][3] > 0:
|
if mask_pix[x, y][3] > 0:
|
||||||
# Find a suitable color
|
# Find a suitable color
|
||||||
w = randrange(60, 96)
|
w = randrange(150,181)
|
||||||
a=mask_pix[x,y]
|
a=mask_pix[x,y]
|
||||||
layer_comp_pix[x, y] = ( w,w,w,a[3] )
|
layer_comp_pix[x, y] = ( w,w,w,a[3] )
|
||||||
mstr_msg("layergen", "Street lines added")
|
mstr_msg("layergen", "Street lines added")
|
||||||
|
72
maskgen.py
@ -27,6 +27,7 @@ from defines import *
|
|||||||
from log import *
|
from log import *
|
||||||
from PIL import Image, ImageFilter, ImageDraw, ImagePath
|
from PIL import Image, ImageFilter, ImageDraw, ImagePath
|
||||||
from random import randrange
|
from random import randrange
|
||||||
|
from functions import *
|
||||||
import random
|
import random
|
||||||
|
|
||||||
class mstr_maskgen:
|
class mstr_maskgen:
|
||||||
@ -67,9 +68,16 @@ class mstr_maskgen:
|
|||||||
return latlng
|
return latlng
|
||||||
|
|
||||||
|
|
||||||
# Only needed if X-Plane scenery is built
|
# Set width of tile - for buildings
|
||||||
def _set_xpscenery_datagroup(self, dg):
|
def set_tile_width(self, tile_width):
|
||||||
self._xpdg = dg
|
self._tile_width = tile_width
|
||||||
|
|
||||||
|
# Numbers needed for the possible building shadow layer
|
||||||
|
def set_latlng_numbers(self, lat, tv, lng, th):
|
||||||
|
self._latitude = lat
|
||||||
|
self._lat_number = tv
|
||||||
|
self._longitude = lng
|
||||||
|
self._lng_number = th
|
||||||
|
|
||||||
|
|
||||||
# Builds the required mask
|
# Builds the required mask
|
||||||
@ -100,6 +108,9 @@ class mstr_maskgen:
|
|||||||
bbox.append(self._box[2] + ((self._box[3]-1) * mstr_zl_18))
|
bbox.append(self._box[2] + ((self._box[3]-1) * mstr_zl_18))
|
||||||
bbox.append(self._box[2] + ((self._box[3]-1) * mstr_zl_18) + mstr_zl_18)
|
bbox.append(self._box[2] + ((self._box[3]-1) * mstr_zl_18) + mstr_zl_18)
|
||||||
|
|
||||||
|
# Building levels, if this is a building
|
||||||
|
bld_levels = 0
|
||||||
|
|
||||||
# Generate mask for ONE tag only
|
# Generate mask for ONE tag only
|
||||||
if self._subtag == None:
|
if self._subtag == None:
|
||||||
for w in way:
|
for w in way:
|
||||||
@ -107,6 +118,8 @@ class mstr_maskgen:
|
|||||||
nd = []
|
nd = []
|
||||||
for d in way:
|
for d in way:
|
||||||
if d[0] == w[0]:
|
if d[0] == w[0]:
|
||||||
|
if self._tag == "building" and bld_levels == 0:
|
||||||
|
bld_levels = xml.find_building_levels(tilexml, w[0])
|
||||||
nd.append(d[1])
|
nd.append(d[1])
|
||||||
frs.append(nd)
|
frs.append(nd)
|
||||||
# Scout through relations as these also make up map data
|
# Scout through relations as these also make up map data
|
||||||
@ -203,5 +216,58 @@ class mstr_maskgen:
|
|||||||
|
|
||||||
# Save image
|
# Save image
|
||||||
mask_img.save(mstr_datafolder + "_cache/" + fstr + "_" + self._tag + "-" + self._value + ".png")
|
mask_img.save(mstr_datafolder + "_cache/" + fstr + "_" + self._tag + "-" + self._value + ".png")
|
||||||
|
|
||||||
|
# If this is a building, we need to render the shadow here, as we only know the height
|
||||||
|
# of the building in this loop.
|
||||||
|
if mstr_shadow_enabled == True:
|
||||||
|
if self._tag == "building":
|
||||||
|
mpp = meters_per_pixel(self._tile_width) * mstr_zl_18
|
||||||
|
pix_per_floor = mstr_shadow_floor_h / mpp
|
||||||
|
total_pix = pix_per_floor * bld_levels
|
||||||
|
shift = int(total_pix)
|
||||||
|
|
||||||
|
fn = mstr_datafolder + "_cache/" + fstr + "_" + self._tag + "-" + self._value + "_layer_shadow.png"
|
||||||
|
|
||||||
|
mask_pix = mask_img.load()
|
||||||
|
|
||||||
|
bld_shadow = Image.new("RGBA", (mstr_photores, mstr_photores))
|
||||||
|
bld_shadow_pix = bld_shadow.load()
|
||||||
|
|
||||||
|
# Shadow sweep
|
||||||
|
shf = 1
|
||||||
|
while shf <= shift:
|
||||||
|
for y in range(mstr_photores):
|
||||||
|
for x in range(mstr_photores):
|
||||||
|
mp = mask_pix[x,y]
|
||||||
|
if mp[3] != 0:
|
||||||
|
if x+(shf*2) < mstr_photores and y+shf < mstr_photores:
|
||||||
|
bld_shadow_pix[x+(shf*2), y+shf] = (0,0,0,255)
|
||||||
|
shf = shf+1
|
||||||
|
|
||||||
|
# Building removal sweep
|
||||||
|
for y in range(mstr_photores):
|
||||||
|
for x in range(mstr_photores):
|
||||||
|
mp = mask_pix[x,y]
|
||||||
|
if mp[3] != 0:
|
||||||
|
bld_shadow_pix[x,y] = (0,0,0,0)
|
||||||
|
|
||||||
|
|
||||||
|
# Correct alpha
|
||||||
|
bld_shadow_pix = bld_shadow.load()
|
||||||
|
for y in range(mstr_photores):
|
||||||
|
for x in range(mstr_photores):
|
||||||
|
sp = bld_shadow_pix[x,y]
|
||||||
|
if sp[3] != 0:
|
||||||
|
bld_shadow_pix[x,y] = (0,0,0,120)
|
||||||
|
|
||||||
|
# Store
|
||||||
|
if os.path.isfile(fn) == True:
|
||||||
|
lyr = Image.open(fn)
|
||||||
|
lyr.alpha_composite(bld_shadow)
|
||||||
|
lyr.save(fn)
|
||||||
|
else:
|
||||||
|
bld_shadow.save(fn)
|
||||||
|
|
||||||
|
|
||||||
# Inform
|
# Inform
|
||||||
mstr_msg("maskgen", "Mask built.")
|
mstr_msg("maskgen", "Mask built.")
|
||||||
|
@ -201,8 +201,9 @@ class mstr_orthographic:
|
|||||||
|
|
||||||
# Generate the mask
|
# Generate the mask
|
||||||
mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2])
|
mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2])
|
||||||
if mstr_xp_genscenery == True:
|
if layer[0] == "building":
|
||||||
mg._set_xpscenery_datagroup(xp_datagroup)
|
mg.set_tile_width(self._findWidthOfLongitude(bb_lat))
|
||||||
|
mg.set_latlng_numbers(self._lat, lat_grid, self._long, lng_grid)
|
||||||
mg._build_mask()
|
mg._build_mask()
|
||||||
|
|
||||||
# Generate the layer
|
# Generate the layer
|
||||||
|
17
osmxml.py
@ -151,6 +151,23 @@ class mstr_osmxml:
|
|||||||
return surface
|
return surface
|
||||||
|
|
||||||
|
|
||||||
|
# Find the levels of a building (for shadow rendering)
|
||||||
|
def find_building_levels(self, xmlfile, way_id):
|
||||||
|
lvl = 3 # Standard if we don't find anything else
|
||||||
|
xml_doc = xml.dom.minidom.parse(xmlfile)
|
||||||
|
wpdata = xml_doc.getElementsByTagName("way")
|
||||||
|
for wp in wpdata:
|
||||||
|
if wp.getAttribute("id") == way_id:
|
||||||
|
tags = wp.getElementsByTagName("tag")
|
||||||
|
for tag in tags:
|
||||||
|
a = tag.getAttribute("k")
|
||||||
|
v = tag.getAttribute("v")
|
||||||
|
if a == "building:levels":
|
||||||
|
lvl = int(v)
|
||||||
|
break
|
||||||
|
return lvl
|
||||||
|
|
||||||
|
|
||||||
# It turns out that some features hide themselves in the relations section.
|
# It turns out that some features hide themselves in the relations section.
|
||||||
# I figured this out during testing, and almost going insane over the
|
# I figured this out during testing, and almost going insane over the
|
||||||
# question as to why some parts like forests are missing in the masks, while
|
# question as to why some parts like forests are missing in the masks, while
|
||||||
|
12
photogen.py
@ -52,6 +52,7 @@ class mstr_photogen:
|
|||||||
if l[0] == "building":
|
if l[0] == "building":
|
||||||
if os.path.isfile(root_filename + l[0] + "-" + l[1] + "_layer_shadow.png"):
|
if os.path.isfile(root_filename + l[0] + "-" + l[1] + "_layer_shadow.png"):
|
||||||
shd = Image.open(root_filename + l[0] + "-" + l[1] + "_layer_shadow.png")
|
shd = Image.open(root_filename + l[0] + "-" + l[1] + "_layer_shadow.png")
|
||||||
|
shd = shd.filter(ImageFilter.GaussianBlur(radius=2))
|
||||||
bldg_shadow.alpha_composite(shd)
|
bldg_shadow.alpha_composite(shd)
|
||||||
# Details merging
|
# Details merging
|
||||||
bldg_details = Image.new("RGBA", (self._imgsize, self._imgsize))
|
bldg_details = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
@ -69,10 +70,18 @@ class mstr_photogen:
|
|||||||
bld = Image.open(root_filename + l[0] + "-" + l[1] + "_layer.png")
|
bld = Image.open(root_filename + l[0] + "-" + l[1] + "_layer.png")
|
||||||
bld = bld.filter(ImageFilter.GaussianBlur(radius=1))
|
bld = bld.filter(ImageFilter.GaussianBlur(radius=1))
|
||||||
bldg_main.alpha_composite(bld)
|
bldg_main.alpha_composite(bld)
|
||||||
|
# Trees merging
|
||||||
|
tree_main = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
|
for l in mstr_ortho_layers:
|
||||||
|
if l[0] == "building":
|
||||||
|
if os.path.isfile(root_filename + l[0] + "-" + l[1] + "_layer_building_trees.png"):
|
||||||
|
trs= Image.open(root_filename + l[0] + "-" + l[1] + "_layer_building_trees.png")
|
||||||
|
tree_main.alpha_composite(trs)
|
||||||
# Merge the building layers
|
# Merge the building layers
|
||||||
bldg_final = Image.new("RGBA", (self._imgsize, self._imgsize))
|
bldg_final = Image.new("RGBA", (self._imgsize, self._imgsize))
|
||||||
bldg_final.alpha_composite(bldg_details)
|
bldg_final.alpha_composite(bldg_details)
|
||||||
bldg_final.alpha_composite(bldg_shadow)
|
bldg_final.alpha_composite(bldg_shadow)
|
||||||
|
bldg_final.alpha_composite(tree_main)
|
||||||
bldg_final.alpha_composite(bldg_main)
|
bldg_final.alpha_composite(bldg_main)
|
||||||
|
|
||||||
for l in mstr_ortho_layers:
|
for l in mstr_ortho_layers:
|
||||||
@ -153,8 +162,7 @@ class mstr_photogen:
|
|||||||
["water", "lake"],
|
["water", "lake"],
|
||||||
["water", "pond"],
|
["water", "pond"],
|
||||||
["water", "river"],
|
["water", "river"],
|
||||||
["leisure", "swimming_pool"],
|
["leisure", "swimming_pool"]
|
||||||
["waterway", "stream"]
|
|
||||||
)
|
)
|
||||||
for l in water_layers:
|
for l in water_layers:
|
||||||
fn = mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_" + l[0] + "-" + l[1] + "_layer_mask.png"
|
fn = mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_" + l[0] + "-" + l[1] + "_layer_mask.png"
|
||||||
|
Before Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 2.8 MiB |
BIN
textures/boundary/administrative/brd/b4.png
Normal file
After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 958 KiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 578 KiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 871 KiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 716 KiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 899 KiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 904 KiB After Width: | Height: | Size: 1.1 MiB |
BIN
textures/boundary/administrative/ptc/b4_p1.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
textures/boundary/administrative/ptc/b4_p2.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
textures/building/area/p11.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
textures/building/area/p12.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
textures/building/area/p13.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
textures/building/area/p14.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
textures/building/area/p15.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 2.8 MiB |
BIN
textures/landuse/residential-boundary/brd/b4.png
Normal file
After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 958 KiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 578 KiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 871 KiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 716 KiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 899 KiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 904 KiB After Width: | Height: | Size: 1.1 MiB |
BIN
textures/landuse/residential-boundary/ptc/b4_p1.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
textures/landuse/residential-boundary/ptc/b4_p2.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 2.8 MiB |
BIN
textures/landuse/residential/brd/b4.png
Normal file
After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 958 KiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 578 KiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 871 KiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 716 KiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 899 KiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 904 KiB After Width: | Height: | Size: 1.1 MiB |
BIN
textures/landuse/residential/ptc/b4_p1.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
textures/landuse/residential/ptc/b4_p2.png
Normal file
After Width: | Height: | Size: 1.2 MiB |