Esempio n. 1
0
    def iterative_cl_pt_mapping(cl, bufdists, side):

        mapper = []
        lines = []
        plt.close()

        old = cl
        for i, bd in enumerate(bufdists):

            new = shapely_offset_ls(cl, bd, side)

            Co, Ao, so = cu.curvars(old.coords.xy[0], old.coords.xy[1])
            Cn, An, sn = cu.curvars(new.coords.xy[0], new.coords.xy[1])

            Ao = np.insert(Ao, 0, 0)
            An = np.insert(An, 0, 0)

            distance, path = fastdtw(Ao, An, dist=euclidean)
            path = np.array(path)

            mapper.append(path)
            lines.append(new)

            old = new

        return lines, mapper
Esempio n. 2
0
def test_inflection_pts():
    """Test inflection_points()."""
    xs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    ys = [1, 4, 9, 16, 25, 16, 9, 4, 1, 0]
    C, _, _ = cu.curvars(xs, ys, unwrap=False)
    # run function
    infs = cu.inflection_points(C)

    # make assertion
    assert np.all(infs == np.array([3, 5]))
Esempio n. 3
0
    def C(self, x=None, y=None):
        """
        Important: curvatures are negativized to match the zs approach
        """
        if x is None:
            x, y, _ = self.__get_x_and_y()

        Cs, _, _ = cu.curvars(x, y, unwrap=True)
        Cs = np.insert(Cs, 0, 0)
        return -Cs
Esempio n. 4
0
def test_curvars():
    """Test unwrap==False."""
    xs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    ys = [1, 4, 9, 16, 25, 16, 9, 4, 1, 0]
    C, Areturn, s = cu.curvars(xs, ys, unwrap=False)

    # make assertions
    assert np.shape(C) == (9,)
    assert np.shape(Areturn) == (9,)
    assert np.shape(s) == (9,)
    assert C[0] == pytest.approx(-0.03131767154315412)
    assert Areturn[4] == pytest.approx(-1.460139105621001)
    assert s[7] == pytest.approx(48.775500247528115)
Esempio n. 5
0
    def shapely_offset_ls(ls, dist, side):
        """
        Just a wrapper around shapely's offset_linestring() function. That
        function adds little barbs sometimes to the end of the offset
        linestring. This function detects and removes those.
        """
        offset = cu.offset_linestring(ls, dist, side)

        # Look for barbs by finding abrupt angle changes
        _, A, _ = cu.curvars(offset.coords.xy[0], offset.coords.xy[1])
        possibles = np.where(
            np.abs(np.diff(A)) > 1.5)[0]  # Threshold set at 1.5 radians

        if len(possibles) == 0:
            return offset
        else:
            st_idx = 0
            en_idx = len(offset.coords) - 1
            for p in possibles:
                if p < len(offset.coords) / 2:
                    st_idx = max(st_idx, p + 1)
                elif p > len(offset.coords) / 2:
                    en_idx = min(en_idx, p)
            offset = LineString(offset.coords[st_idx:en_idx])

        # elif len(possibles) == 1: # Determine if it's the upstream or downstream that's barbed
        #     if possibles[0] > len(A)/2: # Downstream
        #         offset = LineString(zip(offset.coords.xy[0][:possibles[0]], offset.coords.xy[1][:possibles[0]]))
        #     else: # Upstream
        #         offset = LineString(zip(offset.coords.xy[0][possibles[0]:], offset.coords.xy[1][possibles[0]:]))
        # elif len(possibles) == 2:
        #     offset = LineString(zip(offset.coords.xy[0][possibles[0]:possibles[1]], offset.coords.xy[1][possibles[0]:possibles[1]]))
        # else:
        #     # import pdb; pdb.set_trace()
        #     raise Warning('Barbs could not be removed from centerline offset: dist={}, side={}.'.format(dist,side))

        return offset
Esempio n. 6
0
def test_sine_curvature():
    """Use a sine wave to compute curvature."""
    xs = np.linspace(0, 100, 101)
    ys = np.sin(xs) + 5
    C, Areturn, sdist = cu.curvars(xs, ys)
    # make some simple assertions about shape of outputs
    assert C.shape == (100,)
    assert Areturn.shape == (100,)
    assert sdist.shape == (100,)
    # now define this as a centerline
    CL = centerline(xs, ys)
    # smooth the centerline
    CL.window_cl = 10
    CL.smooth(n=2)
    # make some assertions about the smoothing
    assert CL.xs.shape == (101,)
    assert CL.ys.shape == (101,)
    assert np.sum(CL.xs != xs) > 0
    assert np.sum(CL.ys != ys) > 0
    # resample the centerline to 50 points
    CL.resample(50)
    # assert resampled dimensions are as expected
    assert CL.xrs.shape == (50,)
    assert CL.yrs.shape == (50,)
Esempio n. 7
0
def centerline_mesh(coords,
                    width_chan,
                    meshwidth,
                    grid_spacing,
                    smoothing_param=1):
    """
    Generate a centerline mesh.

    Generates a centerline mesh. Differs from :func:`valleyline_mesh` in that
    it draws perpendiculars rather than offsetting the valley line to compute
    mesh polygons. This method is more effective for narrower channels that
    don't require an exceptionally wide mesh (i.e. not much change).

    Parameters
    ----------
    coords :
        2xN list, tuple, np.array (xs, ys) of coordinates defining centerline
    width_chan :
        width of the river in same units of coords
    mesh_dist :
        how wide should the mesh be, in same units of coords
    grid_spacing :
        how far apart should mesh cells be, in same units of coords

    """
    #    coords = ken.centerline
    #    width_chan = ken.width_chans
    #    meshwidth = ken.max_valley_width_pixels * ken.pixlen * 1.1
    #    grid_spacing = meshwidth/10
    #    smoothing_param = 1

    if np.shape(coords)[0] == 2 and np.size(coords) != 4:
        coords = np.transpose(coords)

    # Get lengths along centerline
    s, ds = cu.s_ds(coords[:, 0], coords[:, 1])

    # Mirror centerline manually since scipy f***s it up - only flip the axis that has the largest displacement
    # Mirroring done to avoid edge effects when smoothing
    npad = int(width_chan / np.mean(ds) *
               10)  # Padding fixed at 10 channel widths
    xs_m, ys_m = mirror_line_ends(coords[:, 0], coords[:, 1], npad)

    # A smoothing filter of one-channel width will be passed over the centerline coordinates
    window_len = int(width_chan / np.mean(ds) * smoothing_param)
    if window_len % 2 == 0:  # Window must be odd
        window_len = window_len + 1

    # Smooth
    xs_sm = signal.savgol_filter(xs_m,
                                 window_length=window_len,
                                 polyorder=3,
                                 mode='interp')
    ys_sm = signal.savgol_filter(ys_m,
                                 window_length=window_len,
                                 polyorder=3,
                                 mode='interp')

    # plt.close('all')
    # plt.plot(xs_sm, ys_sm)
    # plt.plot(xs_m, ys_m)
    # plt.axis('equal')

    # Re-sample centerline to even spacing
    s, _ = cu.s_ds(xs_sm, ys_sm)
    npts = int(s[-1] / grid_spacing)
    xy_rs, _ = cu.evenly_space_line(xs_sm, ys_sm, npts)
    xs_rs = xy_rs[0]
    ys_rs = xy_rs[1]

    # Get angles at each point along centerline
    C, A, s = cu.curvars(xs_rs, ys_rs, unwrap=True)

    # Draw perpendiculars at each centerline point
    mesh_hwidth = meshwidth / 2

    # Compute slope of perpendicular (w/ref to dx/dy and dy/dx)
    m_inv_xy = -1 / (np.diff(xs_rs) / np.diff(ys_rs))
    m_inv_yx = -1 / (np.diff(ys_rs) / np.diff(xs_rs))
    # For storing perpendicular points
    perps = []
    for ic in range(len(m_inv_xy)):

        # Compute perpendicular lines based on largest of dx, dy (reduces distortion)
        if m_inv_yx[ic] > m_inv_xy[ic]:
            dx = np.sqrt(mesh_hwidth**2 / (1 + m_inv_yx[ic]**2))
            dy = dx * m_inv_yx[ic]

        else:
            dy = np.sqrt(mesh_hwidth**2 / (1 + m_inv_xy[ic]**2))
            dx = dy * m_inv_xy[ic]

        upper_pt = (xs_rs[ic] + dx, ys_rs[ic] + dy)
        lower_pt = (xs_rs[ic] - dx, ys_rs[ic] - dy)

        perps.append((upper_pt, lower_pt))

    # Now orient perpendiculars so that both sides are continuous
    # NOTE: this method is not guaranteed to work when the grid spacing is much
    # larger than the buffer width (it likely will be fine, but for highly-
    # curved bends failure is possible). There are more robust ways to separate
    # points into left/right bank, but this is quick, dirty, and works for most
    # applications.
    perp_aligned = [perps[0]]
    for ip in range(1, len(perps)):

        left_pre, right_pre = perp_aligned[ip - 1]

        p0 = perps[ip][0]
        p1 = perps[ip][1]

        if np.sqrt((p0[0] - left_pre[0])**2 +
                   (p0[1] - left_pre[1])**2) < np.sqrt(
                       (p1[0] - left_pre[0])**2 + (p1[1] - left_pre[1])**2):
            perp_aligned.append((p0, p1))
        else:
            perp_aligned.append((p1, p0))

    # plt.close('all')
    # plt.plot(xs_rs, ys_rs,'.')
    # plt.axis('equal')
    # for p in perp_aligned:
    #     plt.plot(p[0][0], p[0][1], 'k.')
    #     plt.plot(p[1][0], p[1][1], 'r.')

    # Trim the centerline to remove the mirrored portions
    start_idx = np.argmin(
        np.sqrt((coords[0, 0] - xs_rs)**2 + (coords[0, 1] - ys_rs)**2)) - 1
    end_idx = np.argmin(
        np.sqrt((coords[-1, 0] - xs_rs)**2 + (coords[-1, 1] - ys_rs)**2)) + 1

    # Build the polygon mesh
    polys = []
    for i in range(start_idx, end_idx + 1):
        polys.append([
            perp_aligned[i][0], perp_aligned[i][1], perp_aligned[i + 1][1],
            perp_aligned[i + 1][0], perp_aligned[i][0]
        ])

    perps_out = perp_aligned[start_idx:end_idx + 1]
    cl_resampled = np.r_['1,2,0', xs_rs, ys_rs]
    s_out = s[start_idx:end_idx + 1] - s[start_idx]

    return perps_out, polys, cl_resampled, s_out