Closed anderslogg closed 9 months ago
My thoughts on translating the FME script to python with some help from ChatGPT!
The primary goal of this script is to automate the generation of Unreal Engine (UE) tiles from geospatial datasets. The datasets include a Digital Elevation Model (DEM), land use vector data (MY polygon layer), and a road network vector data (VL polygon layer). The process involves converting these datasets into specific tile resolutions suitable for UE and applying specific preprocessing steps like resampling, merging, and convolution filtering.
DETALJTYP
which indicates the type of land use.Tile_X_<tile_column>_Y<tile_row>.png
Tile_X_<tile_column>_Y<tile_row>.png
Tile_X_<tile_column>_Y<tile_row>.png
Currently, the conversion process relies on FME, a comprehensive software designed to manage spatial data. The workflow in FME consists of:
While effective, this process requires manual intervention and lacks the flexibility and customization a dedicated Python script can provide. Additionally, FME may not be readily available to all users, making the Python alternative more universally accessible.
The core of this script revolves around three major functions:
Input Validation:
DETALJTYP
attribute does not contain any of the valid values we later look for etc).HeightMapGeneration:
GenerateRoadMask:
DETALJTYP
attribute).GenerateLandUseMask:
DETALJTYP
attribute.Data Size and Processing Time: Handling large datasets might result in substantial processing times. Parallel processing could be explored for optimization.
Memory Management: Operations like rasterizing or convolution can be memory-intensive especially if a raster mosaic needs to be loaded into memory. This needs to be handeled elegantly.
Boundary Handling: When using a clipping boundary, users need to ensure it is appropriately defined to prevent unwanted data exclusions.
landuse_mapping = {
"WATER": ["VATTEN"], # The repeated "VATTEN" values are consolidated under WATER
"GLACIER": ["ÖPGLAC"],
"BUILDINGS": ["BEBSLUT", "BEBHÖG", "BEBLÅG", "BEBIND"],
"FARMING": ["ODLÅKER", "ODLFRUKT"],
"OPEN AREAS": ["ÖPMARK", "ÖPKFJÄLL", "ÖPTORG"],
"FOREST": ["SKOGBARR", "SKOGLÖV", "SKOGFBJ"],
"UNMAPPED": ["MRKO"] # Unused as of now
}
WATER: Represents areas designated as "VATTEN".
GLACIER: Represents glaciers.
BUILDINGS: Captures all types of buildings and developments.
FARMING: Includes areas designated for farming and fruit-growing.
OPEN AREAS: Represents various open lands.
FOREST: Comprises different forest types.
UNMAPPED: Denotes areas that have not been mapped.
Note: Something similar can be used for the road buffers.
validate_input_data
Validates the input data for correctness, resolution, and the presence of required attributes.
Parameters:
dem_directory
: Path to the DEM geotiff directory.landuse_path
: Path to the landuse MY layer shapefile.road_path
: Path to the road VL layer shapefile.optional_clipping_boundary
: Optional clipping boundary.
def validate_input_data(dem_directory, landuse_path, road_path, optional_clipping_boundary=None):
pass
generate_heightmap
Processes DEM data to produce heightmap tiles suitable for Unreal Engine.
Parameters:
dem_directory
: Path to the DEM geotiff file.clipping_boundary
: Clipping boundary for processing.output_folder
: Folder to store the generated tiles.ue_cell_resolution
: Unreal Engine cell resolution (default set to 1009 other valid values include 8129,4033,2017,5055,253,127 see specific tile resolutions suitable for UE).
def generate_heightmap(dem_path, clipping_boundary, output_folder, ue_cell_resolution=1009):
pass
generate_road_mask
Creates a rasterized road mask with buffer and optional convolution filtering.
Parameters:
road_path
: Path to the road VL layer shapefile.clipping_boundary
: Clipping boundary for processing.output_folder
: Folder to store the generated tiles.ue_cell_resolution
: Unreal Engine cell resolution (default set to 1009 seespecific tile resolutions suitable for UE).
def generate_road_mask(road_path, clipping_boundary, output_folder, ue_cell_resolution=1009):
pass
generate_landuse_mask
Processes landuse data to generate raster tiles for each land-use type, utilizing a provided mapping.
Parameters:
landuse_path
: Path to the landuse MY layer shapefile.clipping_boundary
: Clipping boundary for processing.output_folder
: Folder to store the generated tiles.landuse_mapping
: Dictionary mapping of landuse types.ue_cell_resolution
: Unreal Engine cell resolution (default set to 1009 see specific tile resolutions suitable for UE).def generate_landuse_mask(landuse_path, clipping_boundary, output_folder, landuse_mapping, ue_cell_resolution=1009):
pass
Utility functions can be added for:
Raster operations such as resampling, mosaicking, tiling, renaming, calculating z_scale value see calculation method here.
Vector operations like buffering, dissolving, and rasterizing.
File operations to check data integrity, file existence, and handle output.
if __name__ == "__main__":
# Define paths to input files
dem_directory = "path_to_dem_directory"
landuse_path = "path_to_landuse.shp"
road_path = "path_to_road.shp"
# Define output folder
output_folder = "path_to_output_folder"
# Validate input data
validate_input_data(dem_path, landuse_path, road_path)
# Process data
generate_heightmap(dem_path, None, output_folder)
generate_road_mask(road_path, None, output_folder)
generate_landuse_mask(landuse_path, None, output_folder, landuse_mapping)
@dwastberg Perhaps now decide how to import dtcc and use out data structures/methods etc.
Some updates here @vbassn @dwastberg The unreal tiles workflow is now complete. It can generate unreal tiles from DEM, Landuse, Street network.
How to get buildings to work There is a "subworkflow" that brings in the buildings. This also works*. Something worth an explanation here is how to translate the buildings mesh to line up with UE.
The UE workflow generates a minimum overlapping bounding box.
These bounds are passed to Builder. From the resulting mesh we need to apply the following translation
translationx = -bounds.xmin **(Negative)_* translation_y = -bounds.ymin+ ((unreal_resolutioncellresolution)-bounds.height) **(Negative)_ translation_z = centroid of 3D bounding box for combined building meshes (Positive)**
Note: translation_x may be incomplete since I have only tested with a single tile. Further testing required.
Where: unreal cell resolution is the valid UE cell resolution of a single UE tile (1009 pixels per tile by default) cell_resolution is the m/per pixel resolution of DEM that is passed to the workflow (2m if coming via Lantmäteriet)
*What doesn't work Unreal can consume several 3D formats via Datasmith however, none of them are directly compatible with the supported builder formats. FBX is a good option but requires some extra work as of this comment. I have been testing using manual conversions to 3DM via rhino.
Once this is fixed, the buildings may be included in the main workflow.
What is next Like @vbassn mentioned, the next step is to replace io functions with dtcc_io. This requires some reworking in the landuse and street network model. Replace the rasters with the DTCC data model Replace the LM DEM raster with a raster generated through builder using LiDAR
Workflow for supporting Sanjay's work. See workflows/workflow_sanjay.py in DTCC module.
Questions to be answered:
Overview: Explain what the overall idea is
Input data: List input data, file formats etc in detail. How to obtain the data? Also attach example data to this issue.
Output data: List input data, file formats etc in detail. How is the output data used?
How is this done today: Explain the workflow as it is today with existing tools.
Comments: Any comments? How would this work in an ideal world?