def merge(tree, tol=0.1): """Remove inner branches that are short, combining branchpoints as needed.""" for node in list(tree.preOrder()): if node.segment.length < tol: logging.info( " ...cleaned inner segment of length %g at centroid %r" % (node.segment.length, node.segment.centroid.coords[0])) for child in node.children: child.segment = shapely.geometry.LineString( child.segment.coords[:-1] + [ node.parent.segment.coords[0], ]) node.parent.addChild(child) node.remove()
def simplify(tree, tol=0.1): """Simplify, IN PLACE, all tree segments.""" for node in tree.preOrder(): if node.segment is not None: node.segment = node.segment.simplify(tol)
def snap_endpoints(tree, hucs, tol=0.1): """Snap river endpoints to huc segments and insert that point into the boundary. Note this is O(n^2), and could be made more efficient. """ to_add = [] for node in tree.preOrder(): river = node.segment for b, component in itertools.chain(hucs.boundaries.items(), hucs.intersections.items()): # note, this is done in two stages to allow it deal with both endpoints touching for s, seg_handle in component.items(): seg = hucs.segments[seg_handle] #logging.debug("SNAP P0:") #logging.debug(" huc seg: %r"%seg.coords[:]) #logging.debug(" river: %r"%river.coords[:]) altered = False logging.debug(" - checking river coord: %r" % list(river.coords[0])) logging.debug(" - seg coords: {0}".format(list(seg.coords))) new_coord = _snap_and_cut(river.coords[0], seg, tol) logging.debug(" - new coord: {0}".format(new_coord)) if new_coord is not None: logging.info(" snapped river: %r to %r" % (river.coords[0], new_coord)) # move new_coord onto an existing segment coord dist = np.linalg.norm(np.array(seg.coords) - np.expand_dims(new_coord, 0), 2, axis=1) assert (len(dist) == len(seg.coords)) assert (len(dist.shape) == 1) i = int(np.argmin(dist)) if (dist[i] < tol): new_coord = seg.coords[i] # remove points that are closer coords = list(river.coords) done = False while len(coords) > 2 and workflow.utils.distance(new_coord, coords[1]) < \ workflow.utils.distance(new_coord, coords[0]): coords.pop(0) coords[0] = new_coord river = shapely.geometry.LineString(coords) node.segment = river to_add.append((seg_handle, component, 0, node)) break # second stage for s, seg_handle in component.items(): seg = hucs.segments[seg_handle] # logging.debug("SNAP P1:") # logging.debug(" huc seg: %r"%seg.coords[:]) # logging.debug(" river: %r"%river.coords[:]) altered = False logging.debug(" - checking river coord: %r" % list(river.coords[-1])) logging.debug(" - seg coords: {0}".format(list(seg.coords))) new_coord = _snap_and_cut(river.coords[-1], seg, tol) logging.debug(" - new coord: {0}".format(new_coord)) if new_coord is not None: logging.info(" - snapped river: %r to %r" % (river.coords[-1], new_coord)) # move new_coord onto an existing segment coord dist = np.linalg.norm(np.array(seg.coords) - np.expand_dims(new_coord, 0), 2, axis=1) assert (len(dist) == len(seg.coords)) assert (len(dist.shape) == 1) i = int(np.argmin(dist)) if (dist[i] < tol): new_coord = seg.coords[i] # remove points that are closer coords = list(river.coords) done = False while len(coords) > 2 and workflow.utils.distance(new_coord, coords[-2]) < \ workflow.utils.distance(new_coord, coords[-1]): coords.pop(-1) coords[-1] = new_coord river = shapely.geometry.LineString(coords) node.segment = river to_add.append((seg_handle, component, -1, node)) break # find the list of points to add to a given segment to_add_dict = dict() for seg_handle, component, endpoint, node in to_add: if seg_handle not in to_add_dict.keys(): to_add_dict[seg_handle] = list() to_add_dict[seg_handle].append((component, endpoint, node)) # find the set of points to add to each given segment def equal(p1, p2): if workflow.utils.close(p1[2].segment.coords[p1[1]], p2[2].segment.coords[p2[1]], 1.e-5): assert (p1[0] == p2[0]) return True else: return False to_add_dict2 = dict() for seg_handle, insert_list in to_add_dict.items(): new_list = [] for p1 in insert_list: if (all(not equal(p1, p2) for p2 in new_list)): new_list.append(p1) to_add_dict2[seg_handle] = new_list # add these points to the segment for seg_handle, insert_list in to_add_dict2.items(): seg = hucs.segments[seg_handle] # make a list of the coords and a flag to indicate a new # coord, then sort it by arclength along the segment. # # Note this needs special care if the seg is a loop, or else the endpoint gets sorted twice if not workflow.utils.close(seg.coords[0], seg.coords[-1]): new_coords = [[p[2].segment.coords[p[1]], 1] for p in insert_list] old_coords = [[c, 0] for c in seg.coords if not any( workflow.utils.close(c, nc, tol) for nc in new_coords)] new_seg_coords = sorted( new_coords + old_coords, key=lambda a: seg.project(shapely.geometry.Point(a))) # determine the new coordinate indices breakpoint_inds = [ i for i, (c, f) in enumerate(new_seg_coords) if f is 1 ] else: new_coords = [[p[2].segment.coords[p[1]], 1] for p in insert_list] old_coords = [[c, 0] for c in seg.coords[:-1] if not any( workflow.utils.close(c, nc, tol) for nc in new_coords)] new_seg_coords = sorted( new_coords + old_coords, key=lambda a: seg.project(shapely.geometry.Point(a))) breakpoint_inds = [ i for i, (c, f) in enumerate(new_seg_coords) if f is 1 ] assert (len(breakpoint_inds) > 0) new_seg_coords = new_seg_coords[ breakpoint_inds[0]:] + new_seg_coords[0:breakpoint_inds[0] + 1] new_seg_coords[0][1] = 0 new_seg_coords[-1][1] = 0 breakpoint_inds = [ i for i, (c, f) in enumerate(new_seg_coords) if f is 1 ] # now break into new segments new_segs = [] ind_start = 0 for ind_end in breakpoint_inds: assert (ind_end is not 0) new_segs.append( shapely.geometry.LineString( [c for (c, f) in new_seg_coords[ind_start:ind_end + 1]])) ind_start = ind_end assert (ind_start < len(new_seg_coords) - 1) new_segs.append( shapely.geometry.LineString( [tuple(c) for (c, f) in new_seg_coords[ind_start:]])) # put all new_segs into the huc list. Note insert_list[0][0] is the component hucs.segments[seg_handle] = new_segs.pop(0) new_handles = hucs.segments.add_many(new_segs) insert_list[0][0].add_many(new_handles) return river
def snap_crossings(hucs, rivers, tol=0.1): """Snaps HUC boundaries and rivers to crossings.""" for tree in rivers: for river_node in tree.preOrder(): _snap_crossing(hucs, river_node, tol)