map_commands.rs and map_processor.rs — 3D Map Rendering & Coordinate Transforms
map_processor.rs + map_commands.rs — 3D Map Rendering & Coordinate Transforms
Converts 3D map files into top-down orthographic PNG images coloured by height (Z), and exposes coordinate transforms so the frontend can convert pixel clicks back to real-world metres. The pipeline runs on a blocking thread to avoid stalling the async runtime.
Supported formats
Extension | Library | Notes |
|---|---|---|
|
| OBJ Y-up convention remapped: world X = OBJ X, world Y = OBJ Z, height = OBJ Y |
|
| X/Y/Z read directly from point records |
|
| Same as LAS (compressed) |
Commands
render_map(filename) → MapMeta Renders a 3D map file to a top-down PNG. The source file must already exist in <appDataDir>/maps/. The output is written to the same directory as <stem>_preview.png. The image is sized to a 2048 px longest edge, aspect ratio preserved. Heavy work is offloaded via spawn_blocking so the async runtime is never blocked. Returns a MapMeta struct the frontend uses for pixel→world transforms.
pixel_to_world(px, py, meta) → (f64, f64) Converts a 2D pixel coordinate (origin bottom-left, X right, Y up) to real-world metres. Expects coordinates in the displayed image's frame — rotation, if any, must be accounted for by the frontend before calling this. The formula is simply world = pixel × metres_per_pixel, with the world origin anchored to the bottom-left corner of the image.
MapMeta fields
MapMeta is serialised and returned to the frontend after every render_map call.
Field | Type | Description |
|---|---|---|
|
| PNG width in pixels (post-rotation) |
|
| PNG height in pixels (post-rotation) |
|
| Real-world X at the left edge (currently always
) |
|
| Real-world Y at the bottom edge (currently always
) |
|
| Scale factor for pixel→world conversion |
|
| Source format detected (
,
,
) |
|
|
if the image was rotated 90° to landscape |
Rendering pipeline
process_map() runs the full pipeline in five stages:
- Load — Parse the source file into a flat list of
Point3Dstructs (x,y,zin world space). - Bounding box — Compute
x_min/max,y_min/max,z_min/maxover all points. World width and height must be non-zero or an error is returned. - Rasterise — Map each point to a pixel coordinate. Where multiple points land on the same pixel, keep the highest Z (i.e. the sky-facing surface wins). Pixel dimensions are derived from
img_size(2048) with the aspect ratio preserved. - Gap fill — Run a two-pass nearest-neighbour distance transform (
fill_gaps) to fill pixels that received no points. The forward pass sweeps top-left → bottom-right (checking left and top neighbours); the backward pass sweeps bottom-right → top-left (checking right and bottom neighbours). Each unfilled pixel inherits the Z of its closest filled neighbour, eliminating stripe artifacts. - Colour + save — Each pixel's Z is normalised to
[0.0, 1.0]and passed through a five-stop colour ramp (height_color): deep blue → cyan → green → yellow → red. If all points share the same Z (flat terrain), mid-green is used. The image is rotated 90° if height exceeds width (to keep the longest edge horizontal), then saved as PNG.
Height colour ramp
Normalised Z | Colour |
|---|---|
0.0 | Deep blue |
0.25 | Cyan |
0.5 | Green |
0.75 | Yellow |
1.0 | Red |
Error conditions
Condition | Error returned |
|---|---|
Unsupported file extension |
|
File parsed but empty |
|
All points collinear in X or Y |
|
Source file missing at invoke time |
|
PNG write failure |
|