# `Meridian.Graph`
[🔗](https://github.com/code-shoily/meridian/blob/v0.1.0/lib/meridian/graph.ex#L1)

A spatial graph—`Yog.Graph` enriched with coordinate system metadata
and an optional spatial bounding box.

Implements `Enumerable` (iterates over `{id, data}` node tuples) and
`Inspect` (compact `#Meridian.Graph<...>` representation).

## Fields

  * `graph` — the underlying `Yog.Graph.t()`
  * `crs` — coordinate reference system identifier (default `"EPSG:4326"`)
  * `srid` — numeric SRID when available (default `4326`)
  * `bounds` — bounding geometry (`%Geo.Polygon{}` or `nil`)

## Examples

    iex> g = Meridian.Graph.new()
    iex> g.crs
    "EPSG:4326"
    iex> g.srid
    4326

    iex> g = Meridian.Graph.new() |> Meridian.Graph.add_node(1, %{name: "A"})
    iex> Enum.to_list(g)
    [{1, %{name: "A"}}]

# `crs`

```elixir
@type crs() :: String.t()
```

# `srid`

```elixir
@type srid() :: pos_integer() | nil
```

# `t`

```elixir
@type t() :: %Meridian.Graph{
  bounds: Geo.Polygon.t() | nil,
  crs: crs(),
  graph: Yog.Graph.t(),
  srid: srid()
}
```

# `add_edge`

```elixir
@spec add_edge(t(), Yog.node_id(), Yog.node_id(), any()) ::
  {:ok, t()} | {:error, String.t()}
```

Adds a spatial edge to the graph.

## Examples

    iex> g = Meridian.Graph.new()
    iex> g = g |> Meridian.Graph.add_node(:a, %{}) |> Meridian.Graph.add_node(:b, %{})
    iex> {:ok, g} = Meridian.Graph.add_edge(g, :a, :b, %{distance: 10.5})
    iex> g.graph.out_edges[:a][:b]
    %{distance: 10.5}

# `add_edge!`

```elixir
@spec add_edge!(t(), Yog.node_id(), Yog.node_id(), any()) :: t()
```

Adds a spatial edge to the graph, raising on error.

# `add_edge_ensure`

```elixir
@spec add_edge_ensure(t(), Yog.node_id(), Yog.node_id(), any(), any()) :: t()
```

Ensures both endpoint nodes exist, then adds an edge.

If `from` or `to` is not already in the graph, it is created with the
supplied `default` node data. Existing nodes are left unchanged.

# `add_node`

```elixir
@spec add_node(t(), Yog.node_id(), map()) :: t()
```

Adds a spatial node to the graph.

`data` should typically contain a `:geometry` key with a `Geo` struct.

## Examples

    iex> g = Meridian.Graph.new()
    iex> point = %Geo.Point{coordinates: {-73.9857, 40.7484}}
    iex> g = Meridian.Graph.add_node(g, :nyc, %{geometry: point, name: "NYC"})
    iex> Meridian.Graph.node(g, :nyc).name
    "NYC"

# `add_nodes`

```elixir
@spec add_nodes(t(), Enumerable.t()) :: t()
```

Adds multiple nodes from an enumerable of `{id, data}` tuples or a map.

## Examples

    iex> g = Meridian.Graph.new()
    iex> g = Meridian.Graph.add_nodes(g, a: %{geometry: %Geo.Point{coordinates: {0.0, 0.0}}})
    iex> Meridian.Graph.node_count(g)
    1

# `edge_count`

```elixir
@spec edge_count(t()) :: non_neg_integer()
```

Returns the number of edges in the spatial graph.

# `edges`

```elixir
@spec edges(t()) :: [{Yog.node_id(), Yog.node_id(), any()}]
```

Returns all edges as a list of `{from, to, weight}` tuples.

# `from_yog`

```elixir
@spec from_yog(
  Yog.Graph.t(),
  keyword()
) :: t()
```

Wraps an existing `Yog.Graph` as a spatial graph.

## Examples

    iex> yog = Yog.undirected() |> Yog.add_edge_ensure(1, 2, 10)
    iex> g = Meridian.Graph.from_yog(yog, crs: "EPSG:4326")
    iex> g.graph == yog
    true

# `has_geometry?`

```elixir
@spec has_geometry?(t(), Yog.node_id()) :: boolean()
```

Checks if a node has a `:geometry` key in its data.

# `has_node?`

```elixir
@spec has_node?(t(), Yog.node_id()) :: boolean()
```

Checks if the graph contains a node with the given ID.

# `kind`

```elixir
@spec kind(t()) :: :directed | :undirected
```

Returns the kind of the underlying graph (`:directed` or `:undirected`).

# `merge`

```elixir
@spec merge(t(), t()) :: t()
```

Merges another spatial graph into this one.

Both graphs must share the same CRS. Nodes and edges from `other` are
added; in case of node ID collisions, `other`'s data wins.

## Examples

    iex> a = Meridian.Graph.new() |> Meridian.Graph.add_node(1, %{name: "A"})
    iex> b = Meridian.Graph.new() |> Meridian.Graph.add_node(2, %{name: "B"}) |> Meridian.Graph.add_edge_ensure(2, 1, 5)
    iex> merged = Meridian.Graph.merge(a, b)
    iex> Meridian.Graph.node_count(merged)
    2
    iex> Meridian.Graph.edge_count(merged)
    1

# `new`

```elixir
@spec new(keyword()) :: t()
```

Creates a new empty spatial graph.

## Options

  * `:kind` — `:directed` (default) or `:undirected`
  * `:crs` — CRS identifier string, defaults to `"EPSG:4326"`
  * `:srid` — numeric SRID, defaults to `4326`

## Examples

    iex> g = Meridian.Graph.new()
    iex> g.graph.kind
    :directed

    iex> g = Meridian.Graph.new(kind: :undirected, crs: "EPSG:3857")
    iex> g.crs
    "EPSG:3857"

# `node`

```elixir
@spec node(t(), Yog.node_id()) :: any() | nil
```

Returns the data associated with a node, or `nil` if the node does not exist.

# `node_count`

```elixir
@spec node_count(t()) :: non_neg_integer()
```

Returns the number of nodes in the spatial graph.

# `nodes`

```elixir
@spec nodes(t()) :: %{required(Yog.node_id()) =&gt; any()}
```

Returns all nodes as a map of `id => data`.

# `recompute_bounds`

```elixir
@spec recompute_bounds(t()) :: t()
```

Recomputes the bounding box from all node geometries.

# `remove_edge`

```elixir
@spec remove_edge(t(), Yog.node_id(), Yog.node_id()) :: t()
```

Removes an edge from the graph.

# `remove_node`

```elixir
@spec remove_node(t(), Yog.node_id()) :: t()
```

Removes a node and all its connected edges.

# `to_yog`

```elixir
@spec to_yog(t()) :: Yog.Graph.t()
```

Unwraps a `Meridian.Graph` to its underlying `Yog.Graph`.

## Examples

    iex> g = Meridian.Graph.new() |> Meridian.Graph.add_node(1, "A")
    iex> %Yog.Graph{} = Meridian.Graph.to_yog(g)

# `update_node`

```elixir
@spec update_node(t(), Yog.node_id(), map()) :: t()
```

Updates a node's data by merging the given map into the existing data.

Returns the updated graph. If the node does not exist, it is created.

## Examples

    iex> g = Meridian.Graph.new() |> Meridian.Graph.add_node(:a, %{name: "A"})
    iex> g = Meridian.Graph.update_node(g, :a, %{tags: [:highway]})
    iex> Meridian.Graph.node(g, :a).tags
    [:highway]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
