defmodule Microwaveprop.Buildings.Loader do @moduledoc """ Lazily loads cached Microsoft footprint csv.gz tiles into the spatial `Microwaveprop.Buildings.Index`. Idempotent: a quadkey loaded once stays loaded for the lifetime of the BEAM. Tiles that haven't been pre-fetched are skipped with a debug log — the rover scorer falls back to terrain-only path analysis when the index is missing for a region. """ alias Microwaveprop.Buildings.Index alias Microwaveprop.Buildings.MsFootprints alias Microwaveprop.Buildings.Parser require Logger @zoom 9 @doc """ Ensures every quadkey covering `bbox` has its csv.gz tile parsed into the index. Returns the list of quadkeys that were *attempted* (whether already loaded, freshly loaded, or missing on disk). """ @spec ensure_loaded_for_bbox(map()) :: [String.t()] def ensure_loaded_for_bbox(bbox) do quadkeys = MsFootprints.quadkeys_for_bbox(bbox, @zoom) Enum.each(quadkeys, &ensure_loaded/1) quadkeys end @doc """ Load a single quadkey. No-op if already loaded; logs and skips if the csv.gz file is missing on disk. """ @spec ensure_loaded(String.t()) :: :ok def ensure_loaded(quadkey) do if Index.loaded?(quadkey) do :ok else do_load(quadkey) end end defp do_load(quadkey) do path = Path.join(MsFootprints.cache_dir(), "#{quadkey}.csv.gz") if File.exists?(path) do records = path |> Parser.parse_tile() |> Enum.to_list() Index.insert_records(records) Index.mark_loaded(quadkey) Logger.info("buildings index: loaded #{quadkey} (#{length(records)} polygons)") else Logger.debug("buildings index: tile #{quadkey} not on disk; skipping") Index.mark_loaded(quadkey) end :ok end end