def _extract_edge_bearings(
    G: nx.MultiGraph | nx.MultiDiGraph,
    min_length: float,
    weight: str | None,
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
    """
    Extract graph's edge bearings.

    Ignores self-loop edges as their bearings are undefined. If `G` is a
    MultiGraph, all edge bearings will be bidirectional (ie, two reciprocal
    bearings per undirected edge). If `G` is a MultiDiGraph, all edge bearings
    will be directional (ie, one bearing per directed edge). For example, if
    an undirected edge has a bearing of 90 degrees then we will record
    bearings of both 90 degrees and 270 degrees for this edge.

    Parameters
    ----------
    G
        Unprojected graph with `bearing` attributes on each edge.
    min_length
        Ignore edges with `length` attributes less than `min_length`. Useful
        to ignore the noise of many very short edges.
    weight
        If None, apply equal weight for each bearing. Otherwise, weight edges'
        bearings by this (non-null) edge attribute. For example, if "length"
        is provided, each edge's bearing observation will be weighted by its
        "length" attribute value.

    Returns
    -------
    bearings, weights
        The edge bearings of `G` and their corresponding weights.
    """
    if projection.is_projected(G.graph["crs"]):  # pragma: no cover
        msg = "Graph must be unprojected to analyze edge bearings."
        raise ValueError(msg)
    bearings = []
    weights = []
    for u, v, data in G.edges(data=True):
        # ignore self-loops and any edges below min_length
        if u != v and data["length"] >= min_length:
            bearings.append(data["bearing"])
            weights.append(data[weight] if weight is not None else 1.0)

    # drop any nulls
    bearings_array = np.array(bearings)
    weights_array = np.array(weights)
    keep_idx = ~np.isnan(bearings_array)
    bearings_array = bearings_array[keep_idx]
    weights_array = weights_array[keep_idx]
    if nx.is_directed(G):
        msg = (
            "`G` is a MultiDiGraph, so edge bearings will be directional (one per "
            "edge). If you want bidirectional edge bearings (two reciprocal bearings "
            "per edge), pass a MultiGraph instead. Use `convert.to_undirected`."
        )
        warn(msg, category=UserWarning, stacklevel=2)
        return bearings_array, weights_array
    # for undirected graphs, add reverse bearings
    bearings_array = np.concatenate([bearings_array, (bearings_array - 180) % 360])
    weights_array = np.concatenate([weights_array, weights_array])
    return bearings_array, weights_array

    Extract graph's edge bearings.

    Ignores self-loop edges as their bearings are undefined. If `G` is a
    MultiGraph, all edge bearings will be bidirectional (ie, two reciprocal
    bearings per undirected edge). If `G` is a MultiDiGraph, all edge bearings
    will be directional (ie, one bearing per directed edge). For example, if
    an undirected edge has a bearing of 90 degrees then we will record
    bearings of both 90 degrees and 270 degrees for this edge.

    Parameters
    ----------
    G
        Unprojected graph with `bearing` attributes on each edge.
    min_length
        Ignore edges with `length` attributes less than `min_length`. Useful
        to ignore the noise of many very short edges.
    weight
        If None, apply equal weight for each bearing. Otherwise, weight edges'
        bearings by this (non-null) edge attribute. For example, if "length"
        is provided, each edge's bearing observation will be weighted by its
        "length" attribute value.

    Returns
    -------
    bearings, weights
        The edge bearings of `G` and their corresponding weights.
    
osmnx/bearing.py