Zum Inhalt

native/macos/KuroNativeSaver/Core/District.swift

// District — the baked OpenStreetMap district the METRO scene renders.
//
// The JSON is shared verbatim with the web engine (src/engine/data/osm-district.json)
// and is BAKED AT BUILD TIME by scripts/bake-osm-district.mjs — it is never fetched.
// That is a hard architectural rule, not a preference: the screensaver must run fully
// offline and the Windows .scr bundle has a <5 MB budget (AGENTS.md § Architecture
// notes). Do not add a runtime download here or anywhere downstream.
//
// Loading mirrors StoryContent: `Bundle.main.url(forResource:)`, which resolves to
// Contents/Resources inside KuroMetalApp.app and to the executable's own directory for
// the bare CLI binaries — all three build scripts drop the file there.
//
// Map data © OpenStreetMap contributors, ODbL 1.0
// (https://www.openstreetmap.org/copyright).

import Foundation

struct District: Decodable {
    struct Meta: Decodable {
        /// Decimetres → metres (0.1). The ring coordinates are stored as integers.
        let scale: Double
        /// Extent along Z in metres — the METRO tile length is derived from it so the
        /// district tiles at TRUE scale instead of being squashed into a fixed tile.
        let spanZ: Double
        let attribution: String?
    }

    /// One building: height in metres, ring in delta-encoded decimetres.
    struct Footprint: Decodable {
        let h: Double
        let r: [Int]
    }

    let meta: Meta
    let footprints: [Footprint]

    /// Parsed once at first use. A missing or malformed resource is a build error, not a
    /// runtime condition — same stance as StoryContent: fail loudly rather than ship a
    /// METRO scene with an empty city in it.
    static let shared: District = load()

    static func load() -> District {
        guard let url = Bundle.main.url(forResource: "osm-district", withExtension: "json") else {
            fatalError("osm-district.json not found next to the executable (\(Bundle.main.bundlePath))")
        }
        do {
            return try JSONDecoder().decode(District.self, from: Data(contentsOf: url))
        } catch {
            fatalError("osm-district.json failed to decode: \(error)")
        }
    }
}