How would I use this API to generate terrain?

Hello! I’m working on a railway game and I’m trying to import real-world terrain into Roblox. I found this API for a Digital Elevation Model (DEM), and I’m wondering how I can use it to generate terrain in Roblox:

LINZ Data Service

Here is an example of the terrain the train uses:

Exporting the files from that link into blender gives me this result, however it uses a lot of memory (more than I have) so I figured it would probably be better for me to just generate the terrain directly into Roblox from the API rather than converting meshes into terrain.

I have no idea how to do https stuff so any help is appreciated.

Trying to do it directly through the Roblox API is basically impossible (or just way too hard) because the API sends image files and Roblox scripts can’t read pixel data from images.

The best method I found was to download the specific crop you want from the site, and then use a Python script to convert that data into a text format that Roblox can read. I actually had Gemini 3 Pro generate this Python script for me and it does work. It keeps the terrain at an almost 1:1 scale with the real world.

Go onto the site you provided and crop a part of the land and export the zip file.

Make sure to create a folder and add it to an editor like VS Code or something similar. Grab the .tif file from the zip file and place it into the newly created folder. Then create a file under the folder name.py and paste this in:

import rasterio
import numpy as np
import os

# --- SETTINGS ---
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_FILE = os.path.join(SCRIPT_DIR, "CB11.tif") # Make sure this matches your file
OUTPUT_FILE = os.path.join(SCRIPT_DIR, "TerrainData.lua")

# 1 = reads every single pixel (Maximum Detail)
# Increase number to downgrade the detail
STEP = 1           

# Do not change this if you want accuracy.
HEIGHT_SCALE = 1.0 

def export_to_lua():
    print(f"--- Processing {INPUT_FILE} in High Definition ---")
    
    if not os.path.exists(INPUT_FILE):
        print("Error: File not found!")
        return

    with rasterio.open(INPUT_FILE) as src:
        elevation = src.read(1)
        
        elevation[elevation < -500] = -9999
        
        valid_data = elevation[elevation > -9999]
        if valid_data.size == 0:
            print("Error: File is empty.")
            return
            
        min_height = np.min(valid_data)
        max_height = np.max(valid_data)
        
        print(f"Real World Height Range: {min_height}m to {max_height}m")
        print(f"Total Elevation Change: {max_height - min_height}m")

        elevation = np.where(elevation > -9999, elevation - min_height + 5, 0)
        
        height, width = elevation.shape
        print(f"Grid Size: {width}x{height} pixels (1 pixel = 1 meter)")
        
        with open(OUTPUT_FILE, "w") as f:
            f.write("return {\n")
            
            for y in range(0, height, STEP):
                row_data = []
                for x in range(0, width, STEP):
                    val = elevation[y, x]
                    h = round(val * HEIGHT_SCALE, 2)
                    row_data.append(str(h))
                
                f.write("\t{" + ",".join(row_data) + "},\n")
            
            f.write("}")
            
    print(f"SUCCESS! Saved to {OUTPUT_FILE}")

if __name__ == "__main__":
    export_to_lua()

You can run this python script by pressing play on the editor or through the terminal. It should then generate the new file inside the folder. If you encounter any errors, here are some fixes.

  • Wrong python version, although I doubt it would matter (I’m using 3.12.8)
  • Make sure all libraries are downloaded (Probably look up some tutorials)

Next Step:

  1. In Roblox Studio create a ModuleScript and this module is where you will paste the TerrainData.
  2. Create a normal script in ServerScriptService or just paste this code into the command prompt to generate the terrain.
local Terrain = workspace.Terrain

-- Make sure this matches the location of the module
local TerrainData = require(game.ServerScriptService.TerrainData)

-- We are reading every pixel, so 1 Data Point = 1 Meter
local SCALE = 4

function Generate()
	Terrain:Clear()

	for x, row in ipairs(TerrainData) do
		for z, heightMeters in ipairs(row) do

			if heightMeters > 0 then
				local finalHeight = heightMeters * SCALE

				local worldX = x * SCALE
				local worldZ = z * SCALE

				Terrain:FillBlock(
					CFrame.new(worldX, finalHeight / 2, worldZ),
					Vector3.new(SCALE, finalHeight, SCALE),
					Enum.Material.Grass
				)
			end
		end
		if x % 20 == 0 then task.wait() end
	end
	print("Done!")
end

Generate()

My Result:

This image is where I cropped and downloaded land that includes some steep cliff and a river, in total this crop was only about 1Mb for the zip file.

This was my result after following my method. This is just another way to recreate the terrain in roblox without going through blender. I never tried any bigger terrain and I do not know how well it will handle. You can try to edit my script by maybe figuring out adding the river or changing the terrain type depending on the height or something. Ive got no clue if this is any faster or a more efficient method than blender. I hope this can become helpful towards you or provide you with some insight or ideas.

1 Like