Get the directed grade (ie, rise over run) for each edge in the network and
    add it to the edge as an attribute. Nodes must have elevation attributes to
    use this function.

    Parameters
    ----------
    G : networkx multidigraph
    add_absolute : bool
        if True, also add the absolute value of the grade as an edge attribute

    Returns
    -------
    G : networkx multidigraph

def add_edge_grades(G, add_absolute=True): # pragma: no cover
    """
    Get the directed grade (ie, rise over run) for each edge in the network and
    add it to the edge as an attribute. Nodes must have elevation attributes to
    use this function.

    Parameters
    ----------
    G : networkx multidigraph
    add_absolute : bool
        if True, also add the absolute value of the grade as an edge attribute

    Returns
    -------
    G : networkx multidigraph
    """

    # for each edge, calculate the difference in elevation from origin to
    # destination, then divide by edge length
    for u, v, data in G.edges(keys=False, data=True):
        elevation_change = G.nodes[v]['elevation'] - G.nodes[u]['elevation']
        
        # round to ten-thousandths decimal place
        grade = round(elevation_change / data['length'], 4)
        data['grade'] = grade
        if add_absolute:
            data['grade_abs'] = abs(grade)

    log('Added grade data to all edges.')
    return G