diff --git a/README.md b/README.md index 59558e5..4167e76 100755 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ Feel free to create a PR at any point, or open an issue if you have any problems ## How it Works -Chunk data is read by opening the world save directory and reading its region files (`.mca`). Once the region data is decoded chunks can be iterated through to find surface blocks (can be changed by editing [surface_blocks.txt](surface_blocks.txt)). To make this process more efficient, the script takes advantage of precomputed heightmaps, which are also decoded from their binary format. These heightmaps contain info such as the y-level of the first motion-blocking minecraft block, so efficiency of finding the surface is greatly improved. +Chunk data is read by opening the world save directory and reading its region files (`.mca`). Once the region data is decoded chunks can be iterated through to find surface blocks (can be changed by editing [symbols.json](symbols.json)). To make this process more efficient, the script takes advantage of precomputed heightmaps, which are also decoded from their binary format. These heightmaps contain info such as the y-level of the first motion-blocking minecraft block, so efficiency of finding the surface is greatly improved. After the chunks are read several `.tif` files are saved in a folder called `data` at the root directory of the project. These files contain the raw data extracted from the minecraft files that is needed to create the topographical map. `.tif` files are chosen as they are a cross-compatible and effective data matrix format. diff --git a/src/convert_world.py b/src/convert_world.py index 1101526..303e389 100644 --- a/src/convert_world.py +++ b/src/convert_world.py @@ -1,3 +1,4 @@ +import json import logging import platform import sys @@ -99,8 +100,12 @@ def chunk_at(region, cx, cz): ) args = parser.parse_args(sys.argv[1:]) -surface_blocks = [line.rstrip() for line in open("surface_blocks.txt") if line.rstrip()] -# structure_blocks = [line.rstrip() for line in open("structure_blocks.txt") if line.rstrip()] +with open("symbols.json", "r") as json_file: + symbol_data = json.load(json_file) +surface_blocks = [symbol["blocks"].get("ground" )\ + for _, symbol in symbol_data.items() \ + if symbol["blocks"].get("ground")] +surface_blocks = list(dict.fromkeys([i for sub in surface_blocks for i in sub])) bx1, bz1, bx2, bz2 = args.x1, args.z1, args.x2, args.z2 if bx1 > bx2 or bz1 > bz2: @@ -132,7 +137,7 @@ def chunk_at(region, cx, cz): data = { "dem": create_mat(np.uint8) if args.compress_height_limit else create_mat(np.int16), - "vegetation": create_mat(np.bool_), + "canopy": create_mat(np.bool_), "landcover": create_mat(np.uint8), "trees": create_mat(np.bool_) } @@ -162,20 +167,15 @@ def chunk_at(region, cx, cz): max_height = int(chunk.heightmap[bz_in_c, bx_in_c]) if chunk.heightmap.any() else 255 min_height = 0 - canopy = False - tree = False + tree_so_far = False for by in range(max_height, min_height, -1): block = chunk.get_block(bx_in_c, by, bz_in_c) # -- surface processes: dem and landcover -- - if block.id == "air": - canopy = False - tree = False - continue if block.id in surface_blocks: data["dem"][entry] = by data["landcover"][entry] = surface_blocks.index(block.id) - if tree: + if tree_so_far: data["trees"][entry] = 1 break @@ -193,13 +193,14 @@ def chunk_at(region, cx, cz): break # -- other processes: vegetation -- - if block.id.endswith("leaves"): - data["vegetation"][entry] = 1 - canopy = True + if block.id in symbol_data["405"]["blocks"]["canopy"]: + data["canopy"][entry] = 1 # -- other processes: trees -- - if block.id.endswith("log") and canopy: - tree = True + if block.id in symbol_data["418"]["blocks"]["canopy"]: + tree_so_far = True + elif not block.id in symbol_data["418"]["blocks"]["trunk"]: + tree_so_far = False logging.info("Writing data...") try: diff --git a/src/create_map.r b/src/create_map.r index 1f50a43..73cc247 100644 --- a/src/create_map.r +++ b/src/create_map.r @@ -24,11 +24,12 @@ suppressPackageStartupMessages({ library(terra) library(tmap) + library(jsonlite) library(tiff) library(smoothr) }) -surface_blocks <- read.table("surface_blocks.txt")$V1 +symbol_data <- fromJSON("symbols.json", simplifyDataFrame = F) exif <- tiff::readTIFF("data/dem.tif", payload=F) # increase smoothing with more downsampling to account for inaccuracies @@ -57,7 +58,7 @@ size_from_mm <- function (mmwidth) { log_info("Reading data...") data <- lapply(list( dem=terra::rast("data/dem.tif"), - vegetation=terra::rast("data/vegetation.tif"), + canopy=terra::rast("data/canopy.tif"), landcover=terra::rast("data/landcover.tif"), trees=terra::rast("data/trees.tif") ), function(raster) {crs(raster) <- "ESRI:53032"; raster}) @@ -70,6 +71,11 @@ bounds <- st_as_sf(vect(ext( ))) st_crs(bounds) <- "ESRI:53032" +surface_blocks <- symbol_data |> + lapply(function(obj) obj$blocks$ground) |> + unlist() |> + unique() + log_info("Creating symbols...") log_info("Creating contours...") contours <- list( @@ -85,7 +91,7 @@ contours <- list( log_info("Creating water...") water <- data$landcover -water[data$landcover != (match("water", surface_blocks) - 1)] <- NA +water[data$landcover != (match(symbol_data$`301`$blocks$ground, surface_blocks) - 1)] <- NA water <- list( feature=water |> terra::as.polygons(), @@ -97,9 +103,9 @@ water <- list( ) log_info("Creating canopy...") -data$vegetation[data$vegetation == 0] <- NA +data$canopy[data$canopy == 0] <- NA canopy <- list( - feature=terra::as.polygons(data$vegetation) |> + feature=terra::as.polygons(data$canopy) |> terra::buffer(canopy_buffer * as.logical(settings$smoothing)), render=list(tm_fill(col = "#FFFFFF")), smoothing=20 diff --git a/surface_blocks.txt b/surface_blocks.txt deleted file mode 100644 index 4594be8..0000000 --- a/surface_blocks.txt +++ /dev/null @@ -1,107 +0,0 @@ -grass_block -dirt -coarse_dirt -mycelium -podzol -farmland -grass_path -soul_soil -rooted_dirt -packed_mud -muddy_mangrove_roots -clay - -stone -granite -andesite -diorite -cobblestone -mossy_cobblestone -tuff -calcite -dripstone_block - -sandstone -red_sandstone -smooth_sandstone -smooth_red_sandstone -deepslate - -cobbled_deepslate -bedrock - -coal -copper -lapis_lazuli -iron -gold -redstone -diamond -emerald - -nether_quartz -nether_gold - -obsidian - -netherrack -crimson_nylium -warped_nylium -basalt -polished_basalt -smooth_basalt -magma_block -end_stone -blackstone - -terracotta -white_terracotta -orange_terracotta -magenta_terracotta -light_blue_terracotta -yellow_terracotta -lime_terracotta -pink_terracotta -gray_terracotta -light_gray_terracotta -cyan_terracotta -purple_terracotta -blue_terracotta -brown_terracotta -green_terracotta -red_terracotta -black_terracotta - -white_concrete -orange_concrete -magenta_concrete -light_blue_concrete -yellow_concrete -lime_concrete -pink_concrete -gray_concrete -light_gray_concrete -cyan_concrete -purple_concrete -blue_concrete -brown_concrete -green_concrete -red_concrete -black_concrete - -sand -red_sand -gravel -soul_sand -suspicious_sand - -ice -packed_ice -blue_ice -frosted_ice - -infested_stone -infested_cobblestone -infested_deepslate - -water \ No newline at end of file diff --git a/symbols.json b/symbols.json new file mode 100644 index 0000000..af9bc97 --- /dev/null +++ b/symbols.json @@ -0,0 +1,191 @@ +{ + "213": { + "name": "Sandy ground", + "blocks": { + "ground": [ + "sand", + "red_sand", + "suspicious_sand" + ] + } + }, + "214": { + "name": "Bare rock", + "blocks": { + "ground": [ + "stone", + "granite", + "andesite", + "diorite", + "cobblestone", + "mossy_cobblestone", + "tuff", + "calcite", + "dripstone_block", + "cobbled_deepslate", + "bedrock", + "coal", + "copper", + "lapis_lazuli", + "iron", + "gold", + "redstone", + "diamond", + "emerald", + "nether_quartz", + "nether_gold", + "obsidian", + "gravel", + "infested_stone", + "infested_cobblestone", + "infested_deepslate" + ] + } + }, + "301": { + "name": "Uncrossable body of water", + "blocks": { + "ground": [ + "water" + ] + } + }, + "302": { + "name": "Shallow body of water", + "blocks": { + "ground": [ + "water" + ] + } + }, + "314": { + "name": "Ice", + "blocks": { + "ground": [ + "ice", + "packed_ice", + "blue_ice", + "frosted_ice" + ] + } + }, + "401": { + "name": "Open land", + "blocks": { + "ground": [ + "grass_block", + "dirt", + "coarse_dirt", + "mycelium", + "podzol", + "farmland", + "grass_path", + "soul_soil", + "rooted_dirt", + "packed_mud", + "muddy_mangrove_roots", + "clay", + "sandstone", + "red_sandstone", + "smooth_sandstone", + "smooth_red_sandstone", + "deepslate" + ] + } + }, + "405": { + "name": "forest", + "blocks": { + "canopy": [ + "oak_leaves", + "spruce_leaves", + "birch_leaves", + "jungle_leaves", + "acacia_leaves", + "dark_oak_leaves", + "mangrove_leaves", + "cherry_leaves", + "azalea_leaves", + "flowering_azalea_leaves", + "brown_mushroom_block" + ] + } + }, + "501": { + "name": "Paved area", + "blocks": { + "ground": [ + "terracotta", + "white_terracotta", + "orange_terracotta", + "magenta_terracotta", + "light_blue_terracotta", + "yellow_terracotta", + "lime_terracotta", + "pink_terracotta", + "gray_terracotta", + "light_gray_terracotta", + "cyan_terracotta", + "purple_terracotta", + "blue_terracotta", + "brown_terracotta", + "green_terracotta", + "red_terracotta", + "black_terracotta", + "white_concrete", + "orange_concrete", + "magenta_concrete", + "light_blue_concrete", + "yellow_concrete", + "lime_concrete", + "pink_concrete", + "gray_concrete", + "light_gray_concrete", + "cyan_concrete", + "purple_concrete", + "blue_concrete", + "brown_concrete", + "green_concrete", + "red_concrete", + "black_concrete" + ] + } + }, + "418": { + "name": "Prominent bush or tree", + "blocks": { + "canopy": [ + "oak_leaves", + "spruce_leaves", + "birch_leaves", + "jungle_leaves", + "acacia_leaves", + "dark_oak_leaves", + "mangrove_leaves", + "cherry_leaves", + "azalea_leaves", + "flowering_azalea_leaves", + "brown_mushroom_block" + ], + "trunk": [ + "oak_log", + "spruce_log", + "birch_log", + "jungle_log", + "acacia_log", + "dark_oak_log", + "mangrove_log", + "cherry_log", + "stripped_oak_log", + "stripped_spruce_log", + "stripped_birch_log", + "stripped_jungle_log", + "stripped_acacia_log", + "stripped_dark_oak_log", + "stripped_mangrove_log", + "stripped_cherry_log", + "mushroom_stem" + ] + } + } +} \ No newline at end of file