2024-08-27 20:54:22 +02:00
|
|
|
|
|
|
|
import os
|
|
|
|
from PIL import Image, ImageFilter
|
|
|
|
from defines import *
|
|
|
|
from layergen import *
|
|
|
|
from log import *
|
|
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
|
|
# ORTHOGRAPHIC
|
|
|
|
# Your personal aerial satellite. Always on. At any altitude.*
|
|
|
|
# Developed by MarStrMind
|
2024-08-27 21:55:25 +02:00
|
|
|
# License: Open Software License 3.0
|
2024-08-27 20:54:22 +02:00
|
|
|
# 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
|
2024-09-02 08:03:45 +02:00
|
|
|
def __init__ (self, lat, lng, ty, tx, maxlat, maxlng):
|
2024-08-27 20:54:22 +02:00
|
|
|
self._lat = lat
|
|
|
|
self._lng = lng
|
|
|
|
self._ty = ty
|
|
|
|
self._tx = tx
|
2024-09-02 08:03:45 +02:00
|
|
|
self._maxlatlng = [ maxlat, maxlng ]
|
2024-08-27 20:54:22 +02:00
|
|
|
# Define layer size depending on what is wanted
|
|
|
|
self._imgsize = 0
|
|
|
|
if mstr_photores == 2048: self._imgsize = 3000
|
|
|
|
if mstr_photores == 4096: self._imgsize = 6000
|
|
|
|
# Empty image where everything goes into
|
|
|
|
self._tile = Image.new("RGBA", (self._imgsize, self._imgsize))
|
2024-09-02 08:03:45 +02:00
|
|
|
mstr_msg("photogen", "Photogen initialized")
|
2024-08-27 20:54:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
# This puts it all together. Bonus: AND saves it.
|
|
|
|
def genphoto(self):
|
|
|
|
# Template for the file name which is always the same
|
2024-09-03 20:49:35 +02:00
|
|
|
root_filename = mstr_datafolder + "/_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_"
|
2024-08-27 20:54:22 +02:00
|
|
|
|
|
|
|
# First, we walk through all layers and blend them on top of each other, in order
|
2024-09-02 08:03:45 +02:00
|
|
|
mstr_msg("photogen", "Merging layers")
|
2024-08-27 21:24:32 +02:00
|
|
|
|
2024-08-27 20:54:22 +02:00
|
|
|
for l in mstr_ortho_layers:
|
|
|
|
if os.path.isfile(root_filename + l[0] + "-" + l[1] + "_layer.png"):
|
|
|
|
# Need to divert in case we have shadows
|
|
|
|
if mstr_shadow_enabled == True:
|
|
|
|
if os.path.isfile(root_filename + l[0] + "-" + l[1] + "_layer_shadow.png"):
|
|
|
|
sn = root_filename + l[0] + "-" + l[1] + "_layer_shadow.png"
|
|
|
|
s_layer = Image.open(sn)
|
|
|
|
self._tile.alpha_composite(s_layer)
|
|
|
|
# Complete the file name based on the template
|
|
|
|
fn = root_filename + l[0] + "-" + l[1] + "_layer.png"
|
|
|
|
# Open the layer
|
|
|
|
layer = Image.open(fn)
|
|
|
|
# Converge the layer with this image
|
|
|
|
self._tile.alpha_composite(layer)
|
|
|
|
|
|
|
|
# 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()
|
2024-09-02 08:03:45 +02:00
|
|
|
mstr_msg("photogen", "Checked for empty patches")
|
2024-08-27 20:54:22 +02:00
|
|
|
|
|
|
|
# If this check comes back as true, we need to perform
|
|
|
|
# aforementioned fix:
|
|
|
|
if emptyspace == True:
|
|
|
|
# Choose a suitable layer type
|
2024-08-29 22:46:49 +02:00
|
|
|
lt = [6,14,17]
|
2024-08-27 20:54:22 +02:00
|
|
|
pick = randrange(0,len(lt)-1)
|
|
|
|
ltp = lt[pick]
|
|
|
|
tag = mstr_ortho_layers[ltp][0]
|
|
|
|
value = mstr_ortho_layers[ltp][1]
|
|
|
|
|
2024-09-02 08:03:45 +02:00
|
|
|
mstr_msg("photogen", "Patching empty space")
|
2024-08-27 20:54:22 +02:00
|
|
|
self.buildCompletionMask()
|
|
|
|
|
|
|
|
# Generate the layer as if it were part of the OSM data
|
|
|
|
lg = mstr_layergen(tag, value, self._lat, self._ty, self._lng, self._tx, False, is_completion=True)
|
2024-09-02 08:03:45 +02:00
|
|
|
lg.set_max_latlng_tile(self._maxlatlng)
|
2024-08-27 20:54:22 +02:00
|
|
|
lg.genlayer()
|
|
|
|
|
|
|
|
# Load the image
|
|
|
|
completion = Image.open(root_filename + "tile-completion_layer.png")
|
|
|
|
|
|
|
|
# Merge the images
|
|
|
|
completion.alpha_composite(self._tile)
|
|
|
|
|
|
|
|
# Make this the real one
|
|
|
|
self._tile = completion
|
|
|
|
|
|
|
|
|
|
|
|
# We are now in posession of the final image.
|
|
|
|
|
|
|
|
# Scale to correct size.
|
|
|
|
self._tile = self._tile.resize((mstr_photores, mstr_photores), Image.Resampling.BILINEAR)
|
|
|
|
|
|
|
|
# This we can save accordingly.
|
2024-09-03 20:49:35 +02:00
|
|
|
self._tile.convert('RGB').save(mstr_datafolder + "Tiles/" + str(self._lat) + "_" + str(self._lng) + "/Textures/" + str(self._ty) + "_" + str(self._tx) + ".jpg", format='JPEG', subsampling=0, quality=100)
|
2024-08-27 20:54:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
# 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))
|
|
|
|
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.
|
|
|
|
|
2024-09-03 20:49:35 +02:00
|
|
|
mask.save( mstr_datafolder + "_cache/" + str(self._lat) + "-" + str(self._ty) + "_" + str(self._lng) + "-" + str(self._tx) + "_tile-completion.png" )
|
2024-09-02 08:03:45 +02:00
|
|
|
mstr_msg("photogen", "Generated and saved empty space mask")
|