Exemplo n.º 1
0
def test_directionlity_trackers():
    """Test of directionality tracker function."""
    links = {'id': [0, 1]}
    nodes = {'id': [0, 1]}
    ntype = 'delta'
    di.add_directionality_trackers(links, nodes, ntype)
    # assertions
    assert 'certain' in links.keys()
    assert 'certain_order' in links.keys()
    assert 'certain_alg' in links.keys()
    assert 'guess' in links.keys()
    assert 'guess_alg' in links.keys()
    assert 'maxang' not in links.keys()
    # pretend its a river
    di.add_directionality_trackers(links, nodes, 'river')
    assert 'maxang' in links.keys()
Exemplo n.º 2
0
def set_link_directions(links, nodes, imshape, manual_set_csv=None):
    """
    Set each link direction in a network.

    This function sets the direction of each link within the network. It
    calls a number of helping functions and uses a somewhat-complicated logic
    to achieve this. The algorithms and logic is described in this open-access
    paper: https://esurf.copernicus.org/articles/8/87/2020/esurf-8-87-2020.pdf

    Every time this is run, all directionality information is reset and
    recomputed. This includes checking for manually set links via the provided
    csv.

    Parameters
    ----------
    links : dict
        Network links and associated properties.
    nodes : dict
        Network nodes and associated properties.
    imshape : tuple
        Shape of binary mask as (nrows, ncols).
    manual_set_csv : str, optional
        Path to a user-provided csv file of known link directions. The default
        is None.

    Returns
    -------
    links : dict
        Network links and associated properties with all directions set.
    nodes : dict
        Network nodes and associated properties with all directions set.

    """
    # Add fields to links dict for tracking and setting directionality
    links, nodes = dy.add_directionality_trackers(links, nodes, 'delta')

    # If a manual fix csv has been provided, set those links first
    links, nodes = dy.dir_set_manually(links, nodes, manual_set_csv)

    # Initial attempt to set directions
    links, nodes = set_initial_directionality(links, nodes, imshape)

    # At this point, all links have been set. Check for nodes that violate
    # continuity
    cont_violators = dy.check_continuity(links, nodes)

    # Attempt to fix any sources or sinks within the network
    if len(cont_violators) > 0:
        links, nodes = dy.fix_sources_and_sinks(links, nodes)

    # Check that continuity problems are resolved
    cont_violators = dy.check_continuity(links, nodes)
    if len(cont_violators) > 0:
        print(
            'Nodes {} violate continuity. Check connected links and fix manually.'
            .format(cont_violators))

    # Attempt to fix any cycles in the network (reports unfixable within function)
    links, nodes, allcyclesfixed = fix_delta_cycles(links, nodes, imshape)

    # The following is done automatically now, regardless of if cycles or sinks exist
    # # Create a csv to store manual edits to directionality if does not exist
    # if os.path.isfile(manual_set_csv) is False:
    #     if len(cont_violators) > 0 or allcyclesfixed == 0:
    #         io.create_manual_dir_csv(manual_set_csv)
    #         print('A .csv file for manual fixes to link directions at {}.'.format(manual_set_csv))

    if allcyclesfixed == 2:
        print('No cycles were found in network.')

    return links, nodes
Exemplo n.º 3
0
def set_directionality(links, nodes, Imask, exit_sides, gt, meshlines,
                       meshpolys, Idt, pixlen, manual_set_csv):
    """
    Set direction of each link.

    This function sets the direction of each link within the network. It
    calls a number of helping functions and uses a somewhat-complicated logic
    to achieve this. The algorithms and logic is described in this open-access
    paper: https://esurf.copernicus.org/articles/8/87/2020/esurf-8-87-2020.pdf

    Every time this is run, all directionality information is reset and
    recomputed. This includes checking for manually set links via the provided
    csv.

    Parameters
    ----------
    links : dict
        Network links and associated properties.
    nodes : dict
        Network nodes and associated properties.
    Imask : np.array
        Binary mask of the network.
    exit_sides : str
        Two-character string of cardinal directions denoting the upstream and
        downsteram sides of the image that the network intersects (e.g. 'SW').
    gt : tuple
        gdal-type GeoTransform of the original binary mask.
    meshlines : list
        List of shapely.geometry.LineStrings that define the valleyline mesh.
    meshpolys : list
        List of shapely.geometry.Polygons that define the valleyline mesh.
    Idt : np.array()
        Distance transform of Imask.
    pixlen : float
        Length resolution of each pixel.
    manual_set_csv : str, optional
        Path to a user-provided csv file of known link directions. The default
        is None.

    Returns
    -------
    links : dict
        Network links and associated properties with all directions set.
    nodes : dict
        Network nodes and associated properties with all directions set.

    """
    imshape = Imask.shape

    # Add fields to links dict for tracking and setting directionality
    links, nodes = dy.add_directionality_trackers(links, nodes, 'river')

    # If a manual fix csv has been provided, set those links first
    links, nodes = dy.dir_set_manually(links, nodes, manual_set_csv)

    # Append morphological information used to set directionality to links dict
    links, nodes = directional_info(links, nodes, Imask, pixlen, exit_sides,
                                    gt, meshlines, meshpolys, Idt)

    # Begin setting link directionality
    # First, set inlet/outlet directions as they are always 100% accurate
    links, nodes = dy.set_inletoutlet(links, nodes)

    # Set the directions of the links that are more certain via centerline
    # distance method
    # alg = 22
    alg = dy.algmap('cl_dist_set')
    cl_distthresh = np.percentile(links['cldists'], 85)
    for lid, cld, lg, lga, cert in zip(links['id'], links['cldists'],
                                       links['guess'], links['guess_alg'],
                                       links['certain']):
        if cert == 1:
            continue
        if cld >= cl_distthresh:
            linkidx = links['id'].index(lid)
            if dy.algmap('cl_dist_guess') in lga:
                usnode = lg[lga.index(dy.algmap('cl_dist_guess'))]
                links, nodes = dy.set_link(links, nodes, linkidx, usnode, alg)

    # Set the directions of the links that are more certain via centerline
    # angle method
    # alg = 23
    alg = dy.algmap('cl_ang_set')
    cl_angthresh = np.percentile(
        links['clangs'][np.isnan(links['clangs']) == 0], 25)
    for lid, cla, lg, lga, cert in zip(links['id'], links['clangs'],
                                       links['guess'], links['guess_alg'],
                                       links['certain']):
        if cert == 1:
            continue
        if np.isnan(cla) == True:
            continue
        if cla <= cl_angthresh:
            linkidx = links['id'].index(lid)
            if dy.algmap('cl_ang_guess') in lga:
                usnode = lg[lga.index(dy.algmap('cl_ang_guess'))]
                links, nodes = dy.set_link(links, nodes, linkidx, usnode, alg)

    # Set the directions of the links that are more certain via centerline
    # distance AND centerline angle methods
    # alg = 24
    alg = dy.algmap('cl_dist_and_ang')
    cl_distthresh = np.percentile(links['cldists'], 70)
    ang_thresh = np.percentile(links['clangs'][np.isnan(links['clangs']) == 0],
                               35)
    for lid, cld, cla, lg, lga, cert in zip(links['id'], links['cldists'],
                                            links['clangs'], links['guess'],
                                            links['guess_alg'],
                                            links['certain']):
        if cert == 1:
            continue
        if cld >= cl_distthresh and cla < ang_thresh:
            linkidx = links['id'].index(lid)
            if dy.algmap('cl_dist_guess') in lga and dy.algmap(
                    'cl_ang_guess') in lga:
                if lg[lga.index(dy.algmap('cl_dist_guess'))] == lg[lga.index(
                        dy.algmap('cl_ang_guess'))]:
                    usnode = lg[lga.index(dy.algmap('cl_dist_guess'))]
                    links, nodes = dy.set_link(links, nodes, linkidx, usnode,
                                               alg)

    # Set directions by most-certain angles
    angthreshs = np.linspace(0, 0.4, 10)
    for a in angthreshs:
        links, nodes = dy.set_by_known_flow_directions(links,
                                                       nodes,
                                                       imshape,
                                                       angthresh=a,
                                                       lenthresh=3)

    # Set using direction of nearest main channel
    links, nodes = dy.set_by_nearest_main_channel(links,
                                                  nodes,
                                                  imshape,
                                                  nodethresh=1)

    angthreshs = np.linspace(0, 1.5, 20)
    for a in angthreshs:
        links, nodes = dy.set_by_known_flow_directions(links,
                                                       nodes,
                                                       imshape,
                                                       angthresh=a)

    # At this point, if any links remain unset, they are just set randomly
    if np.sum(links['certain']) != len(links['id']):
        print('{} links were randomly set.'.format(
            len(links['id']) - np.sum(links['certain'])))
        links['certain'] = np.ones((len(links['id']), 1))

    # Check for and try to fix cycles in the graph
    links, nodes, cantfix_cyclelinks, cantfix_cyclenodes = fix_river_cycles(
        links, nodes, imshape)

    # Check for sources or sinks within the graph
    cont_violators = dy.check_continuity(links, nodes)

    # Summary of problems:
    manual_fix = 0
    if len(cantfix_cyclelinks) > 0:
        print('Could not fix cycle links: {}.'.format(cantfix_cyclelinks))
        manual_fix = 1
    else:
        print('All cycles were resolved.')
    if len(cont_violators) > 0:
        print('Continuity violated at nodes {}.'.format(cont_violators))
        manual_fix = 1

    # Create a csv to store manual edits to directionality if does not exist
    if manual_fix == 1:
        if os.path.isfile(manual_set_csv) is False:
            io.create_manual_dir_csv(manual_set_csv)
            print('A .csv file for manual fixes to link directions at {}.'.
                  format(manual_set_csv))
        else:
            print('Use the csv file at {} to manually fix link directions.'.
                  format(manual_set_csv))

    return links, nodes