forest_navigating_uav/worldgen/forest_worldgen/export/export_meta.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""Export metadata and validation statistics for generated worlds."""

import json
from datetime import datetime

from ..spatial_stats import compute_validation_stats


def export_meta(
    positions, world_config, layout_config, output_path, seed=None, start_goal_anchors=None
):
    """
    Export metadata about the generated world to JSON.

    Includes per-pattern spatial statistics (Clark-Evans R, g(r), L(r)-r)
    computed *after* generation so they serve as validation, not constraints.
    """
    gen_config = world_config["generation"]
    area_size = gen_config["area_size"]

    # --- validation statistics ---
    validation = compute_validation_stats(positions, area_size)

    metadata = {
        "timestamp": datetime.now().isoformat(),
        "seed": seed,
        "world": {
            "name": world_config["world_name"],
            "area_size": area_size,
            "min_distance": gen_config["min_distance"],
            "tree_height_range": [gen_config["min_tree_height"], gen_config["max_tree_height"]],
        },
        "layout": {
            "type": layout_config["layout"]["type"] if layout_config else "single",
        },
        "statistics": {
            "total_objects": len(positions),
            "density": len(positions) / (area_size**2),
        },
        "validation": {
            "clark_evans_R": validation["clark_evans_R"],
            "g_small_r_mean": validation["g_small_r_mean"],
            "L_small_r_mean": validation["L_small_r_mean"],
            "pair_correlation": validation["pair_correlation"],
            "ripley_L_minus_r": validation["ripley_L_minus_r"],
        },
        "positions": [{"x": float(x), "y": float(y)} for x, y in positions],
    }

    if start_goal_anchors is not None:
        metadata["start_goal"] = {
            "start_xy": [
                float(start_goal_anchors["start_xy"][0]),
                float(start_goal_anchors["start_xy"][1]),
            ],
            "goal_xy": [
                float(start_goal_anchors["goal_xy"][0]),
                float(start_goal_anchors["goal_xy"][1]),
            ],
            "tree_exclusion_radius": float(start_goal_anchors["exclusion_radius"]),
            "removed_objects": int(start_goal_anchors["removed_objects"]),
        }

    with open(output_path, "w") as f:
        json.dump(metadata, f, indent=2)