# How MapPreviewInfo Generates Visual Map Previews in Arnis

> Learn how MapPreviewInfo in louis-e/arnis generates visual map previews after world generation. It renders a top-down PNG and notifies the frontend upon completion.

- Repository: [Louis Erbkamm/arnis](https://github.com/louis-e/arnis)
- Tags: how-to-guide
- Published: 2026-03-20

---

**MapPreviewInfo stores the world path and X/Z bounds to trigger a background thread that renders a top-down PNG visualization of the generated Minecraft world and notifies the frontend when complete.**

When generating Minecraft worlds from real-world geographic data, Arnis provides immediate visual feedback through automated map previews. The `MapPreviewInfo` struct serves as the critical data bridge between the world generation backend and the preview rendering pipeline, enabling non-blocking visualization of newly created worlds.

## Understanding the MapPreviewInfo Structure

### Data Fields and Construction

The `MapPreviewInfo` struct is defined in [`src/data_processing.rs`](https://github.com/louis-e/arnis/blob/main/src/data_processing.rs) and encapsulates all metadata required to render a spatial preview:

```rust
pub struct MapPreviewInfo {
    pub world_path: PathBuf,
    pub min_x: i32,
    pub max_x: i32,
    pub min_z: i32,
    pub max_z: i32,
    pub world_area: i64,
}

```

The `world_area` field is computed automatically during construction based on the X/Z bounds, providing a quick check for oversized worlds that could crash the renderer.

## Triggering Preview Generation After World Creation

After the world generation completes successfully, the GUI layer constructs a `MapPreviewInfo` instance and initiates the preview pipeline. This occurs in [`src/gui.rs`](https://github.com/louis-e/arnis/blob/main/src/gui.rs) immediately after the world data is written to disk:

```rust
// src/gui.rs – after the world has been written to disk
if world_format == WorldFormat::JavaAnvil {
    // `xzbbox` holds the world's X/Z bounds
    let preview_info = data_processing::MapPreviewInfo::new(
        generation_options.path.clone(),
        &xzbbox,
    );
    // Runs in a background thread, non-blocking UI
    data_processing::start_map_preview_generation(preview_info);
}

```

This logic appears twice in the file—once for terrain-only mode (lines 30-36) and once for full-data mode (lines 83-90)—ensuring previews are generated regardless of the selected generation mode.

## Background Thread Rendering Process

The `start_map_preview_generation` function in [`src/data_processing.rs`](https://github.com/louis-e/arnis/blob/main/src/data_processing.rs) manages the lifecycle of the preview generation to prevent UI freezing:

```rust
pub fn start_map_preview_generation(info: MapPreviewInfo) {
    if info.world_area > MAX_MAP_PREVIEW_AREA {
        return;
    }

    std::thread::spawn(move || {
        let result = std::panic::catch_unwind(...);
        match result {
            Ok(Ok(_path)) => emit_map_preview_ready(),
            /* error handling omitted */
        }
    });
}

```

The function first validates that `world_area` does not exceed `MAX_MAP_PREVIEW_AREA`, a safety limit preventing memory exhaustion on massive worlds. It then spawns a dedicated OS thread with `std::thread::spawn`, wrapping the rendering in `catch_unwind` to isolate panics from the main application.

## The Rendering Pipeline

Inside the thread, the actual visualization is produced by `map_renderer::render_world_map` in [`src/map_renderer.rs`](https://github.com/louis-e/arnis/blob/main/src/map_renderer.rs):

```rust
// src/map_renderer.rs – core rendering routine
pub fn render_world_map(
    world_dir: &Path,
    min_x: i32,
    max_x: i32,
    min_z: i32,
    max_z: i32,
) -> Result<std::path::PathBuf, String> {
    // … compute image size, iterate over region files in parallel …
    // For each region, collect the top-most block colour per column
    // Write the final RgbImage to `<world_dir>/arnis_world_map.png`
}

```

The renderer iterates through Minecraft region files (`*.mca`), extracts the highest non-air block for each X/Z column, applies a pre-computed color palette based on block types, and outputs `arnis_world_map.png` directly into the world directory.

## Frontend Integration and Display

Upon successful rendering, `emit_map_preview_ready()` in [`src/progress.rs`](https://github.com/louis-e/arnis/blob/main/src/progress.rs) dispatches a `map-preview-ready` event through Tauri’s event system. The frontend handles this in [`src/gui/js/main.js`](https://github.com/louis-e/arnis/blob/main/src/gui/js/main.js):

```rust
// Sending the preview-ready event
match result {
    Ok(Ok(_path)) => emit_map_preview_ready(),
    /* error handling omitted */
}

```

```javascript
// src/gui/js/main.js – called when backend signals readiness
async function showWorldPreviewButton() {
    await loadWorldMapData();                // <-- gui_get_world_map_data
    if (currentWorldMapData) {
        const mapFrame = document.querySelector('.map-container');
        mapFrame.contentWindow.postMessage({
            type: 'worldPreviewReady',
            data: currentWorldMapData
        }, '*');
    }
}

// Load map image and bounds from the backend
async function loadWorldMapData() {
    const mapData = await invoke('gui_get_world_map_data', { worldPath });
    if (mapData) currentWorldMapData = mapData;
}

```

The `gui_get_world_map_data` command in [`src/gui.rs`](https://github.com/louis-e/arnis/blob/main/src/gui.rs) reads the generated PNG, base64-encodes it, retrieves metadata from [`metadata.json`](https://github.com/louis-e/arnis/blob/main/metadata.json), and returns a `WorldMapData` object. The JavaScript then posts this data to the embedded map iframe, where Leaflet (configured in [`src/gui/js/maps/leaflet.js`](https://github.com/louis-e/arnis/blob/main/src/gui/js/maps/leaflet.js)) renders the image as a georeferenced overlay using the provided bounds.

## Summary

- **MapPreviewInfo** encapsulates the world path and X/Z bounding box, calculating total area for safety checks.
- **Construction** occurs in [`src/gui.rs`](https://github.com/louis-e/arnis/blob/main/src/gui.rs) immediately after world generation completes, with separate calls for terrain-only and full-data modes.
- **Background processing** via `start_map_preview_generation` prevents UI blocking and enforces `MAX_MAP_PREVIEW_AREA` limits.
- **Rendering** in [`src/map_renderer.rs`](https://github.com/louis-e/arnis/blob/main/src/map_renderer.rs) produces `arnis_world_map.png` by scanning region files and coloring top-block columns.
- **Frontend delivery** uses Tauri events and `gui_get_world_map_data` to transfer base64-encoded images and bounds to the Leaflet map viewer.

## Frequently Asked Questions

### What information does MapPreviewInfo store?

MapPreviewInfo stores the absolute `world_path` as a `PathBuf`, the spatial boundaries (`min_x`, `max_x`, `min_z`, `max_z`) as `i32` values, and the computed `world_area` as an `i64`. This data is defined in [`src/data_processing.rs`](https://github.com/louis-e/arnis/blob/main/src/data_processing.rs) and instantiated in [`src/gui.rs`](https://github.com/louis-e/arnis/blob/main/src/gui.rs) immediately after world generation.

### Why does Arnis use a background thread for map previews?

The `start_map_preview_generation` function spawns a dedicated OS thread via `std::thread::spawn` to prevent the rendering workload from freezing the Tauri-based UI. This approach also wraps the renderer in `catch_unwind` to isolate panics, ensuring that map generation failures do not crash the main application.

### What file format does the map preview use?

The renderer outputs a PNG file named `arnis_world_map.png` directly into the generated world directory. The frontend retrieves this image via the `gui_get_world_map_data` command, which base64-encodes the PNG for transmission to the JavaScript layer, where Leaflet displays it as a georeferenced image overlay.

### How does the frontend receive the completed map preview?

After the PNG is written, the backend emits a `map-preview-ready` event through Tauri's event system. The JavaScript handler `showWorldPreviewButton` in [`src/gui/js/main.js`](https://github.com/louis-e/arnis/blob/main/src/gui/js/main.js) invokes the `gui_get_world_map_data` command to fetch the base64 image and metadata, then posts this data via `postMessage` to the map iframe, where Leaflet renders the visual preview using the provided geographic bounds.