Added Mountain building

This commit is contained in:
NichiHachi
2024-06-15 17:13:46 +02:00
parent a78558a988
commit 8195396a04
21 changed files with 128 additions and 24 deletions

View File

@@ -4,6 +4,7 @@ from PIL import Image
import random import random
from data_analysis import handle_import_image from data_analysis import handle_import_image
from typing import Union from typing import Union
import numpy as np
class City: class City:
@@ -141,6 +142,31 @@ class City:
roads.extend(district.roads) roads.extend(district.roads)
return roads return roads
def point_in_which_district(self, point: Union[Position, tuple[int, int]]) -> int:
"""
Get the index of the district in which the point is located.
:param point: The point to check.
:return: The index of the district in which the point is located.
"""
if isinstance(point, Position):
point = (point.x, point.y)
return self.map_data[point[1]][point[0]]
def get_district_mountain_map(self) -> Image:
"""
Get the map of a district.
:param district_id: The id of the district.
:return: The map of the district.
"""
district_id = [district.tile_id for district in self.districts if district.type == "mountain"]
array = np.array([[True if self.map_data[y][x] in district_id else False for x in range(len(self.map_data[0]))]
for y in range(len(self.map_data))])
image = Image.fromarray(array)
image.save('./data/mountain_map.png')
return image
if __name__ == '__main__': if __name__ == '__main__':
city = City() city = City()

View File

@@ -3,6 +3,7 @@ from typing import Union
from random import randint from random import randint
from PIL import Image from PIL import Image
class Road: class Road:
def __init__(self, position: Position, id_height: int, id_width: int, border: bool = False): def __init__(self, position: Position, id_height: int, id_width: int, border: bool = False):
self.position: Position = position self.position: Position = position
@@ -58,7 +59,7 @@ class District:
return (0 <= point_new.x < len(map_data[0]) and return (0 <= point_new.x < len(map_data[0]) and
0 <= point_new.y < len(map_data) and 0 <= point_new.y < len(map_data) and
map_data[point_new.y][point_new.x] == 0 and map_data[point_new.y][point_new.x] == 0 and
(self.type == "Mountain" or (self.type == "mountain" or
abs(height_map[point_new.y][point_new.x] - height_map[point.y][point.x]) < 2)) abs(height_map[point_new.y][point_new.x] - height_map[point.y][point.x]) < 2))
def is_point_inside(self, point: Position, map_data) -> bool: def is_point_inside(self, point: Position, map_data) -> bool:

View File

@@ -3,7 +3,7 @@ import numpy as np
from skimage.morphology import skeletonize from skimage.morphology import skeletonize
from skan.csr import skeleton_to_csgraph from skan.csr import skeleton_to_csgraph
from collections import Counter from collections import Counter
from PIL import Image from PIL import Image, ImageDraw
import random import random
@@ -213,3 +213,30 @@ class Skeleton:
# ) # )
print("[Skeleton] Mapping completed.") print("[Skeleton] Mapping completed.")
return heightmap # , roadsArea return heightmap # , roadsArea
def road_area(self, name: str, radius: int = 10) -> Image:
print("[Skeleton] Start mapping the road area...")
heightmap = Image.open("data/heightmap.png")
width, height = heightmap.size
road_area_map = Image.new("L", (width, height), 0)
road_area_map_draw = ImageDraw.Draw(road_area_map)
# Lines
for i in range(len(self.lines)):
for j in range(len(self.lines[i])):
z = self.coordinates[self.lines[i][j]][0]
x = self.coordinates[self.lines[i][j]][2]
circle_coords = (z - radius, x - radius, z + radius, x + radius)
road_area_map_draw.ellipse(circle_coords, fill=255)
# Centers
for i in range(len(self.centers)):
z = self.coordinates[self.centers[i]][0]
x = self.coordinates[self.centers[i]][2]
circle_coords = (z - radius, x - radius, z + radius, x + radius)
road_area_map_draw.ellipse(circle_coords, fill=255)
road_area_map.save("data/"+name)
print("[Skeleton] Road area mapping completed.")
return road_area_map

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -4,6 +4,7 @@ import numpy as np
from scipy import ndimage from scipy import ndimage
from Skeleton import Skeleton from Skeleton import Skeleton
from typing import Union from typing import Union
import cv2
def get_data(world: World): def get_data(world: World):
@@ -22,13 +23,14 @@ def handle_import_image(image: Union[str, Image]) -> Image:
return image return image
def filter_negative(image: Image) -> Image: def filter_negative(image: Union[str, Image]) -> Image:
""" """
Invert the colors of an image. Invert the colors of an image.
Args: Args:
image (image): image to filter image (image): image to filter
""" """
image = handle_import_image(image)
return Image.fromarray(np.invert(np.array(image))) return Image.fromarray(np.invert(np.array(image)))
@@ -215,12 +217,24 @@ def convert_2D_to_3D(image: Union[str, Image], make_it_flat: bool = False) -> np
return volume return volume
def skeleton_highway_map(image: Union[str, Image] = './data/highwaymap.png'): def skeleton_highway_map(image: Union[str, Image] = './data/highwaymap.png') -> Skeleton:
image_array = convert_2D_to_3D(image, True) image_array = convert_2D_to_3D(image, True)
skeleton = Skeleton(image_array) skeleton = Skeleton(image_array)
skeleton.parse_graph(True) skeleton.parse_graph(True)
heightmap_skeleton = skeleton.map() heightmap_skeleton = skeleton.map()
heightmap_skeleton.save('./data/skeleton_highway.png') heightmap_skeleton.save('./data/skeleton_highway.png')
skeleton.road_area('skeleton_highway_area.png', 10)
return skeleton
def skeleton_mountain_map(image: Union[str, Image] = './data/mountain_map.png') -> Skeleton:
image_array = convert_2D_to_3D(image, True)
skeleton = Skeleton(image_array)
skeleton.parse_graph()
heightmap_skeleton = skeleton.map()
heightmap_skeleton.save('./data/skeleton_mountain.png')
skeleton.road_area('skeleton_mountain_area.png',3)
return skeleton
def smooth_sobel_water() -> Image: def smooth_sobel_water() -> Image:
@@ -232,3 +246,29 @@ def smooth_sobel_water() -> Image:
group = filter_negative(group) group = filter_negative(group)
group.save('./data/smooth_sobel_watermap.png') group.save('./data/smooth_sobel_watermap.png')
return group return group
def detect_mountain(image: Union[str, Image] = './data/sobelmap.png') -> Image:
image = handle_import_image(image)
sobel = np.array(image)
pixels = sobel.reshape((-1, 1))
pixels = np.float32(pixels)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
k = 3
_, labels, centers = cv2.kmeans(pixels, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
centers = np.uint8(centers)
segmented_image = centers[labels.flatten()]
segmented_image = segmented_image.reshape(sobel.shape)
mountain = segmented_image == segmented_image.max()
contours, _ = cv2.findContours(mountain.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
max_contour = max(contours, key=cv2.contourArea)
M = cv2.moments(max_contour)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
print(f"[Data Analysis] The center of the mountain is at ({cX}, {cY})")
return (cX, cY)

View File

@@ -18,7 +18,7 @@ class Bin:
best_spot = None best_spot = None
best_spot_empty_area = float('inf') best_spot_empty_area = float('inf')
for i in range(len(self.grid[0]) - rectangle.width + 1): # Swap usage of x and y for i in range(len(self.grid[0]) - rectangle.width + 1):
for j in range(len(self.grid) - rectangle.height + 1): for j in range(len(self.grid) - rectangle.height + 1):
if self.can_place(rectangle, i, j): if self.can_place(rectangle, i, j):
empty_area = self.calculate_empty_area(rectangle, i, j) empty_area = self.calculate_empty_area(rectangle, i, j)
@@ -37,21 +37,21 @@ class Bin:
empty_area = 0 empty_area = 0
for rect_x in range(x, x + rectangle.width): for rect_x in range(x, x + rectangle.width):
for rect_y in range(y, y + rectangle.height): for rect_y in range(y, y + rectangle.height):
if self.grid[rect_y][rect_x]: # Swap usage of x and y if self.grid[rect_y][rect_x]:
empty_area += 1 empty_area += 1
return empty_area return empty_area
def can_place(self, rectangle, x, y): def can_place(self, rectangle, x, y):
for rect_x in range(x, x + rectangle.width): for rect_x in range(x, x + rectangle.width):
for rect_y in range(y, y + rectangle.height): for rect_y in range(y, y + rectangle.height):
if not self.grid[rect_y][rect_x]: # Swap usage of x and y if not self.grid[rect_y][rect_x]:
return False return False
return True return True
def update_grid(self, rectangle, x, y): def update_grid(self, rectangle, x, y):
for rect_x in range(x, x + rectangle.width): for rect_x in range(x, x + rectangle.width):
for rect_y in range(y, y + rectangle.height): for rect_y in range(y, y + rectangle.height):
self.grid[rect_y][rect_x] = False # Swap usage of x and y self.grid[rect_y][rect_x] = False
def pack_rectangles(rectangles, grid): def pack_rectangles(rectangles, grid):
@@ -62,36 +62,33 @@ def pack_rectangles(rectangles, grid):
for bin in bins: for bin in bins:
if bin.place_rectangle(rectangle): if bin.place_rectangle(rectangle):
break break
else: # No break, meaning rectangle couldn't be placed in any bin else:
new_bin = Bin(grid) new_bin = Bin(grid)
if new_bin.place_rectangle(rectangle): if new_bin.place_rectangle(rectangle):
bins.append(new_bin) bins.append(new_bin)
else: else:
return False # If a rectangle can't be placed even in a new bin, return False return False
return True # If all rectangles can be placed, return True return True
import random import random
def generate_rectangle(max_width, max_height): def generate_rectangle(max_width:int = 25):
width = random.randint(6, 20) width = random.randint(10, max_width)
height = random.randint(6, 20) height = random.randint(10, max_width)
return Rectangle(width, height) return Rectangle(width, height)
def pack_rectangles(grid): def pack_rectangles(grid):
max_width = len(grid[0])
max_height = len(grid)
bin = Bin(grid) bin = Bin(grid)
while True: while True:
rectangle = generate_rectangle(max_width // 2, max_height // 2) rectangle = generate_rectangle()
if not bin.place_rectangle(rectangle): if not bin.place_rectangle(rectangle):
break # Stop when a rectangle can't be placed break
print(len(bin.rectangles)) print(len(bin.rectangles))
return bin.rectangles # Return the list of rectangles that were placed return bin.rectangles
def draw_rectangles(rectangles, grid): def draw_rectangles(rectangles, grid):

View File

@@ -1,22 +1,35 @@
import World import World
from PIL import Image from PIL import Image
from data_analysis import get_data, highway_map, filter_sobel, skeleton_highway_map, smooth_sobel_water, subtract_map from data_analysis import get_data,filter_negative, skeleton_mountain_map, highway_map, filter_sobel, skeleton_highway_map, \
smooth_sobel_water, subtract_map, detect_mountain
from City import City from City import City
from Position import Position from Position import Position
from random import randint from random import randint
from pack_rectangle import generate_building
if __name__ == '__main__': if __name__ == '__main__':
#world = World.World() #world = World.World()
#heightmap, watermap, treemap = get_data(world) #heightmap, watermap, treemap = get_data(world)
#filter_sobel("./data/heightmap.png").save('./data/sobelmap.png') #filter_sobel("./data/heightmap.png").save('./data/sobelmap.png')
smooth_sobel_water = smooth_sobel_water() smooth_sobel_water = smooth_sobel_water()
#skeleton_highway_map(highway_map()) skeleton_highway_map(highway_map())
city = City() city = City()
for i in range(10): mountain_coo = detect_mountain()
city.add_district(Position(randint(0, 400), randint(0, 400))) city.add_district(Position(mountain_coo[0], mountain_coo[1]), "mountain")
city.add_district(Position(200, 200), "zdz")
city.add_district(Position(300, 300), "cool")
city.loop_expend_district() city.loop_expend_district()
city.district_draw_map() city.district_draw_map()
city.district_generate_road() city.district_generate_road()
image_mountain_map = city.get_district_mountain_map()
road = city.draw_roads(Image.new('RGB', (401, 401)), 4) road = city.draw_roads(Image.new('RGB', (401, 401)), 4)
road.save('./data/roadmap.png') road.save('./data/roadmap.png')
subtract_map(smooth_sobel_water, road).save('./data/roadmap2.png') subtract_map(smooth_sobel_water, road).save('./data/roadmap2.png')
subtract_map('./data/roadmap2.png', './data/skeleton_highway_area.png').save('./data/roadmap2.png')
subtract_map('./data/roadmap2.png', './data/mountain_map.png').save('./data/roadmap2.png')
generate_building('./data/roadmap2.png')
skeleton_mountain_map(image_mountain_map)
subtract_map('./data/mountain_map.png','./data/skeleton_mountain_area.png').save('./data/mountain_map.png')
subtract_map(smooth_sobel_water, filter_negative('./data/mountain_map.png')).save('./data/mountain_map.png')
generate_building('./data/mountain_map.png')