Planet-scale spatial data,interactive at full resolution
SpatialEngine
No downsampling. No preview meshes. No specialist graphics team.
Load, edit, and compute against billions of points, voxels, and meshes while the view stays responsive in the browser. One Python client drives the same runtime everywhere you deploy it.
# Synthetic terrain, scanned into 640,000 points.
#
# Sums a few octaves of sine to get a ridged landscape, samples it on a jittered
# grid the way an airborne scan would, and colours the cloud by elevation.
import math
import random
import tempfile
from pathlib import Path
import fdk
client = fdk.connect()
obj = client.std.object
SIDE, SPACING = 800, 2.5 # 800 x 800 samples, 2 km across
def elevation(x, y):
"""Three octaves of ridged sine, in metres."""
h = 120.0 * math.sin(x / 640.0) * math.cos(y / 520.0)
h += 45.0 * math.sin(x / 190.0 + 1.3) * math.cos(y / 160.0)
h += 12.0 * math.sin(x / 55.0) * math.sin(y / 47.0)
return h + 40.0 * abs(math.sin(x / 300.0 + y / 900.0))
random.seed(7)
csv_path = Path(tempfile.gettempdir()) / "terrain.csv"
lo, hi = math.inf, -math.inf
with open(csv_path, "w") as f:
print("x,y,z,elev", file=f)
for i in range(SIDE):
for j in range(SIDE):
# Jitter each sample so the cloud reads as a scan, not a lattice.
x = i * SPACING + random.uniform(-1.0, 1.0)
y = j * SPACING + random.uniform(-1.0, 1.0)
z = elevation(x, y)
lo, hi = min(lo, z), max(hi, z)
print(f"{x:.2f},{y:.2f},{z:.2f},{z:.2f}", file=f)
header = obj.representation.load_header_config_from_csv(path=str(csv_path)).unwrap()
rep = obj.representation.load_point_cloud_from_csv(
path=str(csv_path), header_config=header
).unwrap()
node = obj.unsafe.create_id_node(name="Terrain scan").unwrap()
obj.upsert_representation(node=node, new_representation={
"PointCloud": {
"data": rep["PointCloud"]["data"],
"appearance": {"Geometry": {
"point_size": 3.0,
# The ramp spans the real elevation range, so the colours are the map legend.
"appearance": {"attribute": "elev", "range": [lo, hi], "ramp": "Terrain"},
"filter": "None",
"opacity": 1.0,
}},
},
}).unwrap()
client.std.wait_for_scene_sync()
client.std.camera.zoom_extents(duration_seconds=1.5)
print(f"{SIDE * SIDE:,} points, elevation {lo:.0f} m to {hi:.0f} m.")
A few octaves of sine make a landscape. Sampling it on a jittered grid makes a scan, and the elevation column becomes the colour ramp.
Work against the real dataset. Point clouds, meshes, voxels, block models, and map tiles load without being cut down to a preview first.
Only the data a view actually needs is streamed and drawn, so large scenes stay interactive instead of turning into a batch render.
The same Python client and runtime drive every target, so moving an application between them is not a rewrite.
What SpatialEngine gives your application
Geometry, attributes, coordinate systems, and time sit in one runtime, so the Python workflow and the interactive viewport always operate on the same state.
One runtime for geometry, attributes, and time
A scene, its attributes, and its history are the same object. Nothing has to be exported between a processing step and the view your users work in.
Python statements you can read
Loading data, building scenes, running spatial math, and driving the camera are small, explicit statements, which is also why AI coding tools compose them quickly and predictably.
Versioned state your users can trust
Application and data changes stay explicit, so a user can inspect what changed, reverse it, and share the exact state they were looking at.
Bring us the spatial workflow you need to build
A technical session produces a focused prototype using a representative dataset and interface.