662 lines
26 KiB
Python
662 lines
26 KiB
Python
|
|
import os
|
|
from PIL import Image, ImageFilter, ImageEnhance, ImageFile
|
|
|
|
from defines import *
|
|
from layergen import *
|
|
from log import *
|
|
from functions import *
|
|
from xp_normalmap import *
|
|
|
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
|
|
|
# -------------------------------------------------------------------
|
|
# 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
|
|
# -------------------------------------------------------------------
|
|
# photogen.py
|
|
# The class that generates the photo tiles from previous layers,
|
|
# in their correct order.
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
class mstr_photogen:
|
|
|
|
# Initializer doesn't need much
|
|
def __init__ (self, lat, lng, ty, tx, maxlat, maxlng):
|
|
self._lat = lat
|
|
self._lng = lng
|
|
self._ty = ty
|
|
self._tx = tx
|
|
self._maxlatlng = [ maxlat, maxlng ]
|
|
# Define layer size depending on what is wanted
|
|
self._imgsize = mstr_photores
|
|
# Empty image where everything goes into
|
|
self._tile = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
self._latlngfld = self.latlng_folder([lat,lng])
|
|
mstr_msg("photogen", "Photogen initialized")
|
|
|
|
|
|
# Defines the order of layer names that were processed
|
|
# Called by orthographic prior to starting the photo gen process
|
|
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):
|
|
# Template for the file name which is always the same
|
|
#root_filename = mstr_datafolder + "/_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_"
|
|
|
|
# Correct layers
|
|
mstr_msg("photogen", "Correcting layer order issues")
|
|
layers = self.correctLayerIssues(layers)
|
|
|
|
# First, we walk through all layers and blend them on top of each other, in order
|
|
mstr_msg("photogen", "Merging layers")
|
|
|
|
lyr=0
|
|
for l in layers:
|
|
if self._lyrnames[lyr] != "building":
|
|
self._tile.alpha_composite(l)
|
|
lyr=lyr+1
|
|
|
|
|
|
# When we have run through this loop, we will end up with a sandwiched
|
|
# image of all the other images, in their correct order.
|
|
# However, since I have discovered that some areas in OSM simply do not
|
|
# have any tag or information, it is possible that the final image will
|
|
# have empty, alpha-transparent patches.
|
|
# For this reason we need to check against these and fix that.
|
|
|
|
# First, we will check if there is something to fix:
|
|
emptyspace = self.checkForEmptySpace()
|
|
mstr_msg("photogen", "Checked for empty patches")
|
|
|
|
# If this check comes back as true, we need to perform
|
|
# aforementioned fix:
|
|
if emptyspace == True:
|
|
|
|
mstr_msg("photogen", "Patching empty space")
|
|
|
|
cmpl = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
|
|
edn = self.find_earthnavdata_number()
|
|
edns = self.latlng_folder(edn)
|
|
|
|
cmpl_ptc = Image.open(mstr_datafolder + "textures/tile/background/ptc.png")
|
|
cmpl_brd = Image.open(mstr_datafolder + "textures/tile/background/brd.png")
|
|
|
|
# Generate the source image
|
|
for p in range(1, 201):
|
|
rx = randrange(0 - int(cmpl_ptc.width / 2), cmpl.width - int(cmpl_ptc.width / 2))
|
|
ry = randrange(0 - int(cmpl_ptc.height / 2), cmpl.height - int(cmpl_ptc.height / 2))
|
|
cmpl.alpha_composite(cmpl_ptc, dest=(rx, ry))
|
|
cmpl.alpha_composite(cmpl_brd)
|
|
|
|
# Patches to add from other sources. If they don't exist, we also need to make them
|
|
masks = glob.glob(mstr_datafolder + "textures/tile/completion/*.png")
|
|
amt = randrange(5, 16)
|
|
patchtags = [
|
|
["landuse", "meadow"],
|
|
["landuse", "grass"],
|
|
["natural", "heath"],
|
|
["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._tile.width or patchmask.height > self._tile.height:
|
|
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))
|
|
|
|
rg = mstr_resourcegen(patchtags[patchpick][0], patchtags[patchpick][1], src)
|
|
rg.setLayerContrast(randrange(1, 4))
|
|
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(0, self._imgsize - layerpatch.width)
|
|
ly = randrange(0, self._imgsize - layerpatch.height)
|
|
cmpl.alpha_composite(layerpatch, (lx, ly))
|
|
|
|
# Merge the images
|
|
cmpl.alpha_composite(self._tile)
|
|
|
|
# Make this the real one
|
|
self._tile = cmpl
|
|
|
|
|
|
# There may be some tiles that have a larger sea or even an ocean in them - these need to be
|
|
# removed from the final tile
|
|
ocean_pix = self._tile.load()
|
|
for y in range(self._tile.width):
|
|
for x in range(self._tile.height):
|
|
p = ocean_pix[x,y]
|
|
if p[0] == 255 and p[1] == 0 and p[2] == 255:
|
|
t = (0,0,0,0)
|
|
ocean_pix[x,y] = t
|
|
|
|
# Alpha correction on final image
|
|
corrpix = self._tile.load()
|
|
for y in range(0, self._tile.height):
|
|
for x in range(0, self._tile.width):
|
|
c = corrpix[x,y]
|
|
if c[3] > 0:
|
|
nc = (c[0], c[1], c[2], 255)
|
|
corrpix[x,y] = nc
|
|
if c[3] == 0:
|
|
corrpix[x,y] = (0,0,0,0)
|
|
|
|
# One more thing...
|
|
mstr_msg("photogen", "Adding features to layers")
|
|
self.addTreesToFeatures(layers, waterlayers)
|
|
# One final thing...
|
|
mstr_msg("photogen", "Adding details to buildings")
|
|
self.addDetailsToBuildings()
|
|
|
|
# Throw missing buildings on top
|
|
lyr = 0
|
|
for l in layers:
|
|
if self._lyrnames[lyr] == "building":
|
|
self._tile.alpha_composite(l)
|
|
lyr = lyr + 1
|
|
|
|
# We are now in posession of the final image.
|
|
|
|
# Contrast
|
|
#self._tile = ImageEnhance.Contrast(self._tile).enhance(0.1)
|
|
|
|
# This we can save accordingly.
|
|
self._tile.save(mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + ".png")
|
|
|
|
# Now we convert this into a DDS
|
|
_tmpfn = mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx)
|
|
os.system(mstr_xp_ddstool + " --png2dxt1 " + _tmpfn + ".png " + _tmpfn + ".dds" )
|
|
os.remove(mstr_datafolder + "z_orthographic/orthos/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx) + ".png")
|
|
|
|
# Now generate the normal map for this ortho.
|
|
# But only if this is enabled.
|
|
if mstr_xp_genscenery and mstr_xp_scn_normalmaps:
|
|
# Generate the normal normal map first (hah)
|
|
nrm = mstr_xp_normalmap()
|
|
#nrmimg = nrm.generate_normal_map_for_layer(self._tile, False)
|
|
|
|
# Now we need to walk through the water layers and generate a combined normal map
|
|
wtrlyr = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
for w in waterlayers:
|
|
wtrlyr.alpha_composite(w)
|
|
wtrlyr = wtrlyr.resize((int(mstr_photores/2), int(mstr_photores/2)), Image.Resampling.BILINEAR)
|
|
wtrimg = nrm.generate_normal_map_for_layer(wtrlyr, True)
|
|
|
|
# Blend
|
|
#nrmimg.alpha_composite(wtrimg)
|
|
|
|
# Save
|
|
nrmfln = mstr_datafolder + "z_orthographic/normals/" + self._latlngfld + "/" + str(self._ty) + "_" + str(self._tx)
|
|
wtrimg.save(nrmfln + ".png")
|
|
|
|
|
|
# Generates some random tree.
|
|
# We will now move away from using pre-made trees...
|
|
# they didn't look so great
|
|
def generate_tree(self, bccolor=None):
|
|
sx = randrange(18, 31)
|
|
sy = randrange(18, 31)
|
|
|
|
treepoly = Image.new("RGBA", (sx, sy))
|
|
draw = ImageDraw.Draw(treepoly)
|
|
|
|
draw.ellipse((4, 4, sx - 4, sy - 4), fill="black")
|
|
|
|
tree = Image.new("RGBA", (sx, sy))
|
|
treepx = tree.load()
|
|
maskpx = treepoly.load()
|
|
|
|
# How many tree points do we want?
|
|
treepts = 75
|
|
# How many of those have been drawn?
|
|
ptsdrawn = 0
|
|
|
|
bc = []
|
|
bcp = 0
|
|
if bccolor == None:
|
|
bc = [
|
|
(36, 50, 52),
|
|
(30, 41, 39),
|
|
(32, 45, 37),
|
|
(32, 39, 49),
|
|
(33, 34, 40),
|
|
(44, 50, 53),
|
|
(40, 46, 48),
|
|
(14, 31, 38),
|
|
(17, 41, 33),
|
|
(39, 56, 35),
|
|
(51, 51, 42),
|
|
(12, 27, 31),
|
|
(45, 59, 48),
|
|
(37, 54, 29),
|
|
(59, 50, 34),
|
|
(59, 59, 35),
|
|
(59, 51, 35),
|
|
(70, 72, 45),
|
|
(48, 59, 44),
|
|
(29, 47, 23),
|
|
(47, 61, 43),
|
|
(29, 68, 15),
|
|
(53, 77, 63),
|
|
(20, 68, 40)
|
|
]
|
|
bcp = randrange(0, len(bc))
|
|
else:
|
|
bc = bccolor
|
|
bcp = randrange(0, len(bc))
|
|
|
|
treedraw = ImageDraw.Draw(tree)
|
|
while ptsdrawn < treepts + 1:
|
|
rx = randrange(0, sx)
|
|
ry = randrange(0, sy)
|
|
mp = maskpx[rx, ry]
|
|
if mp[3] > 0:
|
|
d = randrange(0, 51)
|
|
r = bc[bcp][0]
|
|
g = bc[bcp][1] + d
|
|
b = bc[bcp][2] + (d // 2)
|
|
a = randrange(170, 256)
|
|
c = (r, g, b, a)
|
|
ex = randrange(2, 5)
|
|
ey = randrange(2, 5)
|
|
treedraw.ellipse((rx - (ex // 2), ry - (ey // 2), rx + (ey // 2), ry + (ey // 2)), fill=c)
|
|
ptsdrawn = ptsdrawn + 1
|
|
|
|
for y in range(0, tree.height):
|
|
for x in range(0, tree.width):
|
|
tp = treepx[x, y]
|
|
diff = randrange(0, 31)
|
|
nc = (tp[0] - diff, tp[1] - diff, tp[2] - diff, tp[3])
|
|
treepx[x, y] = nc
|
|
|
|
tree = tree.filter(ImageFilter.GaussianBlur(radius=0.5))
|
|
|
|
for y in range(0, tree.height):
|
|
for x in range(0, tree.width):
|
|
tp = treepx[x, y]
|
|
diff = randrange(0, 51)
|
|
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
|
|
|
|
|
|
# This used to be in layergen and solves the problem of roads being rendered above trees.
|
|
# It is the only solution that worked, after some research.
|
|
def addTreesToFeatures(self, layers, wtr):
|
|
|
|
# Generate a combined water layer. Order and appearance does not matter.
|
|
wtrlyr = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
for l in wtr:
|
|
wtrlyr.alpha_composite(l)
|
|
|
|
# Preload the water layer for comparison
|
|
wtrpix = wtrlyr.load()
|
|
|
|
# To make trees blend better with the environment, we can determine
|
|
# an average color from the forest layer. The pass this color as
|
|
# base instead.
|
|
forest = -1
|
|
curlyr = 0
|
|
for lyr in self._lyrnames:
|
|
if lyr[0] == "landuse" and lyr[1] == "forest":
|
|
forest = curlyr
|
|
|
|
# Find the average color of the forest layer
|
|
frstclr = []
|
|
frstpix = None
|
|
if forest != -1:
|
|
frstpix = layers[forest].load()
|
|
for y in range(0, self._imgsize):
|
|
for x in range(0, self._imgsize):
|
|
frs = frstpix[x,y]
|
|
if frs[3] > 0:
|
|
c = ( frs[0]-40, frs[1]-40, frs[2]-40 )
|
|
frstclr.append(c)
|
|
|
|
|
|
# Walk through list of layers to decide where to add the trees
|
|
curlyr = 0
|
|
for lyr in self._lyrnames:
|
|
# Add trees only in some features
|
|
if (lyr[0] == "landuse" and lyr[1] == "cemetery") or (
|
|
lyr[0] == "landuse" and lyr[1] == "residential") or (
|
|
lyr[0] == "leisure" and lyr[1] == "park"):
|
|
trees = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
amt = 4000
|
|
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)
|
|
ly = randrange(0, self._imgsize)
|
|
lp = lyrmask[lx,ly]
|
|
wp = wtrpix[lx,ly]
|
|
if lp[3] == 255 and wp[3] == 0: # Exclude water bodies from tree placement
|
|
tree = None
|
|
if len(frstclr) != 0: tree = self.generate_tree(bccolor=frstclr)
|
|
else: tree = self.generate_tree()
|
|
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:
|
|
rndshd = randrange(5, 210)
|
|
sc = (0, 0, 0, rndshd)
|
|
spx = 8
|
|
spy = 5
|
|
if self._zoomlevel == 16:
|
|
spx = 2
|
|
spy = 1
|
|
if x + spx < self._imgsize and y + spy < self._imgsize:
|
|
shadow_pix[x + spx, y + spy] = sc
|
|
tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2))
|
|
tree_shadow.alpha_composite(trees)
|
|
self._tile.alpha_composite(tree_shadow)
|
|
curlyr = curlyr + 1
|
|
|
|
# Reset for tree rows
|
|
curlyr = 0
|
|
treerow = -1
|
|
for lyr in self._lyrnames:
|
|
if lyr[0] == "natural" and lyr[1] == "tree_row":
|
|
treerow = curlyr
|
|
break
|
|
curlyr = curlyr + 1
|
|
|
|
if treerow != -1:
|
|
rowpx = layers[curlyr].load()
|
|
for y in range(0, self._tile.height):
|
|
for x in range(0, self._tile.width):
|
|
tpx = rowpx[x,y]
|
|
if tpx[3] == 1:
|
|
tree = None
|
|
if len(frstclr) == 0: tree = self.generate_tree()
|
|
else: tree = self.generate_tree(bccolor=frstclr)
|
|
self._tile.alpha_composite(tree, dest=(x, y))
|
|
|
|
# Reset
|
|
curlyr = 0
|
|
bldg = []
|
|
tilepix = self._tile.load()
|
|
for lyr in self._lyrnames:
|
|
if lyr[0] == "building":
|
|
bldg.append(curlyr)
|
|
curlyr = curlyr + 1
|
|
|
|
for b in range(0, len(bldg)):
|
|
bldg_lyr = layers[bldg[b]].load()
|
|
shdw = Image.open(mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_" + self._lyrnames[bldg[b]][0] + "-" + self._lyrnames[bldg[b]][1] + "_layer_shadow.png")
|
|
shdw_pix = shdw.load()
|
|
for y in range(0, self._imgsize):
|
|
for x in range(0, self._imgsize):
|
|
bpix = bldg_lyr[x,y]
|
|
spix = shdw_pix[x,y]
|
|
if bpix[3] > 0 and spix[0] == 255:
|
|
tilepix[x,y] = ( bpix[0], bpix[1], bpix[2], bpix[3] )
|
|
|
|
|
|
# Adds some intricate details to buildings
|
|
def addDetailsToBuildings(self):
|
|
curlyr = 0
|
|
bldg = []
|
|
for lyr in self._lyrnames:
|
|
if lyr[0] == "building":
|
|
bldg.append(curlyr)
|
|
curlyr = curlyr + 1
|
|
|
|
for b in range(0, len(bldg)):
|
|
shdw = Image.open(mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_" + self._lyrnames[bldg[b]][0] + "-" + self._lyrnames[bldg[b]][1] + "_layer_shadow.png")
|
|
shdw_pix = shdw.load()
|
|
|
|
det_image = Image.open(mstr_datafolder + "textures/building/details/"+str(randrange(1, 16))+".png")
|
|
|
|
detpx = det_image.load()
|
|
|
|
for y in range(0, shdw.height):
|
|
for x in range(0, shdw.width):
|
|
spx = shdw_pix[x,y]
|
|
if spx[0] != 255:
|
|
c = (0,0,0,0)
|
|
detpx[x,y] = c
|
|
|
|
self._tile.alpha_composite(det_image)
|
|
|
|
|
|
|
|
# Correct some layer issues
|
|
def correctLayerIssues(self, layers):
|
|
|
|
# First the residential/forest dilemma
|
|
residential = -1
|
|
forest = -1
|
|
reserve = -1
|
|
|
|
curlyr = 0
|
|
for lyr in self._lyrnames:
|
|
if lyr[0] == "landuse" and lyr[1] == "residential": residential=curlyr
|
|
if lyr[0] == "landuse" and lyr[1] == "forest": forest = curlyr
|
|
if lyr[0] == "leisure" and lyr[1] == "nature_reserve": reserve = curlyr
|
|
curlyr = curlyr+1
|
|
|
|
# Make sure we hit the correct layers with correct content!
|
|
# Only do something if both are found.
|
|
if residential != -1 and forest != -1:
|
|
rsd_pix = layers[residential].load()
|
|
frs_pix = layers[forest].load()
|
|
rsd_adj = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
adj_pix = rsd_adj.load()
|
|
|
|
for y in range(0, self._tile.height):
|
|
for x in range(0, self._tile.width):
|
|
fp = frs_pix[x,y]
|
|
rp = rsd_pix[x,y]
|
|
if rp[3] > 0 and fp[3] > 0:
|
|
adj_pix[x,y] = (rp[0], rp[1], rp[2], rp[3])
|
|
|
|
layers[forest].alpha_composite(rsd_adj)
|
|
|
|
|
|
if residential != -1 and reserve != -1:
|
|
rsd_pix = layers[residential].load()
|
|
frs_pix = layers[reserve].load()
|
|
rsd_adj = Image.new("RGBA", (self._imgsize, self._imgsize))
|
|
adj_pix = rsd_adj.load()
|
|
|
|
for y in range(0, self._tile.height):
|
|
for x in range(0, self._tile.width):
|
|
fp = frs_pix[x,y]
|
|
rp = rsd_pix[x,y]
|
|
if rp[3] > 0 and fp[3] > 0:
|
|
adj_pix[x,y] = (rp[0], rp[1], rp[2], rp[3])
|
|
|
|
layers[reserve].alpha_composite(rsd_adj)
|
|
|
|
# At ZL16 there seems to be an issue with water bodies, especially rivers (natural:water).
|
|
# Let's see if we can correct that
|
|
|
|
# These files will need to be generated after manual check of the orthos
|
|
if os.path.isfile(mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/water/" + str(self._ty) + "_" + str(self._tx)) == True:
|
|
# natural:water
|
|
ntrl_water = False
|
|
ntrl_idx = 0
|
|
for lyr in self._lyrnames:
|
|
if lyr[0] == "natural" and lyr[1] == "water":
|
|
ntrl_water = True
|
|
break
|
|
ntrl_idx = ntrl_idx + 1
|
|
|
|
if ntrl_water == True:
|
|
|
|
# We'll go through all layers and "shavve off" excess.
|
|
# This way we can hopefully correct incorrect line and polygon renders.
|
|
for l in range(0, len(layers)):
|
|
if l < ntrl_idx: # <- We don't want to load the target layer itself
|
|
lyrpix = layers[l].load()
|
|
wtr_pix = layers[ntrl_idx].load()
|
|
for y in range(0, self._tile.height):
|
|
for x in range(0, self._tile.width):
|
|
lp = lyrpix[x,y]
|
|
wp = wtr_pix[x,y]
|
|
if lp[3] > 0:
|
|
a = 255 - lp[3]
|
|
if a < wp[3]:
|
|
c = (wp[0], wp[1], wp[2], a)
|
|
wtr_pix[x,y] = c
|
|
|
|
# water:river
|
|
river_water = False
|
|
river_idx = 0
|
|
for lyr in self._lyrnames:
|
|
if lyr[0] == "water" and lyr[1] == "river":
|
|
river_water = True
|
|
break
|
|
river_idx = river_idx + 1
|
|
|
|
if river_water == True:
|
|
|
|
# We'll go through all layers and "shavve off" excess.
|
|
# This way we can hopefully correct incorrect line and polygon renders.
|
|
for l in range(0, len(layers)):
|
|
if l < river_idx and l != ntrl_idx: # <- We don't want to load the target layer itself
|
|
lyrpix = layers[l].load()
|
|
wtr_pix = layers[river_idx].load()
|
|
for y in range(0, self._tile.height):
|
|
for x in range(0, self._tile.width):
|
|
lp = lyrpix[x,y]
|
|
wp = wtr_pix[x,y]
|
|
if lp[3] > 0:
|
|
a = 255 - lp[3]
|
|
if a < wp[3]:
|
|
c = (wp[0], wp[1], wp[2], a)
|
|
wtr_pix[x,y] = c
|
|
|
|
return layers
|
|
|
|
|
|
# This checks the final image for empty patches. Should one be
|
|
# found, we will generate something to fill the gap. If this is
|
|
# the case, we will also note this in the database for the tile,
|
|
# under the special tag and value "tile", "completion". The same
|
|
# conditions apply for edge testing and so on.
|
|
def checkForEmptySpace(self):
|
|
empty = False
|
|
|
|
# Load photo
|
|
layer_pix = self._tile.load()
|
|
|
|
# Scan!
|
|
for y in range(self._tile.width-1):
|
|
for x in range(self._tile.height-1):
|
|
p = layer_pix[x,y]
|
|
if p[3] < 255: # <- Check for empty or non-complete alpha
|
|
empty = True
|
|
break
|
|
|
|
# Tell about findings
|
|
return empty
|
|
|
|
|
|
# This returns a mask of the empty space to cover, should there be any
|
|
def buildCompletionMask(self):
|
|
mask = Image.new("RGBA", (self._imgsize, self._imgsize), (0,0,0,0))
|
|
mask_pix = mask.load()
|
|
|
|
# Load photo
|
|
layer_pix = self._tile.load()
|
|
|
|
# Scan!
|
|
for y in range(self._tile.width-1):
|
|
for x in range(self._tile.height-1):
|
|
p = layer_pix[x,y]
|
|
if p[3] < 255: # <- Check for empty or non-complete alpha
|
|
mask_pix[x,y] = (0,0,0,255)
|
|
# We do not apply any blur or other effects here - we only want the
|
|
# exact pixel positions.
|
|
|
|
#mask.save( mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_tile-completion.png" )
|
|
mstr_msg("photogen", "Generated and saved empty space mask")
|
|
return mask
|
|
|
|
|
|
# Construct a folder name for latitude and longitude
|
|
def latlng_folder(self, numbers):
|
|
fstr = ""
|
|
if numbers[0] >= 0: fstr = "+"
|
|
if numbers[0] < 0: fstr = "-"
|
|
if abs(numbers[0]) < 10: fstr = fstr + "0" + str(numbers[0])
|
|
if abs(numbers[0]) >= 10 and numbers[0] <= 90: fstr = fstr + str(numbers[0])
|
|
|
|
if numbers[1] >= 0: fstr = fstr + "+"
|
|
if numbers[1] < 0: fstr = fstr + "-"
|
|
if abs(numbers[1]) < 10: fstr = fstr + "00" + str(numbers[1])
|
|
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
|
|
|
|
|
|
# Find the next "by-ten" numbers for the current latitude and longitude
|
|
def find_earthnavdata_number(self):
|
|
earthnavdata = []
|
|
lat = abs(int(self._lat / 10) * 10)
|
|
lng = abs(int(self._lng / 10) * 10)
|
|
earthnavdata.append(lat)
|
|
earthnavdata.append(lng)
|
|
return earthnavdata |