741 lines
38 KiB
Python
741 lines
38 KiB
Python
|
|
# -------------------------------------------------------------------
|
|
# 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
|
|
# -------------------------------------------------------------------
|
|
# layergen.py
|
|
# Generates a full-sized geo layer image, based on the required layer
|
|
# type. We use a simple randomization method to generate such an
|
|
# image, which is then used for the final photo in photogen.
|
|
# -------------------------------------------------------------------
|
|
|
|
import glob
|
|
import os
|
|
from random import randrange
|
|
import random
|
|
from PIL import Image, ImageFilter, ImageDraw, ImagePath
|
|
from defines import *
|
|
from log import *
|
|
from tiledb import *
|
|
from osmxml import *
|
|
from functions import *
|
|
|
|
class mstr_layergen:
|
|
|
|
# Initializes the layer generator. can_choose will go false if we need
|
|
# a pre-determined layer from another tile, should this be adjacent to it.
|
|
# In this case layer_needed will be populated with the appropriate number.
|
|
# You also need the zoom level so that we can generate a scaled version.
|
|
def __init__(self, tag, value, lat, latnum, lng, lngnum, is_line, is_completion=False):
|
|
self._tag = tag
|
|
self._value = value
|
|
self._latitude = lat
|
|
self._lat_number = latnum
|
|
self._longitude = lng
|
|
self._lng_number = lngnum
|
|
self._layerborder = -1
|
|
self._tiledb = mstr_tiledb(lat, lng)
|
|
self._tiledb.create_tables()
|
|
self._is_completion = is_completion
|
|
# Define layer size depending on what is wanted
|
|
self._imgsize = 0
|
|
self._isline = is_line
|
|
if mstr_photores == 2048: self._imgsize = 3000
|
|
if mstr_photores == 4096: self._imgsize = 6000
|
|
#mstr_msg("layergen", "Layer gen initialized")
|
|
|
|
# Define maximum latitude and longitude tile numbers
|
|
def set_max_latlng_tile(self, maxlatlng):
|
|
self._maxlat = maxlatlng[0]
|
|
self._maxlng = maxlatlng[1]
|
|
mstr_msg("layergen", "Maximum latitude and longitude tile numbers received")
|
|
|
|
# This generates a "border" image, for example farmland usually has a small space of grass
|
|
# before the actual crop of farm field itself. This generates this "border" layer,
|
|
# and returns it.
|
|
# Needs the actual edge mask, and the tag and value to be used as border.
|
|
# Perform necessary adjustments on the mask prior to this call, for example blurring or
|
|
# other effects.
|
|
def genborder(self, edgemask, tag, value):
|
|
layer = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
root_folder = mstr_datafolder + "Textures/" + tag + "/" + value
|
|
|
|
# Determine which sources we use
|
|
brd = glob.glob(root_folder + "/brd/b*.png")
|
|
src = -1
|
|
if len(brd) == 1: src=1
|
|
if len(brd) >= 2:
|
|
src = randrange(1, len(brd))
|
|
ptc = glob.glob(root_folder + "/ptc/b" + str(src) + "_p*.png")
|
|
|
|
# Load in the sources to work with
|
|
brd_src = Image.open(root_folder + "/brd/b" + str(src) + ".png")
|
|
ptc_src = []
|
|
for p in ptc:
|
|
ptc_src.append(Image.open(p))
|
|
mstr_msg("layergen", "Border sources selected")
|
|
|
|
# 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
|
|
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", "Border image generated")
|
|
|
|
# We now need to add the seamless border
|
|
layer.alpha_composite( brd_src )
|
|
mstr_msg("layergen", "Layer image completed")
|
|
|
|
# And now for the Big Mac.
|
|
# Generate the layer from the mask.
|
|
layer_comp = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
layer_final = Image.composite(layer, layer_comp, edgemask)
|
|
|
|
# Provide the image
|
|
return layer_final
|
|
|
|
|
|
# This generates the layer from the defined mask
|
|
def genlayer(self):
|
|
|
|
mstr_msg("layergen", "Layer to be generated: " + str(self._latitude) + "-" + str(self._lat_number) + ":" + str(self._longitude) + "-" + str(self._lng_number) + " -- tag: " + self._tag + " - value: " + self._value )
|
|
|
|
# Before we generate the layer, let's check for airports in this chunk
|
|
mstr_msg("layergen", "Checking for airport/s with ICAO code")
|
|
osmxml = mstr_osmxml(0,0)
|
|
icao = osmxml.find_icao_codes(mstr_datafolder + "_cache/tile.xml")
|
|
mstr_msg("layergen", "Found " + str(len(icao)) + " airport/s")
|
|
# Runway surface, if any other than concrete/asphalt
|
|
rw_surface = ""
|
|
# If we find an airport, make a note ...
|
|
if len(icao) >= 1:
|
|
for i in icao:
|
|
# ... but only, if this airport is not already noted
|
|
iccheck = self._tiledb.perform_query("SELECT * FROM airports WHERE icao='" + i +"';")
|
|
if len(iccheck) == 0:
|
|
self._tiledb.insert_icao(i, self._lat_number, self._lng_number, self._latitude, self._longitude)
|
|
mstr_msg("layergen", "Airport/s noted in data file")
|
|
rw_surface = osmxml.find_runway_surface(mstr_datafolder + "_cache/tile.xml")
|
|
|
|
# The image for the layer itself
|
|
layer = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
layer_pix = layer.load()
|
|
|
|
# There are some things we need to use sources for, and some things, we do not.
|
|
# We need to differentiate that.
|
|
|
|
if (self._isline == False and self._tag != "building") or (self._is_completion == True):
|
|
# Determine where we get the our source material from
|
|
root_folder = mstr_datafolder + "Textures/"
|
|
for s in mstr_ortho_layers:
|
|
if s[0] == self._tag and s[1] == self._value:
|
|
fld_main = len(s)-2
|
|
fld_sub = len(s)-1
|
|
root_folder = root_folder + s[fld_main] + "/" + s[fld_sub]
|
|
|
|
# Determine which sources to use.
|
|
# First, we need to check for adjacent tile information. We then either
|
|
# need to use the source of any adjacent tile, or we can choose freely.
|
|
src = -1
|
|
|
|
# Find our adjacent tiles
|
|
adjtiles = findAdjacentTilesTo(self._lat_number, self._lng_number)
|
|
|
|
mstr_msg("layergen", "Performing adjacency check")
|
|
# Walk through each tile and see what we can find in relation to this
|
|
# tile in the center
|
|
# Since we already know the order in adjtiles, we can do this real easy
|
|
if self._is_completion == False:
|
|
at = self._tiledb.get_adjacency_for_source(adjtiles[0][0], adjtiles[0][1], self._tag, self._value) # Top
|
|
ar = self._tiledb.get_adjacency_for_source(adjtiles[1][0], adjtiles[1][1], self._tag, self._value) # Right
|
|
ab = self._tiledb.get_adjacency_for_source(adjtiles[2][0], adjtiles[2][1], self._tag, self._value) # Bottom
|
|
al = self._tiledb.get_adjacency_for_source(adjtiles[3][0], adjtiles[3][1], self._tag, self._value) # Left
|
|
if self._is_completion == True:
|
|
at = self._tiledb.get_adjacency_for_completion(adjtiles[0][0], adjtiles[0][1]) # Top
|
|
ar = self._tiledb.get_adjacency_for_completion(adjtiles[1][0], adjtiles[1][1]) # Right
|
|
ab = self._tiledb.get_adjacency_for_completion(adjtiles[2][0], adjtiles[2][1]) # Bottom
|
|
al = self._tiledb.get_adjacency_for_completion(adjtiles[3][0], adjtiles[3][1]) # Left
|
|
|
|
if len(at) == 1:
|
|
self._tag = at[0][2]
|
|
self._value = at[0][3]
|
|
if len(ar) == 1:
|
|
self._tag = ar[0][2]
|
|
self._value = ar[0][3]
|
|
if len(ab) == 1:
|
|
self._tag = ab[0][2]
|
|
self._value = ab[0][3]
|
|
if len(al) == 1:
|
|
self._tag = al[0][2]
|
|
self._value = al[0][3]
|
|
|
|
root_folder = mstr_datafolder + "Textures/"
|
|
for s in mstr_ortho_layers:
|
|
if s[0] == self._tag and s[1] == self._value:
|
|
fld_main = len(s)-2
|
|
fld_sub = len(s)-1
|
|
root_folder = root_folder + s[fld_main] + "/" + s[fld_sub]
|
|
|
|
# We are south to the top tile.
|
|
if len(at) == 1 and src == -1:
|
|
if "b" in at[0][5]: src = int(at[0][4])
|
|
# We are west to the right tile.
|
|
if len(ar) == 1 and src == -1:
|
|
if "l" in ar[0][5]: src = int(ar[0][4])
|
|
# We are north to the bottom tile.
|
|
if len(ab) == 1 and src == -1:
|
|
if "t" in ab[0][5]: src = int(ab[0][4])
|
|
# We are east to the left tile.
|
|
if len(al) == 1 and src == -1:
|
|
if "r" in al[0][5]: src = int(al[0][4])
|
|
|
|
# Should we be at the border of the degree for latitude and longitude, we need to perform
|
|
# additional checks
|
|
is_deg_brd_t = False
|
|
is_deg_brd_r = False
|
|
is_deg_brd_b = False
|
|
is_deg_brd_l = False
|
|
if self._lat_number == 1: is_deg_brd_b = True
|
|
if self._lat_number == self._maxlat: is_deg_brd_t = True
|
|
if self._lng_number == 1: is_deg_brd_l = True
|
|
if self._lng_number == self._maxlng: is_deg_brd_r = True
|
|
|
|
# Adjacent latitude and longitude tiles
|
|
deg_tiles = []
|
|
deg_tiles.append( ( self._latitude+1, self._longitude ) ) # Top
|
|
deg_tiles.append( ( self._latitude, self._longitude+1 ) ) # Right
|
|
deg_tiles.append( ( self._latitude-1, self._longitude ) ) # Bottom
|
|
deg_tiles.append( ( self._latitude, self._longitude-1 ) ) # Left
|
|
|
|
# Perform degree border checks
|
|
# - and make sure we do not run into errors - this drove me crazy in testing
|
|
atd = []
|
|
ard = []
|
|
abd = []
|
|
ald = []
|
|
if is_deg_brd_t == True:
|
|
if self._is_completion == False:
|
|
atd = self._tiledb.get_adjacency_for_source_in_lat_lng(deg_tiles[0][0], deg_tiles[0][1], 1, self._lng_number, self._tag, self._value) # Top
|
|
if self._is_completion == True:
|
|
atd = self._tiledb.get_adjacency_for_completion_in_lat_lng(deg_tiles[0][0], deg_tiles[0][1], 1, self._lng_number) # Top
|
|
if is_deg_brd_r == True:
|
|
if self._is_completion == False:
|
|
ard = self._tiledb.get_adjacency_for_source_in_lat_lng(deg_tiles[1][0], deg_tiles[1][1], self._lat_number, 1, self._tag, self._value) # Right
|
|
if self._is_completion == True:
|
|
ard = self._tiledb.get_adjacency_for_completion_in_lat_lng(deg_tiles[1][0], deg_tiles[1][1], self._lat_number, 1) # Right
|
|
if is_deg_brd_b == True:
|
|
maxlatlng = self._tiledb.get_highest_latlong_from_tile(self._latitude-1, self._longitude)
|
|
if self._is_completion == False:
|
|
abd = self._tiledb.get_adjacency_for_source_in_lat_lng(deg_tiles[2][0], deg_tiles[2][1], maxlatlng[0], self._lng_number, self._tag, self._value) # Bottom
|
|
if self._is_completion == True:
|
|
abd = self._tiledb.get_adjacency_for_completion_in_lat_lng(deg_tiles[2][0], deg_tiles[2][1], maxlatlng[0], self._lng_number) # Bottom
|
|
if is_deg_brd_l == True:
|
|
maxlatlng = self._tiledb.get_highest_latlong_from_tile(self._latitude, self._longitude-1)
|
|
if self._is_completion == False:
|
|
ald = self._tiledb.get_adjacency_for_source_in_lat_lng(deg_tiles[2][0], deg_tiles[2][1], self._lat_number, maxlatlng[1], self._tag, self._value) # Left
|
|
if self._is_completion == True:
|
|
ald = self._tiledb.get_adjacency_for_completion_in_lat_lng(deg_tiles[2][0], deg_tiles[2][1], self._lat_number, maxlatlng[1]) # Left
|
|
|
|
if (is_deg_brd_t == True or is_deg_brd_r == True or is_deg_brd_b == True or is_deg_brd_l == True):
|
|
if src == -1 and self._is_completion == True:
|
|
if len(atd) == 1:
|
|
self._tag = atd[0][2]
|
|
self._value = atd[0][3]
|
|
if len(ard) == 1:
|
|
self._tag = ard[0][2]
|
|
self._value = ard[0][3]
|
|
if len(abd) == 1:
|
|
self._tag = abd[0][2]
|
|
self._value = abd[0][3]
|
|
if len(ald) == 1:
|
|
self._tag = ald[0][2]
|
|
self._value = ald[0][3]
|
|
|
|
root_folder = mstr_datafolder + "Textures/"
|
|
for s in mstr_ortho_layers:
|
|
if s[0] == self._tag and s[1] == self._value:
|
|
fld_main = len(s)-2
|
|
fld_sub = len(s)-1
|
|
root_folder = root_folder + s[fld_main] + "/" + s[fld_sub]
|
|
|
|
|
|
# Should we get here and one of the degree border checks turns out true,
|
|
# we need to make sure that we select the source we found. This should
|
|
# enable seamless tiling... around the entire planet
|
|
if is_deg_brd_t == True and len(at) == 0 and src == -1:
|
|
if len(atd) == 1:
|
|
if "b" in atd[0][5]: src = int(atd[0][4])
|
|
if is_deg_brd_r == True and len(ar) == 0 and src == -1:
|
|
if len(ard) == 1:
|
|
if "l" in ard[0][5]: src = int(ard[0][4])
|
|
if is_deg_brd_b == True and len(ab) == 0 and src == -1:
|
|
if len(abd) == 1:
|
|
if "t" in abd[0][5]: src = int(abd[0][4])
|
|
if is_deg_brd_l == True and len(al) == 0 and src == -1:
|
|
if len(ald) == 1:
|
|
if "r" in ald[0][5]: src = int(ald[0][4])
|
|
|
|
mstr_msg("layergen", "Adjacency check completed")
|
|
|
|
brd = glob.glob(root_folder + "/brd/b*.png")
|
|
|
|
# If the adjacency check returned nothing (src is still -1),
|
|
# then pick something
|
|
if src == -1:
|
|
if len(brd) == 1: src=1
|
|
if len(brd) >= 2:
|
|
src = randrange(1, len(brd))
|
|
|
|
ptc = glob.glob(root_folder + "/ptc/b" + str(src) + "_p*.png")
|
|
|
|
# Load in the sources to work with
|
|
brd_src = Image.open(root_folder + "/brd/b" + str(src) + ".png")
|
|
ptc_src = []
|
|
for p in ptc:
|
|
ptc_src.append(Image.open(p))
|
|
mstr_msg("layergen", "Layer sources selected")
|
|
|
|
# OK! Load the mask
|
|
if self._is_completion == False:
|
|
osm_mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + ".png" )
|
|
if self._is_completion == True:
|
|
osm_mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_tile-completion.png" )
|
|
|
|
# Generate an edge mask from the original
|
|
osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)
|
|
osm_edge = osm_edge.filter(ImageFilter.MaxFilter)
|
|
mstr_msg("layergen", "Edge mask generated")
|
|
|
|
# This adds some natural looking shapes to these types of features
|
|
if self._value == "forest" or self._value == "nature_reserve":
|
|
epx = osm_edge.load()
|
|
imgd = ImageDraw.Draw(osm_mask)
|
|
|
|
# Walk through a grid of 200x200 - on the edge image
|
|
for y in range(0, osm_mask.height, int(osm_mask.height/200)):
|
|
for x in range(0, osm_mask.width, int(osm_mask.width/200)):
|
|
px = epx[x,y]
|
|
if px[3] == 255:
|
|
rx = randrange(24,60)
|
|
ry = randrange(24,60)
|
|
f = randrange(1,10)
|
|
|
|
# Randomize the found locations a little
|
|
psx = randrange(x-11, x+11)
|
|
psy = randrange(y-11, y+11)
|
|
|
|
# Do some magic - but not on edges
|
|
if x > 0 and x < osm_mask.width and y > 0 and y < osm_mask.height:
|
|
if f != 5:
|
|
imgd.ellipse((psx-int(rx/2), psy-int(ry/2), psx+rx, psy+ry), fill="black")
|
|
if f == 3 or f == 7:
|
|
imgd.ellipse((psx-int(rx/2), psy-int(ry/2), psx+rx, psy+ry), fill=(0,0,0,0))
|
|
|
|
|
|
# We need to change the image in certain conditions
|
|
if self._value == "hedge" and self._tag == "barrier":
|
|
osm_mask = osm_edge
|
|
|
|
# From here on in we will need to perform some adjustments on the masks, depending
|
|
# on what they are.
|
|
for i in mstr_mask_blur:
|
|
if i[0] == self._tag and i[1] == self._value:
|
|
osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2]))
|
|
break
|
|
|
|
# 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
|
|
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")
|
|
|
|
|
|
# Here we need to do some magic to make some features look more natural
|
|
if (self._tag == "landuse" and self._value == "meadow") or (self._tag == "natural" and self._value == "grassland") or (self._tag == "natural" and self._value == "heath"):
|
|
amt = randrange(1,5)
|
|
for i in range(1, amt):
|
|
ptc = randrange(1, 14)
|
|
img = Image.open(mstr_datafolder + "Textures/tile/completion/p" + str(ptc)+".png")
|
|
lx = randrange( int(layer.width/20), layer.width - (int(layer.width/20)) - img.width )
|
|
ly = randrange( int(layer.width/20), layer.width - (int(layer.width/20)) - img.height )
|
|
layer.alpha_composite( img, (lx, ly) )
|
|
|
|
|
|
# We now need to add the seamless border
|
|
layer.alpha_composite( brd_src )
|
|
mstr_msg("layergen", "Layer image completed")
|
|
|
|
|
|
# And now for the Big Mac.
|
|
# Generate the layer from the mask.
|
|
layer_comp = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
layer_pix = layer.load()
|
|
mask_pix = osm_mask.load()
|
|
layer_comp_pix = layer_comp.load()
|
|
for y in range(self._imgsize):
|
|
for x in range(self._imgsize):
|
|
if mask_pix[x, y][3] > 0:
|
|
rgb=layer_pix[x,y]
|
|
a=mask_pix[x,y]
|
|
if self._value == "residential":
|
|
layer_comp_pix[x, y] = ( rgb[0], rgb[1], rgb[2], int(a[3]/2))
|
|
if self._value != "residential":
|
|
layer_comp_pix[x, y] = ( rgb[0], rgb[1], rgb[2], a[3])
|
|
|
|
# For some things, we will need to add a border and then add this to the layer.
|
|
layer_border = None
|
|
if self._tag == "landuse":
|
|
if self._value == "forest" or self._value == "farmland":
|
|
osm_edge = osm_edge.filter(ImageFilter.ModeFilter(size=15))
|
|
osm_edge = osm_edge.filter(ImageFilter.BoxBlur(radius=2))
|
|
layer_border = self.genborder(osm_edge, "landuse", "meadow")
|
|
layer_comp.alpha_composite(layer_border)
|
|
|
|
|
|
# Store layer
|
|
if self._is_completion == False:
|
|
layer_comp.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" )
|
|
if self._is_completion == True:
|
|
layer_comp.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_tile-completion_layer.png" )
|
|
#layer_final.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" )
|
|
mstr_msg("layergen", "Layer image finalized and saved.")
|
|
|
|
|
|
# Let's try our hand at pseudo shadows
|
|
if mstr_shadow_enabled == True:
|
|
if mstr_shadow_shift >= 2:
|
|
shadow = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
for sh in mstr_shadow_casters:
|
|
if self._tag == sh[0] and self._value == sh[1]:
|
|
mstr_msg("layergen", "Generating shadow for layer")
|
|
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 = 0
|
|
# Buildings get slightly closer shadows
|
|
if self._tag == "building":
|
|
shf_x = x + int(mstr_shadow_shift/2)
|
|
if self._tag != "building":
|
|
shf_x = x + mstr_shadow_shift
|
|
if shf_x <= self._imgsize-1:
|
|
a = mask_pix[x,y][3]
|
|
st = 0
|
|
if self._tag == "building":
|
|
st = random.uniform(0.25, mstr_shadow_strength/2)
|
|
if self._tag != "building":
|
|
st = random.uniform(0.45, mstr_shadow_strength)
|
|
ca = a * st
|
|
aa = int(ca)
|
|
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")
|
|
mstr_msg("layergen", "Shadow layer completed")
|
|
|
|
|
|
|
|
# Check if pixels touch the borders of the image, and if so -
|
|
# make a not of that in the database.
|
|
at=False
|
|
ar=False
|
|
ab=False
|
|
al=False
|
|
layer_pix = layer_comp.load() # <- Just to be safe
|
|
|
|
# Top scan
|
|
for i in range(0, self._imgsize-1):
|
|
p = layer_pix[i,0]
|
|
if p[3] > 0:
|
|
at=True
|
|
break
|
|
|
|
# Right scan
|
|
for i in range(0, self._imgsize-1):
|
|
p = layer_pix[self._imgsize-1,i]
|
|
if p[3] > 0:
|
|
ar=True
|
|
break
|
|
|
|
# Bottom scan
|
|
for i in range(0, self._imgsize-1):
|
|
p = layer_pix[i,self._imgsize-1]
|
|
if p[3] > 0:
|
|
ab=True
|
|
break
|
|
|
|
# Left scan
|
|
for i in range(0, self._imgsize-1):
|
|
p = layer_pix[1,i]
|
|
if p[3] > 0:
|
|
al=True
|
|
break
|
|
|
|
# Construct DB String
|
|
adjstr = ""
|
|
if at==True: adjstr = adjstr + "t"
|
|
if ar==True: adjstr = adjstr + "r"
|
|
if ab==True: adjstr = adjstr + "b"
|
|
if al==True: adjstr = adjstr + "l"
|
|
|
|
# Store into DB - but only if there is something to store
|
|
if adjstr != "":
|
|
if self._is_completion == False:
|
|
r = self._tiledb.get_adjacency_for_source(self._lat_number, self._lng_number, self._tag, self._value)
|
|
if len(r) == 0:
|
|
self._tiledb.insert_info(self._lat_number, self._lng_number, self._tag, self._value, src, adjstr)
|
|
mstr_msg("layergen", "Adjacency info stored in database")
|
|
|
|
if self._is_completion == True:
|
|
r = self._tiledb.get_adjacency_for_completion(self._lat_number, self._lng_number)
|
|
if len(r) == 0:
|
|
self._tiledb.insert_completion_info(self._lat_number, self._lng_number, self._tag, self._value, src, adjstr)
|
|
mstr_msg("layergen", "Adjacency info for completion stored in database")
|
|
|
|
self._tiledb.commit_query()
|
|
self._tiledb.close_db()
|
|
|
|
|
|
# If we encounter one of these road-specific tags, we need to proceed differently.
|
|
|
|
if self._isline == True or self._tag == "building":
|
|
|
|
# We will need the mask in question
|
|
osm_mask = Image.open( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + ".png" )
|
|
|
|
# Generate an edge mask from the original
|
|
osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)
|
|
osm_edge = osm_edge.filter(ImageFilter.MaxFilter)
|
|
mstr_msg("layergen", "Edge mask generated")
|
|
|
|
# As above, we will apply the blur as noted in the defines
|
|
for i in mstr_mask_blur:
|
|
if i[0] == self._tag and i[1] == self._value:
|
|
osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2]))
|
|
break
|
|
osm_edge = osm_edge.filter(ImageFilter.BoxBlur(radius=1))
|
|
|
|
|
|
# And now for the Big Mac.
|
|
# Generate the layer from the mask. Same as above - except!
|
|
# This time we have no source material - instead we will fill the
|
|
# mask with a color that is appropriate for this street type.
|
|
layer_comp = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
mask_pix = osm_mask.load()
|
|
edge_pix = osm_edge.load()
|
|
layer_comp_pix = layer_comp.load()
|
|
|
|
# Let's define some base color ranges for different types of buildings
|
|
bld_clr = [
|
|
("detached", 190, 192, 195),
|
|
("church", 134, 134, 136),
|
|
("hotel", 153, 147, 138),
|
|
("farm", 145, 124, 121),
|
|
("semidetached_house", 167, 163, 152),
|
|
("apartments", 129, 134, 127),
|
|
("civic", 134, 134, 136),
|
|
("garage", 101, 109, 111),
|
|
("office", 139, 152, 156),
|
|
("retail", 121, 122, 108),
|
|
("industrial", 191, 192, 187),
|
|
("house", 145, 124, 121),
|
|
("terrace", 191, 192, 187),
|
|
("hangar", 137, 162, 195),
|
|
("school", 111, 117, 115),
|
|
("yes", 152, 144, 141)
|
|
]
|
|
|
|
# Find the color index to work with
|
|
cidx = 0
|
|
if self._tag == "building":
|
|
for c in bld_clr:
|
|
if c[0] == self._value:
|
|
break
|
|
cidx = cidx+1
|
|
|
|
for y in range(self._imgsize):
|
|
for x in range(self._imgsize):
|
|
if mask_pix[x, y][3] > 0:
|
|
a=mask_pix[x,y]
|
|
e=edge_pix[x,y]
|
|
# Find a suitable color
|
|
d = 0
|
|
if self._tag == "aeroway" and self._value == "runway":
|
|
# It seems only runways with any other surface than concrete
|
|
# are mentioned in OSM. So we need to make sure when to render
|
|
# "concrete" and when to leave it. Only sometimes the word
|
|
# "asphalt" is mentioned
|
|
if rw_surface == "" or rw_surface == "asphalt":
|
|
d = randrange(81, 101)
|
|
layer_comp_pix[x, y] = ( d,d,d,a[3] )
|
|
if self._tag == "railway":
|
|
d = randrange(41, 61)
|
|
layer_comp_pix[x, y] = ( d,d,d,a[3] )
|
|
if self._tag == "highway" and self._value != "motorway":
|
|
d = randrange(140,160)
|
|
layer_comp_pix[x, y] = ( d,d,d,a[3] )
|
|
if self._tag == "highway" and self._value == "motorway":
|
|
d = randrange(1,20)
|
|
r = 86-d
|
|
g = 97-d
|
|
b = 106-d
|
|
layer_comp_pix[x, y] = ( r,g,b,a[3] )
|
|
if self._tag == "waterway" and (self._value == "stream" or self._value == "river"):
|
|
d = randrange(1, 15)
|
|
# Rock, grass, water
|
|
mats = [ (48-d, 45-d, 42-d), (58-d, 81-d, 41-d), (129-d, 148-d, 159-d) ]
|
|
# Pick one of those
|
|
pick = randrange(1,4)
|
|
t = a[3]-d
|
|
if t < 0: t = 0
|
|
layer_comp_pix[x, y] = ( mats[pick-1][0], mats[pick-1][1], mats[pick-1][2], t )
|
|
|
|
# A bit special here
|
|
if self._tag == "building":
|
|
|
|
# Find a color range
|
|
d = randrange(1,21)
|
|
# Adjust this pixel
|
|
c = (bld_clr[cidx][1]-d, bld_clr[cidx][2]-d, bld_clr[cidx][3]-d, 255)
|
|
# Set pixel
|
|
layer_comp_pix[x, y] = c
|
|
|
|
if self._value == "track" or self._value == "path":
|
|
d = randrange(1,20)
|
|
r = 164 - d
|
|
g = 159 - d
|
|
b = 138 - d
|
|
layer_comp_pix[x, y] = ( r,g,b,a[3] )
|
|
|
|
# We will do some super magic here to let houses look more realistic
|
|
if self._tag == "building":
|
|
vls = [ "detached", "hotel", "farm", "semidetached_house", "apartments", "civic", "office", "retail", "industrial", "house", "school", "yes" ]
|
|
if self._value in vls:
|
|
# Generate a new image
|
|
details = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
details_pix = details.load()
|
|
layer_pix = layer_comp.load()
|
|
for y in range(self._imgsize-1):
|
|
for x in range(self._imgsize-1):
|
|
p = layer_pix[x,y]
|
|
if p[3] > 0:
|
|
shf_x = x+randrange(1, 21)
|
|
shf_y = y+randrange(1, 21)
|
|
shf_x2 = x-randrange(1, 21)
|
|
shf_y2 = y-randrange(1, 21)
|
|
if shf_x <= self._imgsize-1 and shf_x >= 0 and shf_y <= self._imgsize-1 and shf_y >= 0:
|
|
st = random.uniform(0.85, 1.0)
|
|
ca = 255 * st
|
|
aa = int(ca)
|
|
d = randrange(1,26)
|
|
d2 = randrange(1,26)
|
|
details_pix[shf_x, shf_y] = (187-d, 179-d, 176-d, aa)
|
|
details_pix[shf_x2, shf_y2] = (187-d2, 179-d2, 176-d2, aa)
|
|
# Merge the details BELOW the houses
|
|
details.alpha_composite(layer_comp)
|
|
layer_comp = details
|
|
# New edge
|
|
osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)
|
|
osm_edge = osm_edge.filter(ImageFilter.GaussianBlur(radius=1))
|
|
# Blur the image
|
|
layer_comp = layer_comp.filter(ImageFilter.GaussianBlur(radius=1))
|
|
osm_edge.alpha_composite(layer_comp)
|
|
layer_comp = osm_edge
|
|
|
|
|
|
# Add some random trees
|
|
div = int(self._imgsize/200)
|
|
trees = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
for y in range(0, self._imgsize, div):
|
|
for x in range(0, self._imgsize, div):
|
|
if x > 0 and x < self._imgsize and y > 0 and y < self._imgsize:
|
|
p = mask_pix[x, y]
|
|
if p[3] != 0:
|
|
# We found something...
|
|
# Determine if we put something somewhere
|
|
placement = randrange(0, 5)
|
|
if placement == 1:
|
|
# Do some random shift away from this location
|
|
shf_x = randrange(x-11, x+11)
|
|
shf_y = randrange(y-11, y+11)
|
|
if shf_x > 0 and shf_x < self._imgsize and shf_y > 0 and shf_y < self._imgsize:
|
|
# Pick a number of trees to place
|
|
numtrees = randrange(1, 16)
|
|
for i in range(1, numtrees):
|
|
# Pick some file
|
|
pick = str(randrange(1, 11))
|
|
tree = Image.open(mstr_datafolder + "Textures/building/area/p" + pick + ".png")
|
|
# Do a correction for the location if needed
|
|
if shf_x < 1: shf_x = 1
|
|
if shf_y < 1: shf_y = 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
|
|
trees.alpha_composite(tree, (shf_x, shf_y))
|
|
trees.alpha_composite(layer_comp)
|
|
layer_comp = trees
|
|
|
|
|
|
mstr_msg("layergen", "Layer image generated")
|
|
|
|
# Building shadow
|
|
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 + randrange(1, mstr_shadow_shift)
|
|
shf_x2 = x + randrange(1, mstr_shadow_shift)
|
|
if shf_x <= self._imgsize-1 and shf_x >= 0 and shf_x2 <= self._imgsize-1 and shf_x2 >= 0:
|
|
a = mask_pix[x,y][3]
|
|
st = random.uniform(0.45, mstr_shadow_strength)
|
|
ca = a * st
|
|
aa = int(ca)
|
|
shadow_pix[shf_x, y] = (0,0,0,aa)
|
|
shadow_pix[shf_x2, 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")
|
|
mstr_msg("layergen", "Shadow layer completed")
|
|
|
|
# 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"):
|
|
# We will now add some white lines for coolness
|
|
mask_pix = osm_edge.load()
|
|
layer_comp_pix = layer_comp.load()
|
|
for y in range(self._imgsize):
|
|
for x in range(self._imgsize):
|
|
if mask_pix[x, y][3] > 0:
|
|
# Find a suitable color
|
|
w = randrange(185, 215)
|
|
a=mask_pix[x,y]
|
|
layer_comp_pix[x, y] = ( w,w,w,a[3] )
|
|
|
|
mstr_msg("layergen", "Street lines added")
|
|
|
|
if self._tag == "waterway" and (self._value == "river" or self._value == "stream"):
|
|
layer_comp = layer_comp.filter(ImageFilter.GaussianBlur(radius=4))
|
|
|
|
# Store layer
|
|
layer_comp.save( mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_" + self._tag + "-" + self._value + "_layer.png" )
|
|
mstr_msg("layergen", "Layer image finalized and saved.")
|
|
|