mstr_osm_endpoint = "https://marstr.online/osm/v1/"\r
\r
# Define the texture resolution you want to have your photos at.\r
-mstr_photores = 2048 # <- Change this to 4096 for 16k resolution\r
+mstr_photores = 2048\r
\r
# Radius of zoom level 18 aerials around airports with ICAO code\r
# Value is in tiles - not in km\r
# You can, however, disable the shadow rendering layer here.\r
mstr_shadow_enabled = True\r
mstr_shadow_strength = 0.65\r
-mstr_shadow_shift = 15\r
+mstr_shadow_shift = 16\r
+mstr_shadow_floor_h = 2.8 # 2.5m ceiling height + 30cm concrete per floor\r
# The tags that cast shadows\r
mstr_shadow_casters = [\r
("landuse", "forest"),\r
("leisure", "nature_reserve"),\r
- ("natural", "wood"),\r
- ("natural", "tree_row"),\r
- ("building", "semidetached_house"),\r
- ("building", "apartments"),\r
- ("building", "garage"),\r
- ("building", "office"),\r
- ("building", "retail"),\r
- ("building", "industrial"),\r
- ("building", "yes")\r
+ ("natural", "wood")\r
]\r
\r
\r
("water", "river", 10),\r
("leisure", "swimming_pool", 10),\r
("waterway", "river", 10),\r
- ("waterway", "stream", 2),\r
+ ("waterway", "stream", 4),\r
("amenity", "parking", 1),\r
("amenity", "school", 1),\r
("highway", "pedestrian", 12),\r
# Z-Order 4\r
- ("highway", "motorway", 2),\r
- ("highway", "primary", 2),\r
- ("highway", "secondary", 2),\r
- ("highway", "tertiary", 2),\r
- ("highway", "unclassified", 2),\r
- ("highway", "living_street", 2),\r
- ("highway", "residential", 2),\r
- ("highway", "service", 2),\r
+ ("highway", "motorway", 1),\r
+ ("highway", "primary", 1),\r
+ ("highway", "secondary", 1),\r
+ ("highway", "tertiary", 1),\r
+ ("highway", "unclassified", 1),\r
+ ("highway", "living_street", 1),\r
+ ("highway", "residential", 1),\r
+ ("highway", "service", 1),\r
("highway", "footway", 2),\r
("highway", "track", 2),\r
("highway", "path", 2),\r
]\r
),\r
("yes", [\r
- "#693333", "#592b2b", "#513434", "#4a1e1e", "#362626",\r
- "#534136", "#4d3424", "#534b45", "#553724", "#574c45",\r
"#373942", "#40424a", "#363b4f", "#2c2d32", "#444651",\r
"#454545", "#39393b", "#4b4b4c", "#363638", "#525252"\r
]\r
brd_src = Image.open(root_folder + "/brd/b" + str(src) + ".png")\r
ptc_src = []\r
for p in ptc:\r
- ptc_src.append(Image.open(p))\r
+ pimg = Image.open(p)\r
+ pimg = pimg.rotate(randrange(0, 360), expand=True)\r
+ ptc_src.append(pimg)\r
mstr_msg("layergen", "Border sources selected")\r
\r
# Begin producing a largely random image\r
- samples = 250 # <- We need this in a moment\r
+ samples = 250 # <- We need this in a moment\r
for i in range(samples):\r
imgid = 0\r
if len(ptc_src) == 1: imgid = 0\r
\r
# Add trees only in some features\r
if (self._tag == "landuse" and self._value == "cemetery") or (self._tag == "landuse" and self._value == "residential") or (self._tag == "leisure" and self._value == "park"):\r
+ trees = Image.new("RGBA", (self._imgsize, self._imgsize))\r
amt = 3500\r
for i in range(1, amt+1):\r
- p = randrange(1, 11)\r
+ p = randrange(1, 16)\r
tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png")\r
lx = randrange( self._imgsize - tree.width ) \r
ly = randrange( self._imgsize - tree.height )\r
- layer.alpha_composite(tree, (lx, ly))\r
+ trees.alpha_composite(tree, (lx, ly))\r
+ \r
+ tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize))\r
+ tree_pix = trees.load()\r
+ shadow_pix = tree_shadow.load()\r
+ for y in range(self._imgsize):\r
+ for x in range(self._imgsize):\r
+ tp = tree_pix[x,y]\r
+ if tp[3] > 0:\r
+ sc = (0,0,0,180)\r
+ if x+8 < self._imgsize and y+5 < self._imgsize:\r
+ shadow_pix[x+8,y+5] = sc\r
+ tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2))\r
+ tree_shadow.alpha_composite(trees)\r
+ layer.alpha_composite(tree_shadow)\r
\r
mstr_msg("layergen", "Layer image completed")\r
\r
if self._tag == "landuse" and self._value == "forest":\r
# The residential layer MUST exist before we reach the forest part.\r
fn = mstr_datafolder + "_cache/" + str(self._latitude) + "-" + str(self._lat_number) + "_" + str(self._longitude) + "-" + str(self._lng_number) + "_landuse-residential_layer.png"\r
- if os.path.isfile(fn) == True:\r
+ if os.path.isfile(fn):\r
rsd = Image.open(fn)\r
- layer_comp.alpha_composite(rsd)\r
+ rsd_pix = rsd.load()\r
+ forest_layer = Image.new("RGBA", (self._imgsize, self._imgsize))\r
+ for_pix = forest_layer.load()\r
+ for y in range(self._imgsize):\r
+ for x in range(self._imgsize):\r
+ rpix = rsd_pix[x,y]\r
+ lpix = layer_comp_pix[x,y]\r
+ if rpix[3] > 0 and lpix[3] > 0:\r
+ for_pix[x,y] = (rpix[0], rpix[1], rpix[2], rpix[3])\r
+ layer_comp.alpha_composite(forest_layer)\r
+ #if os.path.isfile(fn) == True:\r
+ # rsd = Image.open(fn)\r
+ # layer_comp.alpha_composite(rsd)\r
\r
# Store layer\r
if self._is_completion == False:\r
mstr_msg("layergen", "Generating shadow for layer")\r
shadow_pix = shadow.load()\r
mask_pix = osm_mask.load()\r
+ shf = 1\r
+ while shf < mstr_shadow_shift:\r
+ for y in range(self._imgsize):\r
+ for x in range(self._imgsize):\r
+ mp = layer_comp_pix[x,y]\r
+ if mp[3] == 255:\r
+ if x+(shf*2) < self._imgsize and y+shf < self._imgsize:\r
+ rndshd = randrange(5, 210)\r
+ shadow_pix[x+(shf*2), y+shf] = (0,0,0,rndshd)\r
+ shf = shf+1\r
+ \r
+ # Tree removal\r
+ for y in range(self._imgsize):\r
+ for x in range(self._imgsize):\r
+ lp = layer_comp_pix[x,y]\r
+ if lp[3] >= 250:\r
+ shadow_pix[x,y] = (0,0,0,0)\r
+\r
+ shadow = shadow.filter(ImageFilter.GaussianBlur(radius=2))\r
+\r
+ """\r
for y in range(self._imgsize-1):\r
for x in range(self._imgsize-1):\r
m = mask_pix[x,y]\r
shf_x = 0\r
shf_x = x + mstr_shadow_shift\r
- if shf_x <= self._imgsize-1:\r
+ if shf_x < self._imgsize:\r
a = mask_pix[x,y][3]\r
st = 0\r
st = random.uniform(0.45, mstr_shadow_strength)\r
ca = a * st\r
aa = int(ca)\r
shadow_pix[shf_x, y] = (0,0,0,aa)\r
+ """\r
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")\r
mstr_msg("layergen", "Shadow layer completed")\r
\r
#self._tiledb.close_db()\r
\r
# Create a water mask we need to remove from the DDS later\r
- 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"):\r
+ 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"):\r
mstr_msg("layergen", "Generating inland water mask")\r
inl_mask = Image.new("RGBA", (self._imgsize, self._imgsize), (0,0,0,0))\r
lyr_pix = layer_comp.load()\r
if i[0] == self._tag and i[1] == self._value:\r
osm_mask = osm_mask.filter(ImageFilter.BoxBlur(radius=i[2]))\r
break\r
- osm_edge = osm_edge.filter(ImageFilter.BoxBlur(radius=1))\r
\r
\r
# And now for the Big Mac.\r
d = randrange(41, 61)\r
layer_comp_pix[x, y] = ( d,d,d,a[3] )\r
if self._tag == "highway" and self._value != "motorway":\r
- d = randrange(85, 101)\r
- layer_comp_pix[x, y] = ( d,d,d,a[3] )\r
+ dr = randrange(110,121)\r
+ dg = randrange(110,121)\r
+ db = randrange(115,130)\r
+ layer_comp_pix[x, y] = ( dr,dg,db,a[3] )\r
if self._tag == "highway" and self._value == "motorway":\r
- d = randrange(1,20)\r
- r = 86-d\r
- g = 97-d\r
- b = 106-d\r
- layer_comp_pix[x, y] = ( r,g,b,a[3] )\r
+ dr = randrange(77,89)\r
+ dg = randrange(88,96)\r
+ db = randrange(90,101)\r
+ layer_comp_pix[x, y] = ( dr,dg,db,a[3] )\r
if self._tag == "waterway" and (self._value == "stream" or self._value == "river"):\r
d = randrange(1, 15)\r
# Rock, grass, water\r
t = a[3]-d\r
if t < 0: t = 0\r
if e[3] > 0:\r
- layer_comp_pix[x, y] = ( mats[pick-1][0], mats[pick-1][1], mats[pick-1][2], t )\r
+ layer_comp_pix[x, y] = ( mats[pick-1][0], mats[pick-1][1], mats[pick-1][2], 35 )\r
\r
# A bit special here\r
if self._tag == "building":\r
\r
# A bit different for tree rows\r
if self._tag == "natural" and self._value == "tree_row":\r
+ trees = Image.new("RGBA", (self._imgsize, self._imgsize))\r
for t in range(20001):\r
lx = randrange(self._imgsize)\r
ly = randrange(self._imgsize)\r
a = mask_pix[lx,ly]\r
if a[3] > 0:\r
if lx < self._imgsize and ly < self._imgsize:\r
- p = randrange(1,11)\r
+ p = randrange(1,16)\r
tree = Image.open(mstr_datafolder + "textures/building/area/p" + str(p) + ".png")\r
- layer_comp.alpha_composite(tree, (lx, ly))\r
+ trees.alpha_composite(tree, (lx, ly))\r
+ if mstr_shadow_enabled == True:\r
+ tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize))\r
+ tree_pix = trees.load()\r
+ shadow_pix = tree_shadow.load()\r
+ for y in range(self._imgsize):\r
+ for x in range(self._imgsize):\r
+ tp = tree_pix[x,y]\r
+ if tp[3] > 0:\r
+ rndshd = randrange(5, 210)\r
+ sc = (0,0,0,rndshd)\r
+ if x+8 < self._imgsize and y+5 < self._imgsize:\r
+ shadow_pix[x+8,y+5] = sc\r
+ tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2))\r
+ tree_shadow.alpha_composite(trees)\r
+ layer_comp.alpha_composite(tree_shadow)\r
\r
# We will do some super magic here to let houses look more realistic\r
if self._tag == "building":\r
numtrees = randrange(1, 16)\r
for i in range(1, numtrees+1):\r
# Pick some file\r
- pick = str(randrange(1, 11))\r
+ pick = str(randrange(1, 16))\r
tree = Image.open(mstr_datafolder + "textures/building/area/p" + pick + ".png")\r
# Do a correction for the location if needed\r
if shf_x < 1: shf_x = 1\r
if shf_x > self._imgsize - tree.width: shf_x = self._imgsize - tree.width - 1\r
if shf_y > self._imgsize - tree.height: shf_y = self._imgsize - tree.height - 1\r
trees.alpha_composite(tree, (shf_x, shf_y))\r
- trees.alpha_composite(layer_comp)\r
- layer_comp = trees\r
\r
- \r
+ if mstr_shadow_enabled == True:\r
+ tree_shadow = Image.new("RGBA", (self._imgsize, self._imgsize))\r
+ tree_pix = trees.load()\r
+ shadow_pix = tree_shadow.load()\r
+ for y in range(self._imgsize):\r
+ for x in range(self._imgsize):\r
+ tp = tree_pix[x,y]\r
+ if tp[3] > 0:\r
+ sc = (0,0,0,180)\r
+ if x+8 < self._imgsize and y+5 < self._imgsize:\r
+ shadow_pix[x+8,y+5] = sc\r
+ tree_shadow = tree_shadow.filter(ImageFilter.GaussianBlur(radius=2))\r
+ tree_shadow.alpha_composite(trees)\r
+\r
+ # Save this separately, so that we can blur buildings, but not the trees\r
+ 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"\r
+\r
+ if os.path.isfile(fn) == True:\r
+ extrees = Image.open(fn)\r
+ extrees.alpha_composite(tree_shadow)\r
+ extrees.save(fn)\r
+ else:\r
+ tree_shadow.save(fn)\r
+\r
+ #layer_comp.alpha_composite(tree_shadow)\r
+ #tree_shadow.alpha_composite(layer_comp)\r
+ #layer_comp = tree_shadow\r
+\r
mstr_msg("layergen", "Layer image generated")\r
\r
# Building shadow\r
if mstr_shadow_enabled == True:\r
- if self._tag == "building":\r
- mstr_msg("layergen", "Generating shadow for layer")\r
- shadow = Image.new("RGBA", (self._imgsize, self._imgsize))\r
- shadow_pix = shadow.load()\r
- mask_pix = osm_mask.load()\r
- for y in range(self._imgsize-1):\r
- for x in range(self._imgsize-1):\r
- m = mask_pix[x,y]\r
- shf_x = x + mstr_shadow_shift\r
- shf_y = y + (mstr_shadow_shift/2)\r
- if shf_x < self._imgsize and shf_y < self._imgsize:\r
- a = mask_pix[x,y][3]\r
- st = random.uniform(0.3, mstr_shadow_strength)\r
- ca = a * st\r
- aa = int(ca)\r
- shadow_pix[shf_x, shf_y] = (0,0,0,aa)\r
- shadow = shadow.filter(ImageFilter.GaussianBlur(radius=2))\r
- 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")\r
- mstr_msg("layergen", "Shadow layer completed")\r
\r
# Some funnies with shadows\r
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"):\r
# Highways and runways of any kind get some special treatment\r
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"):\r
# We will now add some white lines for coolness\r
+ osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)\r
mask_pix = osm_edge.load()\r
layer_comp_pix = layer_comp.load()\r
for y in range(self._imgsize):\r
layer_comp_pix[x, y] = ( w,w,w,a[3] )\r
\r
if self._tag == "highway" and self._value == "residential":\r
+ osm_edge = osm_mask.filter(ImageFilter.FIND_EDGES)\r
mask_pix = osm_edge.load()\r
layer_comp_pix = layer_comp.load()\r
for y in range(self._imgsize):\r
for x in range(self._imgsize):\r
if mask_pix[x, y][3] > 0:\r
# Find a suitable color\r
- w = randrange(60, 96)\r
+ w = randrange(150,181)\r
a=mask_pix[x,y]\r
layer_comp_pix[x, y] = ( w,w,w,a[3] )\r
mstr_msg("layergen", "Street lines added")\r
from log import *\r
from PIL import Image, ImageFilter, ImageDraw, ImagePath\r
from random import randrange\r
+from functions import *\r
import random\r
\r
class mstr_maskgen:\r
latlng.append(float(i[2]))\r
break\r
return latlng\r
- \r
\r
- # Only needed if X-Plane scenery is built\r
- def _set_xpscenery_datagroup(self, dg):\r
- self._xpdg = dg\r
+\r
+ # Set width of tile - for buildings\r
+ def set_tile_width(self, tile_width):\r
+ self._tile_width = tile_width\r
+\r
+ # Numbers needed for the possible building shadow layer\r
+ def set_latlng_numbers(self, lat, tv, lng, th):\r
+ self._latitude = lat\r
+ self._lat_number = tv\r
+ self._longitude = lng\r
+ self._lng_number = th\r
\r
\r
# Builds the required mask\r
# Longitude\r
bbox.append(self._box[2] + ((self._box[3]-1) * mstr_zl_18))\r
bbox.append(self._box[2] + ((self._box[3]-1) * mstr_zl_18) + mstr_zl_18)\r
+\r
+ # Building levels, if this is a building\r
+ bld_levels = 0\r
\r
# Generate mask for ONE tag only\r
if self._subtag == None:\r
nd = []\r
for d in way:\r
if d[0] == w[0]:\r
+ if self._tag == "building" and bld_levels == 0:\r
+ bld_levels = xml.find_building_levels(tilexml, w[0])\r
nd.append(d[1])\r
frs.append(nd)\r
# Scout through relations as these also make up map data\r
\r
# Save image\r
mask_img.save(mstr_datafolder + "_cache/" + fstr + "_" + self._tag + "-" + self._value + ".png")\r
+\r
+ # If this is a building, we need to render the shadow here, as we only know the height\r
+ # of the building in this loop.\r
+ if mstr_shadow_enabled == True:\r
+ if self._tag == "building":\r
+ mpp = meters_per_pixel(self._tile_width) * mstr_zl_18\r
+ pix_per_floor = mstr_shadow_floor_h / mpp\r
+ total_pix = pix_per_floor * bld_levels\r
+ shift = int(total_pix)\r
+\r
+ fn = mstr_datafolder + "_cache/" + fstr + "_" + self._tag + "-" + self._value + "_layer_shadow.png"\r
+\r
+ mask_pix = mask_img.load()\r
+\r
+ bld_shadow = Image.new("RGBA", (mstr_photores, mstr_photores))\r
+ bld_shadow_pix = bld_shadow.load()\r
+\r
+ # Shadow sweep\r
+ shf = 1\r
+ while shf <= shift:\r
+ for y in range(mstr_photores):\r
+ for x in range(mstr_photores):\r
+ mp = mask_pix[x,y]\r
+ if mp[3] != 0:\r
+ if x+(shf*2) < mstr_photores and y+shf < mstr_photores:\r
+ bld_shadow_pix[x+(shf*2), y+shf] = (0,0,0,255)\r
+ shf = shf+1\r
+\r
+ # Building removal sweep\r
+ for y in range(mstr_photores):\r
+ for x in range(mstr_photores):\r
+ mp = mask_pix[x,y]\r
+ if mp[3] != 0:\r
+ bld_shadow_pix[x,y] = (0,0,0,0)\r
+\r
+\r
+ # Correct alpha\r
+ bld_shadow_pix = bld_shadow.load()\r
+ for y in range(mstr_photores):\r
+ for x in range(mstr_photores):\r
+ sp = bld_shadow_pix[x,y]\r
+ if sp[3] != 0:\r
+ bld_shadow_pix[x,y] = (0,0,0,120)\r
+\r
+ # Store\r
+ if os.path.isfile(fn) == True:\r
+ lyr = Image.open(fn)\r
+ lyr.alpha_composite(bld_shadow)\r
+ lyr.save(fn)\r
+ else:\r
+ bld_shadow.save(fn)\r
+\r
+\r
# Inform\r
mstr_msg("maskgen", "Mask built.")\r