Change to ZL16, initial Perlin noise class and first steps to have Perlins generated for all tags.

This commit is contained in:
Marcus Str. 2025-01-20 21:59:27 +01:00
parent c77002b930
commit c584aa0838
5 changed files with 202 additions and 28 deletions

View File

@ -46,7 +46,7 @@ mstr_clear_cache = True
# Whether or not you want to see progress of the tool as it walks on. # Whether or not you want to see progress of the tool as it walks on.
# High recommendation to leave this on. # High recommendation to leave this on.
mstr_show_log = False mstr_show_log = True
# Should a pseudo shadow be rendered on certain elements? # Should a pseudo shadow be rendered on certain elements?

View File

@ -47,7 +47,10 @@ class mstr_maskgen:
def project_pixel(self, pnt, edge): def project_pixel(self, pnt, edge):
pdiff = edge - pnt pdiff = edge - pnt
byT = pdiff * 1000 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 return divisor

View File

@ -22,6 +22,7 @@ from maskgen import *
from layergen import * from layergen import *
from photogen import * from photogen import *
from tileprep import * from tileprep import *
from perlin import *
from xp_scenery import * from xp_scenery import *
@ -373,9 +374,15 @@ class mstr_orthographic:
mlng = 1 mlng = 1
while bb_lat < self._lat + 1: while bb_lat < self._lat + 1:
bb_lat = bb_lat + self._vstep bb_lat = bb_lat + self._vstep
if bb_lat >= self._lat + 1:
bb_lat = bb_lat - self._zoomlevel
break
mlat = mlat+1 mlat = mlat+1
while bb_lng < self._long + 1: while bb_lng < self._long + 1:
bb_lng = bb_lng + self._zoomlevel bb_lng = bb_lng + self._zoomlevel
if bb_lng >= self._long + 1:
bb_lng = bb_lng - self._zoomlevel
break
mlng = mlng+1 mlng = mlng+1
mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng)) mstr_msg("orthographic", "Max lat tile: " + str(mlat) + " - max lng tile: " + str(mlng))
maxlatlng = [ mlat, mlng ] maxlatlng = [ mlat, mlng ]
@ -411,6 +418,7 @@ class mstr_orthographic:
for lng_grid in range(1, maxlatlng[1]+1): for lng_grid in range(1, maxlatlng[1]+1):
# Adjust bounding box # Adjust bounding box
osmxml = mstr_osmxml() osmxml = mstr_osmxml()
osmxml.setLatLngFld(self._latlngfld)
osmxml.adjust_bbox(bb_lat, bb_lng, bb_lat_edge, bb_lng_edge) osmxml.adjust_bbox(bb_lat, bb_lng, bb_lat_edge, bb_lng_edge)
osmxml.acquire_osm(lat_grid, lng_grid) osmxml.acquire_osm(lat_grid, lng_grid)
mstr_msg("orthographic", "Adjusted bounding box for XML object") mstr_msg("orthographic", "Adjusted bounding box for XML object")
@ -421,27 +429,30 @@ class mstr_orthographic:
curlyr = 1 curlyr = 1
for layer in layers: for layer in layers:
if layer[2] == False and layer[0] != "building": if layer[2] == False and layer[0] != "building":
# Let the user know fn = mstr_datafolder + "z_orthographic/data/" + self._latlngfld + "/contour/contour_" + layer[0] + "_" + layer[1] + ".png"
mstr_msg("orthographic", "Processing layer " + str(curlyr) + " of " + str(len(layers))) if os.path.isfile(fn) == False:
# Let the user know
mstr_msg("orthographic", "Processing layer " + str(curlyr) + " of " + str(len(layers)))
# Generate the mask # Generate the mask
mg = mstr_maskgen( [self._lat, cur_tile_y, self._long, cur_tile_x], self._vstep, layer[0], layer[1], layer[2]) 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 = mg._build_mask(osmxml, is_prep=True) # We need an object here
mask = mask.resize((80,80), Image.Resampling.BILINEAR) mask = mask.resize((80,80), Image.Resampling.BILINEAR)
idx = 0 idx = 0
for c in contours: for c in contours:
if c[0] == layer[0] and c[1] == layer[1]: if c[0] == layer[0] and c[1] == layer[1]:
break break
else: else:
idx=idx+1 idx=idx+1
cnx = (lng_grid-1) * 80 cnx = (lng_grid-1) * 80
cny = cnt_h - (lat_grid * 80) cny = cnt_h - (lat_grid * 80)
contours[idx][2].alpha_composite(mask, dest=(cnx,cny)) 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 = mstr_tileprep(self._lat, self._long, lat_grid, lng_grid, layer[0], layer[1], mask, False)
#tp._determineEdges() #tp._determineEdges()
curlyr = curlyr+1 curlyr = curlyr+1
@ -454,7 +465,8 @@ class mstr_orthographic:
if cur_tile_x > top_lng: if cur_tile_x > top_lng:
top_lng = cur_tile_x 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 ) progress = int( (len(totaldata) / total_tiles) * 100 )
if progress >= nextstep: if progress >= nextstep:
@ -475,13 +487,20 @@ class mstr_orthographic:
# Store all contour images # Store all contour images
for c in contours: #for c in contours:
c[2].save(mstr_datafolder + "_cache/contour_" + c[0] + "_" + c[1] + ".png") # 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") #mstr_msg("orthographic", "Saved contour images for each OSM tag")
# We now need to "raytrace" the resources for correct placement # Genertate all perlin noise maps
mstr_msg("orthographic", "Performing resource plamement tracing for tile") 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 # Let's set up an array which keeps track of all used tiles, per resource
""" """

View File

@ -30,6 +30,12 @@ class mstr_osmxml:
self._lng = 0 self._lng = 0
self._curB_lat = 0 self._curB_lat = 0
self._curB_lng = 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 # 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. # 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. # 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: if os.path.isfile(fn) == False:
# We will use our self-hosted API for this. # We will use our self-hosted API for this.
parse = False parse = False
@ -110,7 +116,9 @@ class mstr_osmxml:
# Check if the DOM object has a document element # Check if the DOM object has a document element
if dom.documentElement: if dom.documentElement:
# Store the content in memory # 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._xmldata = xml.dom.minidom.parseString(self._xmlcontent)
self._xmlcontent = "" # Clear self._xmlcontent = "" # Clear

144
perlin.py Normal file
View File

@ -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