orthographic/maskgen.py

187 lines
7.2 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
# -------------------------------------------------------------------
# maskgen.py
# The class that generates a mask of the layer it was asked to do.
# This mask will then be used to generate a photo layer, which in
# turn is then used to construct the final photo. It can be argued
# that this part of the code is the most crucial one, as the other
# classes involved rely on what this code is doing, and by extension,
# generating.
#
# The PNG generated will be used in this progression:
# - Generate mask from OSM (here)
# - Generate colored photo layer from this mask, for example for
# landuse: forest
# - Compile actual satellite aerial
# -------------------------------------------------------------------
import math
from osmxml import *
from defines import *
from log import *
from PIL import Image, ImageFilter, ImageDraw, ImagePath
from random import randrange
import random
class mstr_maskgen:
# Initializes the class with some required variables
# Much of this code is adjusted to work within a class.
def __init__(self, box, vstep, tag, value, isline, subtag=None, subvalue=None):
self._box = box
self._tag = tag
self._subtag = subtag
self._subvalue = subvalue
self._value = value
self._vstep = vstep
self._scale = 1 / math.cos(math.radians(self._box[0]))
self._isline = isline
#mstr_msg("maskgen", "Intialized mask gen.")
# Projects a point into the canvas of the mask.
# Final projection depends on positive or negative latitude or longitude.
def project_pixel(self, pnt, edge):
pdiff = edge - pnt
byT = pdiff * 1000
divisor = byT / 16
return divisor
# Extract lat/lng from custom extracted nodes block
def latlong_from_id(self, id, nds):
latlng = []
for i in nds:
if i[0] == id:
#latlng.append((float(i[1]), float(i[2])))
latlng.append(float(i[1]))
latlng.append(float(i[2]))
break
return latlng
# Builds the required mask
def _build_mask(self):
# Generate empty image
imgsize = 0
if mstr_photores == 2048: imgsize=3000
if mstr_photores == 4096: imgsize=6000
mask_img = Image.new("RGBA", (imgsize, imgsize))
tilexml = mstr_datafolder + "_cache\\tile.xml"
xml = mstr_osmxml(0,0)
fstr = str(self._box[0]) + "-" + str(self._box[1]) + "_" + str(self._box[2]) + "-" + str(self._box[3])
nds = xml.acquire_nodes(tilexml)
way = xml.acquire_waypoint_data(tilexml)
rls = xml.acquire_relations(tilexml)
mstr_msg("maskgen", "Building mask for " + str(self._box[0]) + "-" + str(self._box[1]) + ", " + str(self._box[2]) + "-" + str(self._box[3]) + ", for " + self._tag + ": " + self._value )
frs = []
# Calculate actual bounding box
bbox = []
# Latitude
bbox.append(self._box[0] + ((self._box[1]-1) * self._vstep))
bbox.append(self._box[0] + ((self._box[1]-1) * self._vstep) + self._vstep)
# Longitude
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)
# Generate mask for ONE tag only
if self._subtag == None:
for w in way:
if w[2] == self._tag and w[3] == self._value:
nd = []
for d in way:
if d[0] == w[0]:
nd.append(d[1])
frs.append(nd)
# Scout through relations as these also make up map data
for r in rls:
if self._tag in r[1] and self._value in r[1]:
nd = []
for w in way:
if int(w[0]) == int(r[0]):
nd.append(w[1])
frs.append(nd)
# Generate mask for one tag, PLUS a subtag. This is mostly used for admin areas
if self._subtag != None:
nd = []
wids = []
for w in way:
if w[2] == self._tag and w[3] == self._value:
wids.append(w[0])
for w in wids:
for wp in way:
if wp[0] == w and wp[2] == self._subtag and wp[3] in self._subvalue:
for d in way:
if d[0] == wp[0] and d[1] != "NULL":
nd.append(d[1])
frs.append(nd)
# Project all pixels
for f in frs:
pts = []
for a in f:
latlng = self.latlong_from_id(a, nds)
if len(latlng) == 2:
# For some reason, sometimes the array is empty. Make sure we have two data points.
if len(latlng) == 2:
# Project the pixel, and add to the polygon shape.
p_lat = self.project_pixel(latlng[0], bbox[1])
p_lng = self.project_pixel(latlng[1], bbox[3])
pixlat = 0
pixlng = 0
pr = 0
if mstr_photores == 2048: pr = 3000
if mstr_photores == 4096: pr = 6000
# Draw pixels in direction according to latitude and longitude positions -
# Latitude:
if self._box[0] > 0:
pixlat = int((imgsize*self._scale)*p_lat)
if self._box[0] < 0:
pixlat = pr - (int((imgsize*self._scale)*p_lat))
# Longitude:
if self._box[2] > 0:
pixlng = int(imgsize - (imgsize*p_lng))
if self._box[2] < 0:
pixlng = pr - (int(imgsize - (imgsize*p_lng)))
pts.append((pixlng, pixlat))
# Corel Draw!
imgd = ImageDraw.Draw(mask_img)
# Draw polygons for everything except those three tags
if self._isline == False:
if len(pts) >= 3:
imgd.polygon(pts, fill="#000000")
# For road specific items, draw lines instead
if self._isline == True:
if len(pts) >= 2: # Only need two points to form a line
idx = 0
for i in range(len(mstr_ortho_layers)):
if mstr_ortho_layers[i][0] == self._tag and mstr_ortho_layers[i][1] == self._value:
idx = i
break
imgd.line(pts, fill="#000000", width=mstr_ortho_layers[idx][2], joint="curve")
# Save image
mask_img.save(mstr_datafolder + "_cache\\" + fstr + "_" + self._tag + "-" + self._value + ".png")
# Inform
mstr_msg("maskgen", "Mask built.")