コード例 #1
0
def fix_delta_cycle(links, nodes, cyc_links, imshape):
    """
    Attempt to resolve a single cycle.

    Attempts to resolve a single cycle within a delta network. The general
    logic is that all link directions of the cycle are un-set except for those
    set by highly-reliable algorithms, and a modified direction-setting
    recipe is implemented to re-set these algorithms. This was developed
    according to the most commonly-encountered cases for real deltas, but could
    certainly be improved.


    Parameters
    ----------
    links : dict
        Network links and associated properties.
    nodes : dict
        Network nodes and associated properties.
    cyc_links : list
        Link ids comprising the cycle to fix.
    imshape : tuple
        Shape of binary mask as (nrows, ncols).

    Returns
    -------
    links : dict
        Network links and associated properties with cycle fixed, if possible.
    nodes : dict
        Network nodes and associated properties with cycle fixed, if possible.
    fixed : bool
        True if the cycle was resolved, else False.

    """
    def re_set_linkdirs(links, nodes, imshape):
        # Cycle links are attempted to be reset according to algorithms here.
        angthreshs = np.linspace(0, 1.2, 20)
        for a in angthreshs:
            links, nodes = dy.set_by_known_flow_directions(
                links,
                nodes,
                imshape,
                angthresh=a,
                lenthresh=0,
                alg=dy.algmap('known_fdr_rs'))

        return links, nodes

    # Track if fix was successful (1:yes, 0:no)
    fixed = 1

    # List of algorithm ids that should not be reset if previously used to
    # determine direction
    # dont_reset_algs = [-1, 0, 4, 5, 13]
    dont_reset_algs = [
        dy.algmap(key) for key in [
            'manual_set', 'inletoutlet', 'main_chans', 'bridges',
            'longest_steepest'
        ]
    ]

    # Simplest method: unset the cycle links and reset them according to angles
    # Get resettale links
    toreset = [
        l for l in cyc_links
        if links['certain_alg'][links['id'].index(l)] not in dont_reset_algs
    ]

    # Get original link orientations in case fix does not work
    orig = dy.cycle_get_original_orientation(links, toreset)

    # Set certainty of cycle links to zero
    for tr in toreset:
        links['certain'][links['id'].index(tr)] = 0

    links, nodes = re_set_linkdirs(links, nodes, imshape)

    # Check that all links were reset
    if sum([links['certain'][links['id'].index(l)]
            for l in toreset]) != len(toreset):
        fixed = 0

    # Check that cycle was resolved
    cyclenode = links['conn'][links['id'].index(toreset[0])][0]
    cyc_n, cyc_l = dy.get_cycles(links, nodes, checknode=cyclenode)

    # If the cycle was not fixed, try again, but set the cycle links AND the
    # links connected to the cycle to unknown
    if cyc_n is not None and cyclenode in cyc_n[0]:
        # First return to original orientation
        links = dy.cycle_return_to_original_orientation(links, orig)

        # Get all cycle links and those connected to cycle
        toreset = set()
        for cn in cyc_n[0]:
            conn = nodes['conn'][nodes['id'].index(cn)]
            toreset.update(conn)
        toreset = list(toreset)

        # Save original orientation in case cycle cannot be fixed
        orig_links = dy.cycle_get_original_orientation(links, toreset)

        # Un-set the cycle+connected links
        for tr in toreset:
            lidx = links['id'].index(tr)
            if links['certain_alg'][lidx] not in dont_reset_algs:
                links['certain'][lidx] = 0

        links, nodes = re_set_linkdirs(links, nodes, imshape)

        # See if the fix resolved the cycle - if not, reset to original
        cyc_n, cyc_l = dy.get_cycles(links, nodes, checknode=cyclenode)
        if cyc_n is not None and cyclenode in cyc_n[0]:
            links = dy.cycle_return_to_original_orientation(links, orig_links)
            fixed = 0

    return links, nodes, fixed
コード例 #2
0
def fix_river_cycle(links, nodes, cyclelinks, cyclenodes, imshape):
    """
    Attempt to fix a single cycle.

    Attempts to resolve a single cycle within a river network. The general
    logic is that all link directions of the cycle are un-set except for those
    set by highly-reliable algorithms, and a modified direction-setting
    recipe is implemented to re-set these algorithms. This was developed
    according to the most commonly-encountered cases for real braided rivers,
    but could certainly be improved.


    Parameters
    ----------
    links : dict
        Network links and associated properties.
    nodes : dict
        Network nodes and associated properties.
    cyclelinks : list
        List of link ids that comprise a cycle.
    cyclenodes : list
        List of node ids taht comprise a cycle.
    imshape : tuple
        Shape of binary mask as (nrows, ncols).

    Returns
    -------
    links : dict
        Network links and associated properties with the cycle fixed if
        possible.
    nodes : dict
        Network nodes and associated properties with the cycle fixed if
        possible.
    fixed : int
        1 if the cycle was resolved, else 0.

    """

    # dont_reset_algs = [20, 21, 22, 23, 0, 5]
    dont_reset_algs = [
        dy.algmap(key) for key in [
            'manual_set', 'cl_dist_guess', 'cl_ang_guess', 'cl_dist_set',
            'cl_ang_set', 'inletoutlet', 'bridges'
        ]
    ]

    fixed = 1  # One if fix was successful, else zero
    reset = 0  # One if original orientation need to be reset

    # If an artifical node triad is present, flip its direction and see if the
    # cycle is resolved.
    # See if any links are part of an artificial triad
    clset = set(cyclelinks)
    all_pars = []
    for i, pl in enumerate(links['parallels']):
        if len(clset.intersection(set(pl))) > 0:
            all_pars.append(pl)

    # Get continuity violators before flipping
    pre_sourcesink = dy.check_continuity(links, nodes)

    if len(
            all_pars
    ) == 1:  # There is one parallel link set, flip its direction and re-set other cycle links and see if cycle is resolved
        certzero = list(set(all_pars[0] + cyclelinks))
        orig_links = dy.cycle_get_original_orientation(
            links, certzero
        )  # Save the original orientations in case the cycle can't be fixed
        for cz in certzero:
            links['certain'][links['id'].index(cz)] = 0

        # Flip the links of the triad
        for l in all_pars[0]:
            links = lnu.flip_link(links, l)

    if len(
            all_pars
    ) > 1:  # If there are multiple parallel pairs, more code needs to be written for these cases
        print(
            'Multiple parallel pairs in the same cycle. Not implemented yet.')
        return links, nodes, 0

    elif len(
            all_pars
    ) == 0:  # No aritifical node triads; just re-set all the cycle links and see if cycle is resolved
        certzero = cyclelinks
        orig_links = dy.cycle_get_original_orientation(links, certzero)
        for cz in certzero:
            lidx = links['id'].index(cz)
            if links['certain_alg'][lidx] not in dont_reset_algs:
                links['certain'][lidx] = 0

    # Resolve the unknown cycle links
    links, nodes = re_set_linkdirs(links, nodes, imshape)

    # See if the fix violated continuity - if not, reset to original
    post_sourcesink = dy.check_continuity(links, nodes)
    if len(set(post_sourcesink) - set(pre_sourcesink)) > 0:
        reset = 1

    # See if the fix resolved the cycle - if not, reset to original
    cyc_n, cyc_l = dy.get_cycles(links, nodes, checknode=cyclenodes[0])
    if cyc_n is not None and cyclenodes[0] in cyc_n[0]:
        reset = 1

    # Return the links to their original orientations if cycle could not
    # be resolved
    if reset == 1:

        links = dy.cycle_return_to_original_orientation(links, orig_links)

        # Try a second method to fix the cycle: unset all the links of the
        # cycle AND the links connected to those links
        set_to_zero = set()
        for cn in cyclenodes:
            conn = nodes['conn'][nodes['id'].index(cn)]
            set_to_zero.update(conn)
        set_to_zero = list(set_to_zero)

        # Save original orientation in case cycle cannot be fixed
        orig_links = dy.cycle_get_original_orientation(links, set_to_zero)

        for s in set_to_zero:
            lidx = links['id'].index(s)
            if links['certain_alg'][lidx] not in dont_reset_algs:
                links['certain'][lidx] = 0

        links, nodes = re_set_linkdirs(links, nodes, imshape)

        # See if the fix violated continuity - if not, reset to original
        post_sourcesink = dy.check_continuity(links, nodes)
        if len(set(post_sourcesink) - set(pre_sourcesink)) > 0:
            reset = 1

        # See if the fix resolved the cycle - if not, reset to original
        cyc_n, cyc_l = dy.get_cycles(links, nodes, checknode=cyclenodes[0])
        if cyc_n is not None and cyclenodes[0] in cyc_n[0]:
            reset = 1

        if reset == 1:
            links = dy.cycle_return_to_original_orientation(links, orig_links)
            fixed = 0

    return links, nodes, fixed
コード例 #3
0
def fix_delta_cycles(links, nodes, imshape):
    """
    Attempt to resolve cycles within the network.

    Attempts to resolve all cycles within the delta network. This function
    is essentially a wrapper for :func:`fix_delta_cycle`, which is where the
    heavy lifting is actually done. This function finds cycles, calls
    :func:`fix_delta_cycle` on each one, then aggregates the results.

    Parameters
    ----------
    links : dict
        Network links and associated properties.
    nodes : dict
        Network nodes and associated properties.
    imshape : tuple
        Shape of binary mask as (nrows, ncols).

    Returns
    -------
    links : dict
        Network links and associated properties with all possible cycles fixed.
    nodes : dict
        Network nodes and associated properties with all possible cycles fixed.
    allfixed : bool
        True if all cycles were resolved, else False.

    """
    # Tracks if all cycles were fixed
    allfixed = 1

    # Create networkx graph object
    G = nx.MultiDiGraph()
    G.add_nodes_from(nodes['id'])
    for lc in links['conn']:
        G.add_edge(lc[0], lc[1])

    # Check for cycles
    cantfix_links = []
    fixed_links = []
    if nx.is_directed_acyclic_graph(G) is not True:

        # Get list of cycles to fix
        c_nodes, c_links = dy.get_cycles(links, nodes)

        # Combine all cycles that share links
        c_links = dy.merge_list_of_lists(c_links)

        # Fix the cycles
        for ic, cfix_links in enumerate(c_links):
            links, nodes, fixed = fix_delta_cycle(links, nodes, cfix_links,
                                                  imshape)
            if fixed == 0:
                cantfix_links.append(ic)
            elif fixed == 1:
                fixed_links.append(ic)

        # Report
        if len(cantfix_links) > 0:
            allfixed = 0
            print('Could not fix the following cycles (links): {}'.format(
                [c_links[i] for i in cantfix_links]))

        if len(c_links) > 0:
            allfixed = 0
            print(
                'The following cycles (links) were fixed, but should be manually checked: {}'
                .format([c_links[i] for i in fixed_links]))

    else:
        allfixed = 2  # Indicates there were no cycles to fix

    return links, nodes, allfixed
コード例 #4
0
def fix_river_cycles(links, nodes, imshape):
    """
    Attempt to resolve cycles in the network.

    Attempts to resolve all cycles within the river network. This function
    is essentially a wrapper for :func:`fix_river_cycle`, which is where the
    heavy lifting is actually done. This function finds cycles, calls
    :func:`fix_river_cycle` on each one, then aggregates the results.

    Parameters
    ----------
    links : dict
        Network links and associated properties.
    nodes : dict
        Network nodes and associated properties.
    imshape : tuple
        Shape of binary mask as (nrows, ncols).

    Returns
    -------
    links : dict
        Network links and associated properties with all possible cycles fixed.
    nodes : dict
        Network nodes and associated properties with all possible cycles fixed.
    cantfix_links : list of lists
        Contains link ids of unresolvable cycles. Length is equal to number of
        unresolvable cycles.
    cantfix_nodes : TYPE
        Contains node ids of unresolvable cycles. Length is equal to number of
        unresolvable cycles.

    """
    # Create networkx graph object
    G = nx.DiGraph()
    G.add_nodes_from(nodes['id'])
    for lc in links['conn']:
        G.add_edge(lc[0], lc[1])

    # Check for cycles
    cantfix_nodes = []
    cantfix_links = []
    if nx.is_directed_acyclic_graph(G) is not True:

        # Get list of cycles to fix
        c_nodes, c_links = dy.get_cycles(links, nodes)

        # Remove any cycles that are subsets of larger cycles
        isin = np.empty((len(c_links), 1))
        isin[:] = np.nan
        for icn, cn in enumerate(c_nodes):
            for icn2, cn2 in enumerate(c_nodes):
                if cn2 == cn:
                    continue
                elif len(set(cn) - set(cn2)) == 0:
                    isin[icn] = icn2
                    break
        cfix_nodes = [
            cn for icn, cn in enumerate(c_nodes) if np.isnan(isin[icn][0])
        ]
        cfix_links = [
            cl for icl, cl in enumerate(c_links) if np.isnan(isin[icl][0])
        ]

        print('Attempting to fix {} cycles.'.format(len(cfix_nodes)))

        # Try to fix all the cycles
        for cnodes, clinks in zip(cfix_nodes, cfix_links):
            links, nodes, fixed = fix_river_cycle(links, nodes, clinks, cnodes,
                                                  imshape)
            if fixed == 0:
                cantfix_nodes.append(cnodes)
                cantfix_links.append(clinks)

    return links, nodes, cantfix_links, cantfix_nodes