Mt. Fuji Elevation Python Scripts

Two Python scripts that process GeoTIFF elevation data into 3D mesh geometry and geographic coordinate lookups, used to bring real-world terrain into a Unity scene.

processTif.py — Reading & Scaling the Elevation Grid

The script accepts a TIFF filename and an optional integer scale factor from the command line. tifffile.imread loads the raw elevation array, then every n-th row and column is sampled to produce a smaller grid before export.

processTif.py — load & scale
import sys
from tifffile import imread

filename  = sys.argv[1]
scalefactor = 1
if len(sys.argv) > 2:
    scalefactor = int(sys.argv[2])

elevData = imread(filename)

elev = []
if scalefactor != 1:
    for line in elevData[::scalefactor]:
        elev.append(line[::scalefactor])
else:
    elev = elevData

3D Surface Preview & CSV Export

After scaling, the grid is written out as a CSV and immediately visualised as a 3D surface with matplotlib so the data can be sanity-checked before the heavier mesh-generation step.

processTif.py — plot & export CSV
import csv
import matplotlib.pyplot as plt
import numpy as np

# write grid to CSV
f = open("gridexport.csv", 'w')
w = csv.writer(f)
w.writerows(elev)
f.close()

# 3-D preview
x = range(int(len(elevData[0]) / scalefactor))
y = range(int(len(elevData)    / scalefactor))
X, Y = np.meshgrid(x, y)

hf = plt.figure()
ha = hf.add_subplot(111, projection='3d')
ha.plot_surface(X, Y, elev)
plt.show()

Triangle Mesh Generation

Each 2×2 block of grid cells is split into two triangles using the standard quad-to-tri pattern, producing a flat index list that Unity's Mesh API can consume directly.

processTif.py — quad triangulation
matLen = len(elev[0])
tris = []

for i, row in enumerate(elev[:-1]):
    for j, item in enumerate(row[:-1]):
        index = i * matLen + j

        # first triangle  (top-left, top-right, bottom-left)
        tris.append(index)
        tris.append(index + 1)
        tris.append(index + matLen)

        # second triangle (top-right, bottom-right, bottom-left)
        tris.append(index + 1)
        tris.append(index + matLen + 1)
        tris.append(index + matLen)

JSON Export — Three Mesh Variants

makeJson() flattens the vertex grid into three parallel arrays (X, Y, Z) and bundles them with the triangle index list. Three separate files are written: the raw smooth mesh, a slope-tile variant where each 2×2 block is flattened to its top-left value, and a tile variant that steps the value forward diagonally — both useful for stylised low-poly terrain.

processTif.py — makeJson & write
def makeJson(matrix, tris):
    jsonObj = {"verticesX": [], "verticesY": [], "verticesZ": [], "tris": []}
    for point in matrix:
        jsonObj["verticesX"].append(float(point[0]))
        jsonObj["verticesY"].append(float(point[1]))
        jsonObj["verticesZ"].append(float(-point[2]))  # negate for Unity's y-up
    jsonObj["tris"] = tris
    return jsonObj

# smooth mesh
f = open(filename.split('.')[0] + '.json', 'w')
f.write(json.dumps(makeJson(matrix, tris)))
f.close()

# slope-tile and flat-tile variants follow the same pattern …

coords_to_grid.py — Geographic Coordinate Lookup

A single utility function maps a latitude/longitude pair to a floating-point row and column within the elevation grid, given the grid's bounding box. The row is inverted (1 - …) because TIFF rows run top-to-bottom while latitude increases upward.

coords_to_grid.py
def getGridPosition(grid, lat, lng, latMin, latMax, longMin, longMax):
    gridRows = len(grid)
    gridCols = len(grid[0])

    latStep = (latMax - latMin) / gridRows
    lngStep = (longMax - longMin) / gridCols

    row = ((1 - (lat - latMin)) / latStep)
    col = ((lng - longMin)         / lngStep)

    return row, col