1 Retrieve Building Footprints with Heights Using a Bounding Box

Set out_type = "all" o return a comprehensive list of outputs, including: - poly: an sf object of building footprints, - binary: a terra raster indicating presence/absence of buildings, and - graduated: a terra raster representing building height.

Specify cell_size = 1 to generate raster layers with 1-meter resolution, ensuring detailed spatial representation of building geometries within the defined area of interest.

buildings_list <- gloBFPr::search_3dglobdf(bbox = c(-83.065644,42.333792,-83.045217,42.346988),
                                           out_type = "all", 
                                           cell_size = 1)

Setting mask = TRUE ensures the height raster is masked by the building footprints.

buildings_rast <- gloBFPr::search_3dglobdf(bbox = c(-83.065644,42.333792,-83.045217,42.346988),
                                           out_type = "graduated_rast", 
                                           mask = TRUE, 
                                           cell_size = 1)

Setting data_source = "GBA" to get building data from GlobalBuildingAtlas.

buildings_gba <- gloBFPr::search_3dglobdf(bbox = c(-83.065644,42.333792,-83.045217,42.346988),
                                           out_type = "poly", 
                                           data_source = "GBA")

2 Build a Fused Digital Surface Model from Building Height Data

get_fused_dsm() combines three raster sources into a single digital surface model (DSM): an OpenTopography-derived terrain DEM, an optional canopy height model, and building heights rasterized from the footprint polygon. The result is DEM + max(canopy height, building height), aligned to the finest available resolution among the inputs.

This requires a free OpenTopography API key for the DEM download (see the “API keys” section of the package README for how to request one).

dsm <- gloBFPr::get_fused_dsm(
  x = buildings_list$poly,
  datasource_canopy_height = "metachm",
  min_tree_height = 2,
  opentopo_key = "YOUR_OPENTOPOGRAPHY_API_KEY",
  quiet = FALSE
)

terra::plot(dsm)

Set datasource_canopy_height = NULL to build the DSM from terrain and buildings only, skipping the canopy height download.

dsm_no_canopy <- gloBFPr::get_fused_dsm(
  x = buildings_list$poly,
  datasource_canopy_height = NULL,
  opentopo_key = "YOUR_OPENTOPOGRAPHY_API_KEY"
)

By default, get_fused_dsm() outputs at the finest native resolution available between the downloaded DEM and canopy height model, so buildings are never silently blurred down to a coarser source raster (this matters especially where the DEM falls back to ~30 m SRTM data). Set resolution explicitly to force a finer grid than either native source — useful for pedestrian-level shadow, wind, or viewshed analysis — or a coarser one to speed up large study areas.

dsm_1m <- gloBFPr::get_fused_dsm(
  x = buildings_list$poly,
  datasource_canopy_height = "metachm",
  resolution = 1,
  opentopo_key = "YOUR_OPENTOPOGRAPHY_API_KEY"
)

Set min_tree_height to control the minimum canopy height (in meters) treated as tree cover rather than noise.

dsm_with_canopy <- gloBFPr::get_fused_dsm(
  x = buildings_list$poly,
  datasource_canopy_height = "metachm",
  min_tree_height = 1,
  opentopo_key = "YOUR_OPENTOPOGRAPHY_API_KEY",
  resolution = 1
)

prepare_openfoam_inputs() builds the same fused DSM internally when called with include_fused_dsm = TRUE and an opentopo_key, using the same datasource_canopy_height/min_tree_height arguments — you do not need to call get_fused_dsm() separately before that workflow. The shadow/radiation functions (svf(), get_shadow_footprint(), get_shadow_height(), get_radiation()) and get_3d_world() take the terrain DEM and canopy height raster as separate dem/canopy_height arguments rather than a pre-fused DSM, so downloading a DEM and canopy raster once and passing them into those functions is the way to avoid repeat downloads there.

3 Exporting 3D City Models

get_3d_world() turns a study area into a portable 3D scene — terrain, buildings, trees, streets, lawns, and water — written as Wavefront OBJ and binary STL files that load directly in Rhino3D and Blender.

library(gloBFPr)
library(sf)
library(terra)

data(globfp_example)
buildings <- globfp_example

3.1 Quick start

The fastest way to get a model is flat mode — no terrain download, no API key. We use the bundled example footprints.

out_dir <- file.path(tempdir(), "world_flat")
world <- get_3d_world(
  x       = buildings,
  terrain = FALSE,
  canopy  = NULL,
  out_dir = out_dir
)

list.files(out_dir)
## [1] "buildings.stl"       "world.mtl"           "world.obj"          
## [4] "world_metadata.json"
world$n_buildings
## [1] 369

Drag world.obj into Rhino or Blender and you have the city. Each building is its own selectable group (building_<id>), coordinates are in metres, and the scene sits at the world origin — world_metadata.json stores the CRS and offset needed to georeference it back.

3.2 Satellite texture

For a photoreal ground, turn on every feature and drape Esri World Imagery over the terrain. Buildings sit on the DEM, trees come from the canopy height model, and streets, lawns, and water are painted into the terrain surface itself:

The package ships a DEM and a canopy height raster for the same extent as globfp_example, so no API key or elevation download is needed:

data(globfp_example_dem)
data(globfp_example_canopy_height)

world <- get_3d_world(
  x              = buildings,
  terrain        = TRUE,
  dem            = rast(globfp_example_dem),
  canopy_height  = rast(globfp_example_canopy_height),
  canopy         = NULL,
  roads          = "overture",
  water          = "overture",
  greenspace     = TRUE,
  basemap        = TRUE,
  facade_palette = TRUE,
  out_dir        = file.path(tempdir(), "world_textured"),
  quiet          = FALSE
)

What each argument contributes:

  • terrain + dem — the bundled elevation raster (omit dem and pass key to download one from OpenTopography instead).
  • canopy_height — trees detected from the bundled canopy height model, each scaled to its measured height (or set canopy = "metachm" to download one).
  • roads — Overture segments buffered to arnis widths and painted as asphalt, concrete sidewalks, and dirt paths. Segments flagged as bridges are instead raised into elevated decks with ramps, railings, and pillars (bridges = FALSE keeps them flat); tunnels are omitted from the ground.
  • water — rivers, lakes, and coast flattened just below their banks with a sand fringe along the shoreline.
  • greenspace — ground-level lawns classified from greenSD map tiles (TRUE/"esri", or "sentinel2"), with canopy cells excluded since the trees already stand for those.
  • basemap — satellite imagery saved as basemap.jpg and mapped onto the ground with texture coordinates.

The imagery replaces the surface-class colors on the terrain; buildings and trees keep their materials. If you publish renders, credit Esri, Maxar, Earthstar Geographics, and the GIS User Community.

3.3 Voxel mode

all_vox = TRUE rebuilds the same scene as blocks, the way arnis builds Minecraft worlds — stepped terrain, quantized building columns, voxel trees, and block-painted streets, lawns, and water:

world_vox <- get_3d_world(
  x              = buildings,
  terrain        = TRUE,
  dem            = rast(globfp_example_dem),
  canopy_height  = rast(globfp_example_canopy_height),
  canopy         = NULL,
  roads          = "overture",
  water          = "overture",
  greenspace     = TRUE,
  facade_palette = TRUE,
  all_vox        = TRUE,
  vox_size       = 1,
  out_dir        = file.path(tempdir(), "world_vox")
)

Heights still come from your data — building Height, CHM tree heights, DEM terrain — only the geometry representation changes. vox_size = 1 is Minecraft scale; larger blocks give chunkier, lighter models. Footprint edges are traded for the voxel look, so use the default mode when you need measurement-grade geometry.

Both modes accept the same styling and performance controls: color_by maps any numeric column (including get_morphology() metrics) to a viridis ramp on the buildings, max_trees caps tree count, simplify_terrain coarsens the DEM, and surface_res sets how finely street edges are resolved.

mirror server hosted at Truenetwork, Russian Federation.