diff --git a/defines.py b/defines.py index 0e5e024..7e411be 100644 --- a/defines.py +++ b/defines.py @@ -46,7 +46,7 @@ mstr_clear_cache = True # Whether or not you want to see progress of the tool as it walks on. # High recommendation to leave this on. -mstr_show_log = False +mstr_show_log = True # Should a pseudo shadow be rendered on certain elements? diff --git a/maskgen.py b/maskgen.py index 12f1061..43c9117 100644 --- a/maskgen.py +++ b/maskgen.py @@ -47,7 +47,10 @@ class mstr_maskgen: def project_pixel(self, pnt, edge): pdiff = edge - pnt byT = pdiff * 1000 - divisor = byT / 16 + divisor = 1.0 + # Divisor depending on zoom level + if self._zoomlevel == mstr_zl_16: divisor = byT / 64 + if self._zoomlevel == mstr_zl_18: divisor = byT / 16 return divisor diff --git a/orthographic.py b/orthographic.py index 929bf95..5c29ebd 100644 --- a/orthographic.py +++ b/orthographic.py @@ -22,6 +22,7 @@ from maskgen import * from layergen import * from photogen import * from tileprep import * +from perlin import * from xp_scenery import * @@ -373,9 +374,15 @@ class mstr_orthographic: mlng = 1 while bb_lat < self._lat + 1: bb_lat = bb_lat + self._vstep + if bb_lat >= self._lat + 1: + bb_lat = bb_lat - self._zoomlevel + break mlat = mlat+1 while bb_lng < self._long + 1: bb_lng = bb_lng + self._zoomlevel + if bb_lng >= self._long + 1: + bb_lng = bb_lng - self._zoomlevel + break mlng = mlng+1 mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng)) maxlatlng = [ mlat, mlng ] @@ -411,6 +418,7 @@ class mstr_orthographic: for lng_grid in range(1, maxlatlng[1]+1): # Adjust bounding box osmxml = mstr_osmxml() + osmxml.setLatLngFld(self._latlngfld) osmxml.adjust_bbox(bb_lat, bb_lng, bb_lat_edge, bb_lng_edge) osmxml.acquire_osm(lat_grid, lng_grid) mstr_msg("orthographic", "Adjusted bounding box for XML object") @@ -421,27 +429,30 @@ class mstr_orthographic: curlyr = 1 for layer in layers: if layer[2] == False and layer[0] != "building": - # Let the user know - mstr_msg("orthographic", "Processing layer " + str(curlyr) + " of " + str(len(layers))) + fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/contour/contour_" + layer[0] + "_" + layer[1] + ".png" + if os.path.isfile(fn) == False: + # Let the user know + mstr_msg("orthographic", "Processing layer " + str(curlyr) + " of " + str(len(layers))) - # Generate the mask - mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2]) - mask = mg._build_mask(osmxml, is_prep=True) # We need an object here + # Generate the mask + mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2]) + mask = mg._build_mask(osmxml, is_prep=True) # We need an object here - mask = mask.resize((80,80), Image.Resampling.BILINEAR) - idx = 0 - for c in contours: - if c[0] == layer[0] and c[1] == layer[1]: - break - else: - idx=idx+1 - - cnx = (lng_grid-1) * 80 - cny = cnt_h - (lat_grid * 80) - contours[idx][2].alpha_composite(mask, dest=(cnx,cny)) + mask = mask.resize((80,80), Image.Resampling.BILINEAR) + idx = 0 + for c in contours: + if c[0] == layer[0] and c[1] == layer[1]: + break + else: + idx=idx+1 + + cnx = (lng_grid-1) * 80 + cny = cnt_h - (lat_grid * 80) + contours[idx][2].alpha_composite(mask, dest=(cnx,cny)) + contours[idx][2].save(fn) - #tp = mstr_tileprep(self._lat, self._long, lat_grid, lng_grid, layer[0], layer[1], mask, False) - #tp._determineEdges() + #tp = mstr_tileprep(self._lat, self._long, lat_grid, lng_grid, layer[0], layer[1], mask, False) + #tp._determineEdges() curlyr = curlyr+1 @@ -454,7 +465,8 @@ class mstr_orthographic: if cur_tile_x > top_lng: top_lng = cur_tile_x - totaldata = glob.glob("D:/Developer/webserver/htdocs/server/osm/*.xml") + + totaldata = glob.glob(mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/osm/*.xml") progress = int( (len(totaldata) / total_tiles) * 100 ) if progress >= nextstep: @@ -475,13 +487,20 @@ class mstr_orthographic: # Store all contour images - for c in contours: - c[2].save(mstr_datafolder + "_cache/contour_" + c[0] + "_" + c[1] + ".png") - mstr_msg("orthographic", "Saved contour images for each OSM tag") + #for c in contours: + # c[2].save(mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/contour/contour_" + c[0] + "_" + c[1] + ".png") + #mstr_msg("orthographic", "Saved contour images for each OSM tag") - # We now need to "raytrace" the resources for correct placement - mstr_msg("orthographic", "Performing resource plamement tracing for tile") + # Genertate all perlin noise maps + for l in mstr_ortho_layers: + mstr_important_msg("orthographic", "Generating Perlin map for " + l[0] + ":" + l[1]) + prln = mstr_perlin(l[0], l[1], mlat, mlng, 16) + pmap = prln._generatePerlinMap() + fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/perlin/perlin_" + l[0] + "_" + l[1] + ".png" + pmap.save(fn) + + # Let's set up an array which keeps track of all used tiles, per resource """ diff --git a/osmxml.py b/osmxml.py index 7c4c150..c5359f4 100644 --- a/osmxml.py +++ b/osmxml.py @@ -30,6 +30,12 @@ class mstr_osmxml: self._lng = 0 self._curB_lat = 0 self._curB_lng = 0 + self._latlngfld = "" + + + # Set LatLngFolder + def setLatLngFld(self, latlngfld): + self._latlngfld = latlngfld # Adjust bbox for when this class should persost, but acquire data for a different bbox @@ -66,7 +72,7 @@ class mstr_osmxml: # We switched to a local storage model to ease load on the server. We will check if we already have the data we need. # If not, go an acquire it. - fn = mstr_datafolder + "z_orthographic/data/+++++/osm/" + str(self._lat) + "-" + str(v) + "_" + str(self._lng) + "-" + str(h) + ".xml" + fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/osm/" + str(int(self._lat)) + "-" + str(v) + "_" + str(int(self._lng)) + "-" + str(h) + ".xml" if os.path.isfile(fn) == False: # We will use our self-hosted API for this. parse = False @@ -110,7 +116,9 @@ class mstr_osmxml: # Check if the DOM object has a document element if dom.documentElement: # Store the content in memory - self._xmlcontent = r.content + with open(fn, encoding="utf8") as f: + contents = f.read() + self._xmlcontent = contents self._xmldata = xml.dom.minidom.parseString(self._xmlcontent) self._xmlcontent = "" # Clear diff --git a/perlin.py b/perlin.py new file mode 100644 index 0000000..a141f88 --- /dev/null +++ b/perlin.py @@ -0,0 +1,144 @@ + +# ------------------------------------------------------------------- +# 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 +# ------------------------------------------------------------------- +# perlin.py +# Generates a perlin noise image which is used to determine how which +# resource looks. +# ------------------------------------------------------------------- + +from PIL import Image # Depends on the Pillow lib +from random import randrange +import random +import numpy as np +from perlin_numpy import ( + generate_perlin_noise_2d, generate_fractal_noise_2d +) + +class mstr_perlin: + + def __init__(self, tag, value, mlat, mlng, zl): + self._tag = tag + self._value = value + self._mlat = mlat + self._mlng = mlng + + self._zl = zl + self._mapscale = 0 + if zl == 16: self._mapscale = 80 + if zl == 18: self._mapscale = 20 + + # Define some base colors + # tag, value, base color, perlin noise color palette + self._bc = [ + # Amenity + ["amenity", "parking", (31,32,34,255), [ (31,32,34), (74,74,73), (56,55,55), (34,40,44) ]], + ["amenity", "school", (26,26,26,255), [ (26,26,26), (78,78,78), (28,29,25), (86,74,64) ]], + # Barrier + ["barrier", "hedge", (27,37,25,255), [ (27,37,25), (8,14,13), (15,20,23), (40,40,38) ]], + # Boundary + ["boundary", "administrative", (50,49,46,255), [ (50,49,46), (32,38,34), (9,14,18), (82,80,76) ]], + # Landuse + ["landuse", "cemetery", (22,22,20,255), [ (22,22,20), (44,43,40), (31,33,29), (39,37,34) ]], + ["landuse", "construction", (65,51,32,255), [ (65,51,32), (77,73,67), (45,49,43), (74,63,48) ]], + ["landuse", "farmland", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]], + ["landuse", "farmyard", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]], + ["landuse", "forest", (0,30,10,255), [ (0,30,10), (18,23,16), (15,23,24), (24,29,27) ]], + ["landuse", "grass", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]], + ["landuse", "greenfield", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]], + ["landuse", "meadow", (28,29,27,255), [ (28,29,27), (36,42,40), (45,44,41), (21,22,20) ]], + ["landuse", "military", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]], + ["landuse", "orchard", (27,38,27,255), [ (27,38,27), (19,22,16), (40,42,33), (40,50,45) ]], + ["landuse", "recreation_ground", (27,38,27,255), [ (27,38,27), (19,22,16), (40,42,33), (40,50,45) ]], + ["landuse", "residential", (50,49,46,255), [ (50,49,46), (32,38,34), (9,14,18), (82,80,76) ]], + ["landuse", "residential-boundary", (50,49,46,255), [ (50,49,46), (32,38,34), (9,14,18), (82,80,76) ]], + ["landuse", "vineyard", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]], + # Leisure + ["leisure", "dog_park", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]], + ["leisure", "garden", (39,39,32,255), [ (39,39,32), (38,47,43), (35,41,31), (21,25,18) ]], + ["leisure", "golf_course", (29,39,28,255), [ (29,39,28), (86,81,76), (60,55,45), (13,18,14) ]], + ["leisure", "green", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]], + ["leisure", "nature_reserve", (0,30,10,255), [ (0,30,10), (18,23,16), (15,23,24), (24,29,27) ]], + ["leisure", "park", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]], + ["leisure", "pitch", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]], + ["leisure", "playground", (65,51,32,255), [ (65,51,32), (77,73,67), (45,49,43), (74,63,48) ]], + ["leisure", "sports_centre", (66,60,49,255), [ (66,60,49), (23,32,27), (14,18,20), (45,43,39) ]], + ["leisure", "swimming_pool", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]], + # Natural + ["natural", "bare_rock", (47,54,56,255), [ (47,54,56), (79,75,71), (84,51,43), (26,33,45) ]], + ["natural", "beach", (79,76,69,255), [ (79,76,69), (82,83,80), (71,61,48), (69,68,66) ]], + ["natural", "desert", (79,64,42,255), [ (79,64,42), (79,47,24), (37,41,27), (51,39,23) ]], + ["natural", "grassland", (23,24,22,255), [ (23,24,22), (40,27,35), (32,34,30), (54,53,51) ]], + ["natural", "heath", (26,28,23,255), [ (26,28,23), (45,44,42), (30,34,29), (45,45,42) ]], + ["natural", "sand", (79,76,69,255), [ (79,76,69), (82,83,80), (71,61,48), (69,68,66) ]], + ["natural", "scree", (38,36,33,255), [ (38,36,33), (81,80,76), (43,42,31), (37,34,27) ]], + ["natural", "scrub", (41,39,35,255), [ (41,39,35), (44,38,34), (30,32,39), (20,27,22) ]], + ["natural", "water", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]], + ["natural", "wetland", (32,34,33,255), [ (32,34,33), (5, 8, 8), (35,36,35), (17,19,17) ]], + ["natural", "wood", (0,30,10,255), [ (0,30,10), (18,23,16), (15,23,24), (24,29,27) ]], + # Water + ["water", "lake", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]], + ["water", "pond", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]], + ["water", "river", (9,13,18,255), [ (9,13,18), (9,15,21), (11,12,12), (22,28,31) ]] + ] + + + # Find base color depending on tag/value + def _findBaseColor(self): + idx = -1 + for b in range(len(self._basecolors)): + if self._bc[b][0] == self._tag and self._bc[b][1] == self._value: + idx = b + break + return idx + + + # Generates a Perlin map depending on what we need + def _generatePerlinMap(self): + idx = self._findBaseColor() + + bw = self._mlng * self._mapscale + bh = self._mlat * self._mapscale + wh = 2048 + base = Image.new("RGBA", (bw,bh), self._bc[idx][2]) + + for c in range(len(self._bc[idx][3])): + np.random.seed(randrange(10000000, 100000000)) + noise1 = generate_fractal_noise_2d((wh,wh), (16,16), 5) + np.random.seed(randrange(10000000, 100000000)) + noise2 = generate_fractal_noise_2d((wh,wh), (8,8), 4) + + im1 = Image.new("RGBA", (wh,wh)) + im2 = Image.new("RGBA", (wh,wh)) + + for y in range(0, wh): + for x in range(0, wh): + n = (noise1[y][x] + 1) / 2 + v = ( + self._bc[idx][3][c][0], + self._bc[idx][3][c][1], + self._bc[idx][3][c][2], + int(n*255) + ) + im1.putpixel((x,y), v) + + for y in range(0, wh): + for x in range(0, wh): + n = (noise2[y][x] + 1) / 2 + v = ( + self._bc[idx][3][c][0], + self._bc[idx][3][c][1], + self._bc[idx][3][c][2], + int(n*255) + ) + im2.putpixel((x,y), v) + + im = Image.blend(im1,im2, alpha=0.5) + base.alpha_composite(im) + + # Provide the perlin map + return base \ No newline at end of file