示例#1
0
 def _smooth_widths(self, branch, degree):
     '''
     1D Gaussian smoothing of Grid width values.
     '''
     try:
         self.widths[branch] = common.smooth(self.widths[branch], degree)
     except:
         print "ERROR: Cannot smooth grid widths! [Key Error]."
示例#2
0
    def eidw(self,
             power=2.0,
             anisotropy=5.0,
             smoothie=0,
             plane='sn',
             plot=False):
        """
        Performs Elliptical Inverse Distance Weighting (EIDW) interpolation to the instance's data
        
        Kwargs: power <float>      : power parameter, should be above 2.0
                anisotropy <float> : anisotropy parameter: if >1.0, it brings points closer in the
                                     longitudinal (s) direction, if <1.0 it brings points closer 
                                     in the transverse (n) direction
                smoothie <int>     : smoothing degree (Gaussian window)
                plane <str>    : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                plot <boolean> : decides to plot or not the resulting EIDW values
        """
        gc.enable()
        print "Calculating: Elliptical Inverse Distance Weighting [EIDW]"
        #check
        if power < 2.0:
            print "Warning: Power parameter too small; set to 2.0."
            power = 2.0

        eidw = deepcopy(self.z)  #avoid overwrite

        #calculate EIDW (radius='None' executes global EIDW)
        if plane == 'sn':
            eidw[self.nodata] = self.eidw_interpol(self.s[self.isdata],
                                                   self.n[self.isdata],
                                                   eidw[self.isdata],
                                                   self.s[self.nodata],
                                                   self.n[self.nodata], power,
                                                   anisotropy, 'None')
        elif plane == 'xy':
            x = self.x.flatten()
            y = self.y.flatten()
            eidw[self.nodata] = self.eidw_interpol(
                x[self.isdata], y[self.isdata], eidw[self.isdata],
                x[self.nodata], y[self.nodata], power, anisotropy, 'None')
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        #grid (matrix) notation
        eidw = common.to_grid(eidw, self.rows, self.cols)
        #add pre-defined banks
        eidw = self.__add_banks(eidw)
        #smoothen
        for i in range(self.rows):
            eidw[i, :] = common.smooth(eidw[i, :], smoothie)
        print "Finished [EIDW]."
        self.z_interpol = eidw
        del eidw
        #plot
        if plot:
            self.plot(fignum='EIDW-GLOBAL')
示例#3
0
    def rbf(self,
            method='linear',
            anisotropy=5.0,
            smoothie=0,
            plane='sn',
            plot=False):
        """
        Performs Radial Basis Interpolation using the defined method (linear or
        thin plate spline)
        
        Kwargs: method <str>        : choice of RBF interpolation ['linear' or 'tps']
                anisotropy <float>  : anisotropy parameter: if >1.0, it brings points closer in the
                                      longitudinal (s) direction, if <1.0 it brings points closer 
                                      in the transverse (n) direction
                smoothie <int>      : smoothing degree (Gaussian window)
                plane <str>         : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                plot <boolean>      : decides to plot or not the resulting RBF values
        """
        gc.enable()
        print "Calculating: Radial Basis Function (RBF) with {0} method".format(
            method)
        rbf = deepcopy(self.z)  #avoid overwrite

        #choice of plane and rbf interpolation
        if plane == 'sn':
            rbf[self.nodata] = self.rbf_solver(
                self.s[self.isdata], self.n[self.isdata], rbf[self.isdata],
                self.s[self.nodata], self.n[self.nodata], anisotropy, method)
        elif plane == 'xy':
            x = self.x.flatten()
            y = self.y.flatten()
            rbf[self.nodata] = self.rbf_solver(x[self.isdata], y[self.isdata],
                                               rbf[self.isdata],
                                               x[self.nodata], y[self.nodata],
                                               anisotropy, method)
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        rbf = common.to_grid(rbf, self.rows, self.cols)  #griddify

        #add pre-defined banks
        rbf = self.__add_banks(rbf)
        #smoothen
        for i in range(self.rows):
            rbf[i, :] = common.smooth(rbf[i, :], smoothie)
        print "Finished [RBF]."
        self.z_interpol = rbf
        del rbf
        #plot
        if plot:
            self.plot(fignum='RADIAL-BASIS[{0}]'.format(method.upper()))
示例#4
0
    def idw(self, power=2.0, smoothie=0, plane='sn', plot=False):
        """
        Performs Inverse Distance Weighting (IDW) interpolation to the instance's data
        
        Kwargs: power <float>  : power parameter, should be above 2.0
                smoothie <int> : smoothing degree (Gaussian window)
                plane <str>    : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                plot <boolean> : decides to plot or not the resulting IDW values
        """
        gc.enable()
        print "Calculating: Inverse Distance Weighting"
        #check
        if power < 2.0:
            print "Warning: Power parameter too small; set to 2.0."
            power = 2.0

        idw = deepcopy(self.z)  #avoid overwrite

        #calculate IDW (when anisotropy ratio is 1, we have IDW; radius='None' executes global IDW)
        if plane == 'sn':

            idw[self.nodata] = self.eidw_interpol(
                self.s[self.isdata], self.n[self.isdata], idw[self.isdata],
                self.s[self.nodata], self.n[self.nodata], power, 1.0, 'None')
        elif plane == 'xy':
            x = self.x.flatten()
            y = self.y.flatten()
            idw[self.nodata] = self.eidw_interpol(
                x[self.isdata], y[self.isdata], idw[self.isdata],
                x[self.nodata], y[self.nodata], power, 1.0, 'None')
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        #grid (matrix) notation
        idw = common.to_grid(idw, self.rows, self.cols)
        #add pre-defined banks
        idw = self.__add_banks(idw)

        #smoothen
        for i in range(self.rows):
            idw[i, :] = common.smooth(idw[i, :], smoothie)
        print "Finished [IDW]."
        self.z_interpol = idw
        del idw
        #plot
        if plot:
            self.plot(fignum='IDW-GLOBAL')
示例#5
0
    def cubic(self, smoothie=0, plane='sn', fill_nans=True, plot=False):
        """
        Performs Cubic interpolation to the instance's data (piecewise cubic,
        continuously differentiable and approximately curvature-minimizing 
        polynomial surface).
        WARNING: High memory usage, usually in \'xy\' plane
        
        Kwargs: smoothie <int>  : smoothing degree (Gaussian window)
                plane <str>     : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                fill_nans <str> : decides to fill NaN values in a linear manner or keep NaN values
                plot <boolean>  : decides to plot or not the resulting CUBIC values
        """
        gc.enable()
        print "Calculating: Cubic Interpolation"
        cubic = deepcopy(self.z)  #avoid overwrite

        #choice of plane
        if plane == 'sn':
            pnts = np.array(zip(self.s, self.n))
        elif plane == 'xy':
            pnts = np.array(zip(self.x.flatten(), self.y.flatten()))
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        #cubic interpolation
        cubic[self.nodata] = griddata(pnts[self.isdata],
                                      cubic[self.isdata],
                                      pnts[self.nodata],
                                      method='cubic',
                                      fill_value=np.nan)
        cubic = common.to_grid(cubic, self.rows, self.cols)  #griddify
        if fill_nans:
            cubic = self.__fill_nans(cubic)  #linear fill of nan values

        #add pre-defined banks
        cubic = self.__add_banks(cubic)
        #smoothen
        for i in range(self.rows):
            cubic[i, :] = common.smooth(cubic[i, :], smoothie)
        print "Finished [CUBIC]."
        self.z_interpol = cubic
        del cubic
        #plot
        if plot:
            self.plot(fignum='CUBIC')
示例#6
0
    def linear(self, smoothie=0, plane='sn', fill_nans=True, plot=False):
        """
        Performs Linear Barycentric interpolation to the instance's data
        
        Kwargs: smoothie <int>  : smoothing degree (Gaussian window)
                plane <str>     : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                fill_nans <str> : decides to fill NaN values in a linear manner or keep NaN values
                plot <boolean>  : decides to plot or not the resulting LINEAR values
        """
        gc.enable()
        print "Calculating: Linear Barycentric Interpolation"
        linear = deepcopy(self.z)  #avoid overwrite

        #choice of plane
        if plane == 'sn':
            pnts = np.array(zip(self.s, self.n))
        elif plane == 'xy':
            pnts = np.array(zip(self.x.flatten(), self.y.flatten()))
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        #linear interpolation
        linear[self.nodata] = griddata(pnts[self.isdata],
                                       linear[self.isdata],
                                       pnts[self.nodata],
                                       method='linear',
                                       fill_value=np.nan)
        linear = common.to_grid(linear, self.rows, self.cols)  #griddify
        if fill_nans:
            linear = self.__fill_nans(linear)  #linear fill of nan values

        #add pre-defined banks
        linear = self.__add_banks(linear)
        #smoothen
        for i in range(self.rows):
            linear[i, :] = common.smooth(linear[i, :], smoothie)
        print "Finished [LINEAR]."
        self.z_interpol = linear
        del linear
        #plot
        if plot:
            self.plot(fignum='LINEAR-BARYCENTRIC')
示例#7
0
    def near(self, smoothie=0, plane='sn', plot=False):
        """
        Fills values of unsampled data points with the values of the closest
        sampled data point of the instance's data.
        
        Kwargs: smoothie <int>  : smoothing degree (Gaussian window)
                plane <str>     : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                plot <boolean>  : decides to plot or not the result NEAREST values
        """
        gc.enable()
        print "Calculating: Nearest-Neighbour \"Interpolation\""
        near = deepcopy(self.z)  #avoid overwrite

        #choice of plane
        if plane == 'sn':
            pnts = np.array(zip(self.s, self.n))
        elif plane == 'xy':
            pnts = np.array(zip(self.x.flatten(), self.y.flatten()))
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        #find nearest values
        near[self.nodata] = griddata(pnts[self.isdata],
                                     near[self.isdata],
                                     pnts[self.nodata],
                                     method='nearest')
        near = common.to_grid(near, self.rows, self.cols)  #griddify

        #add pre-defined banks
        near = self.__add_banks(near)
        #smoothen
        for i in range(self.rows):
            near[i, :] = common.smooth(near[i, :], smoothie)
        print "Finished [NEAR]."
        self.z_interpol = near
        del near
        #plot
        if plot:
            self.plot(fignum='NEAREST')
示例#8
0
    def fuse(self, ftype='free-form', dthres=0.0, anisotropy=1.0, smoothie=0):
        gc.enable()

        print "Calculating: Fusion method (" + ftype + ")"
        if ftype not in ['free-form', 'cross-sections']:
            print "Warning: Please specify testing dataset type as: \'free-form\' for trackline or free-form data, or \'cross-sections\' for cross-sectional data."
            print "Assuming free-form data."
            ftype = 'free-form'

        s, n = self.s.flatten(), self.n.flatten()
        isdata = self.isdata.flatten()
        nodata = self.nodata.flatten()

        #if samples in free-form (ie tracklines)
        if ftype == 'free-form':
            #check
            dthres = abs(float(dthres))

            #distance matrix:
            s0, n0 = s[isdata], n[isdata]  #sampled
            s1, n1 = s[nodata], n[nodata]  #unsampled
            dm = common.distance_matrix(s0, n0, s1, n1, anisotropy)  #matrix

            #if no threshold is defined, calculate the furthest possible point
            # = this is where bathymetry has the most impact
            dmins = dm.min(axis=0)
            if dthres <= 0:
                dthres = dmins.max()
            print "Threshold Distance:", dthres

            #weights
            w_bth = dmins / dthres  #weights for model dataset
            w_bth[w_bth > 1.0] = 1.0  #adjust to range of [0,1]
            w_int = 1.0 - w_bth  #weights for interpolation dataset
            del dm, dmins, s0, n0, s1, n1  #clear

        #if samples in cross-sections
        else:
            #find cross-sections and the points where there is data (clustering of data):
            cs_where = []
            for j in range(self.cols):
                if np.nansum(self.isdata[:, j]) != 0:
                    cs_where.append(j)
                    print "Cross-section at: ", j

            #Find minimum distances from 2 different cross-sections
            ratios = np.ones((self.rows * self.cols, 2)) * np.inf
            for j in cs_where:
                dist_from_cs = common.distance_matrix(
                    self.s[:, j][self.isdata[:, j]],
                    self.n[:, j][self.isdata[:, j]], s, n, anisotropy)
                cs_mindist = np.min(dist_from_cs, axis=0)
                change = cs_mindist < ratios[:, 1]
                ratios[:, 1][change] = cs_mindist[change]
                ratios = np.sort(ratios,
                                 axis=1)  #holds min distances from 2 cs
                del dist_from_cs

            #weights
            w_int = []
            w_bth = []
            for i in range(len(ratios)):
                #real distances
                d1 = ratios[i, 0]  #distance from one cs
                d2 = ratios[i, 1]  #distance from another cs
                if d1 == 0.0:
                    d1 = 1e-20  #avoid division by 0
                if d2 == 0.0:
                    d2 = 1e-20  #avoid division by 0
                hd = (d1 + d2) / 2.0  #half-distance
                if d1 < hd:
                    d2 -= hd
                    r1 = 1. / (1. + (d2 / d1)**2)
                else:
                    d1 -= hd
                    r1 = 1. / (1. + (d1 / d2)**2)
                if r1 > 1.0:
                    r1 = 1.0
                r2 = 1.0 - r1
                w_bth.append(r1)
                w_int.append(r2)
            w_int = np.array(w_int)[nodata]
            w_bth = np.array(w_bth)[nodata]

        #In both cases, fused dataset is the sum of the weighted datasets
        I = self.inter.flatten()
        M = self.model.flatten()
        fusion = self.z.flatten()  #initialize, so to keep measured data values
        fusion[nodata] = I[nodata] * w_int + M[nodata] * w_bth
        fusion = common.to_grid(fusion, self.rows,
                                self.cols)  #grid (matrix) notation
        del w_bth, w_int  #clear

        #smoothen the longitudinals, if required
        if smoothie > 0:
            for i in range(self.rows):
                fusion[i, :] = common.smooth(fusion[i, :], smoothie)
            if smoothie > int(fusion.shape[0] / 4):
                smoothie = int(fusion.shape[0] / 4)
            for i in range(self.cols):
                fusion[:, i] = common.smooth(fusion[:, i], smoothie)

        print "Finished [FUSION]."
        self.fused = fusion
        del fusion
示例#9
0
    def natneigh(self, anisotropy=1.0, smoothie=0, plane='sn', plot=False):
        """
        Performs Natural Neighbor (NN) interpolation with defined anisotropy 
        to the instance's data. The algorithm assumes a brute-force way of 
        calculating the Voronoi cells every time an unsampled point's value is
        computed.
        
        Kwargs: anisotropy <float>  : anisotropy parameter: if >1.0, it brings points closer in the
                                      longitudinal (s) direction, if <1.0 it brings points closer 
                                      in the transverse (n) direction
                smoothie <int>      : smoothing degree (Gaussian window)
                plane <str>         : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                plot <boolean>      : decides to plot or not the resulting NN values
        """

        from scipy.spatial import voronoi_plot_2d
        import matplotlib.pyplot as plt

        gc.enable()
        print "Calculating: Natural Neighbour [NN]"

        nn = deepcopy(self.z)  #avoid overwrite

        #choose plane
        if plane == 'sn':
            x = self.s * 1. / anisotropy  #already flattened
            y = self.n  #already flattened
        elif plane == 'xy':
            x = self.x.flatten() * 1. / anisotropy
            y = self.y.flatten()
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        #DEFINE BOUNDARY FOR INTERPOLATION
        #griddify points to choose boundary easier:
        Gx = common.to_grid(x, self.rows, self.cols)
        Gy = common.to_grid(y, self.rows, self.cols)
        bx = np.hstack((Gx[0, :], Gx[1:-1, -1], Gx[-1, :][::-1], Gx[1:-1,
                                                                    0][::-1]))
        by = np.hstack((Gy[0, :], Gy[1:-1, -1], Gy[-1, :][::-1], Gy[1:-1,
                                                                    0][::-1]))
        #define boundary:
        boundary = np.array(zip(bx, by))

        #VORONOI OF SAMPLED DATA POINTS:
        vorpoints = np.array(zip(x[self.isdata], y[self.isdata]))
        #shift points around central point of the dataset (for precision purposes)
        center = vorpoints.mean(axis=0)
        vorpoints -= center
        #construct Voronoi diagram from (centered) sampled data points
        vor = Voronoi(vorpoints)
        vor.close()
        """
        #TODO: delete:
        voronoi_plot_2d(vor)
        plt.ion()
        plt.axis('equal')
        plt.show()
        """
        #calculate areas of sampled dataset Voronoi cells
        original_areas, vor = self.__find_areas(vor, boundary - center)
        """
        #TODO: delete:        
        # colorize
        for region in vor.regions[1:]:
            polygon = vor.vertices[region]
            plt.fill(*zip(*polygon), alpha=0.4)
        plt.plot(vorpoints[:,0], vorpoints[:,1], 'ko')
        plt.xlim(vor.min_bound[0] - 0.1, vor.max_bound[0] + 0.1)
        plt.ylim(vor.min_bound[1] - 0.1, vor.max_bound[1] + 0.1)
        plt.axis('equal')
        plt.ion()
        plt.show()
        """

        #ITERATE THROUGH UNSAMPLED DATA POINTS:
        # ~ For each unsampled point, construct the new Voronoi diagram consisting of the
        # ~ sampled data and the current unsampled point. Then calculate the new areas and
        # ~ find the normalized weights based on how much of each area is "stolen away" from
        # ~ each sampled dataset Voronoi cell (https://en.wikipedia.org/wiki/Natural_neighbor).
        # ~ The areas are always bounded by the river polygon (based on the grid defined).
        unknown = []
        for i in range(len(x[self.nodata])):
            if i % 1000 == 0:
                print i, "out of ", len(x[self.nodata])
            #add new point shifted around central point
            varys = np.vstack((vorpoints, [
                x[self.nodata][i] - center[0], y[self.nodata][i] - center[1]
            ]))

            #calculate new Voronoi
            pntvor = Voronoi(varys)
            pntvor.close()

            #calculate areas
            new_areas, pntvor = self.__find_areas(pntvor, boundary - center)
            new_areas = new_areas[:-1]  #exclude new point's area
            w = new_areas / original_areas
            w[w >
              1.0] = 1.0  #make sure that no area is larger than initial areas
            areaweight = 1.0 - w
            normalize = np.nansum(areaweight)
            if normalize == 0.0:  #to avoid division by 0
                normalize = 1e-12
            areaweight /= normalize
            unknown.append(np.nansum(areaweight * self.z[self.isdata]))

        nn[self.nodata] = np.array(unknown)

        #grid (matrix) notation
        nn = common.to_grid(nn, self.rows, self.cols)
        #add pre-defined banks
        nn = self.__add_banks(nn)
        #smoothen
        for i in range(self.rows):
            nn[i, :] = common.smooth(nn[i, :], smoothie)
        print "Finished [NN]."
        self.z_interpol = nn
        del nn
        #plot
        if plot:
            self.plot(fignum='NN')
示例#10
0
    def uk(self,
           sill,
           vmodel='spherical',
           anisotropy=1.0,
           smoothie=0,
           plane='sn',
           plot=False):
        """
        Performs Universal Kriging (UK) interpolation with defined anisotropy 
        to the instance's data using the specified model for its semivariogram.
        The sill for the semivariogram needs to be defined. The range used is
        equal to the sill times the anisotropy defined.
        
        Args:
                sill <float>        : Sill value for semivariogram in UK
        
        Kwargs: vmodel <str>        : Semivariogram model (default: 'spherical')
                anisotropy <float>  : anisotropy parameter: if >1.0, it brings points closer in the
                                      longitudinal (s) direction, if <1.0 it brings points closer 
                                      in the transverse (n) direction
                smoothie <int>      : smoothing degree (Gaussian window)
                plane <str>         : chooses plane for interpolation; 'xy' for Cartesian, 'sn' for flow-oriented
                plot <boolean>      : decides to plot or not the resulting UK values
        """
        gc.enable()
        print "Calculating: Universal Kriging [UK]"

        uk = deepcopy(self.z)  #avoid overwrite

        #calculate UK with anisotropy defined
        if plane == 'sn':
            UK = UniversalKriging(
                self.s[self.isdata],
                self.n[self.isdata],
                uk[self.isdata],
                anisotropy_scaling=anisotropy,
                anisotropy_angle=0.0,
                nlags=self.cols,
                variogram_model=vmodel,
                variogram_parameters=[sill, sill * anisotropy, 0.0],
                drift_terms=['functional'],
                functional_drift=[self.uk_funct],
                verbose=True,
                enable_plotting=True)
            uk[self.nodata], sigmas = UK.execute('points', self.s[self.nodata],
                                                 self.n[self.nodata])
        elif plane == 'xy':
            x = self.x.flatten()
            y = self.y.flatten()
            UK = UniversalKriging(
                x[self.isdata],
                y[self.isdata],
                uk[self.isdata],
                anisotropy_scaling=anisotropy,
                anisotropy_angle=0.0,
                nlags=self.cols,
                variogram_model=vmodel,
                variogram_parameters=[sill, sill * anisotropy, 0.0],
                drift_terms=['regional_linear'],
                functional_drift=[self.uk_funct],
                verbose=False,
                enable_plotting=False)
            uk[self.nodata], sigmas = UK.execute('points', x[self.nodata],
                                                 y[self.nodata])
        else:
            print "Error: Plane for interpolation not correctly specified. No interpolation performed."
            return

        #grid (matrix) notation
        uk = common.to_grid(uk, self.rows, self.cols)
        #add pre-defined banks
        uk = self.__add_banks(uk)
        #smoothen
        for i in range(self.rows):
            uk[i, :] = common.smooth(uk[i, :], smoothie)
        print "Finished [UK]."
        self.z_interpol = uk
        del uk
        #plot
        if plot:
            self.plot(fignum='UK')
示例#11
0
    def create_dep(self, branches='all', 
                   Q = 500, C = 45, i_slope = 9e-5, D50 = 300e-6, a = 10, WL = 0, 
                   deg = 20, system = 'm', banks_profile ='linear', curvature = 'global',
                   fix_CbyH = False, fix_CbyB = False, fix_abyB = False,
                   lonsmooth = False, transmooth = True, 
                   confined_width = True, adjust_byArea = False, remove_shallow_banks = False):
        
        if branches=='all':
            branches = self.gr.grid.keys()
        elif type(branches) != type([]):
            print "Please provide names of branches in a list.\n"
            return

        for branch in branches:

            print "Calculating bed levels for branch {0}".format(branch)
            
            try:
                #In order to extract the local curvature, it must be calculated on each point of the grid
                self.bath[branch] = {}
                Gx = self.gr.grid[branch]['x']
                Gy = self.gr.grid[branch]['y']
                k = self.__get_curvature(Gx,Gy,deg,which=curvature)
                widths = self.gr.widths[branch]
                
                #TODO: Experimenting with further smoothing: S-Golay smoothing on curvature (per longitudinal):
                if deg>=3:
                    if deg%2 != 1:
                        smoothfactor = int(deg - deg%2 + 1)
                    else:
                        smoothfactor = int(deg)
                    for q in range(k.shape[0]):
                        k[q,:] = common.savitzky_golay(k[q,:], smoothfactor, 1)
                
                
                #TODO: delete!
                s = np.array(self.gr._get_s_coordinates('1'))
                w = np.array(widths)
                if system=='ft':
                    s*=0.3048
                    w*=0.3048
                curv = k[k.shape[0]/2,:]
                """
                #TODO: LAG????
                curv_s = s + w/2.0 #ASSUMPTION!!!!
                curv_ls = LineString([(0,curv[0])]+zip(curv_s,curv))
                curv_new = []
                for i in s:
                    curv_new.append(curv_ls.interpolate(i).coords[:][0][1])
                curv_new = np.array(curv_new)
                for i in range(k.shape[0]):
                    k[i,:] = curv_new
                """
                plt.figure('WIDTH VS CURVATURE')
                plt.subplot(2,1,1)
                plt.plot(s,w)
                plt.ylabel('widths (m)')
                plt.subplot(2,1,2)
                plt.plot(s,curv)
                plt.ylabel('curvature [-]')
                plt.xlabel('Longitudinal distance (m)')
                plt.ion()
                plt.show()
                
                norow, nocon = self.gr.grid[branch]['x'].shape                
            except KeyError:
                print 'invalid key or parameter value'
                return
            
            #If the data are in feet, change widths into meters for equations to work
            if system=='ft':
                B = np.array(widths) * 0.3048
                if self.banks:
                    self.banks = [j*0.3048 for j in self.banks]
            else:
                B = np.array(widths)
            
            #TODO: DELETE!!
            if fix_CbyB:
                C *= B/np.average(B)
            #TODO: ???
            #k *= np.average(B)/B
            #a += (np.average(B)/B)
            if fix_abyB:
                a *= np.average(B)/B
            
            ### FIND BANKS AND ORIGINAL AREAS ###
            hc_ref = ( float(Q) / ( B * C * i_slope**0.5 ) ) ** (2.0/3.0)
            if fix_CbyH:
                href = np.average(hc_ref)
                p = 1./6
                hc_original = hc_ref**(3./(2*p+3.)) * href**(2*p/(2*p+3.))
                Cp = (hc_original/href)**p * C #fix Chezy
                #hc = hc_ref**(3./(3.-2*p)) * href**(-2*p/(3.-2*p))
                #Cp = (href/hc)**p * C #fix Chezy
                #TODO: delete
                plt.figure('WIDTH VS Chezy')
                plt.subplot(2,1,1)
                plt.plot(s,w)
                plt.ylabel('widths (m)')
                plt.subplot(2,1,2)
                plt.plot(s,Cp)
                plt.ylabel('Chezy coeff')
                plt.xlabel('Longitudinal distance (m)')
                plt.ion()
                plt.show()
            else:
                hc_original = hc_ref
                Cp = C * np.ones(hc_original.shape)
            E = 0.0944 * ((hc_original/D50)**0.3)
            delta = (self.rhos-self.rhow)/self.rhow
            theta = 1.0 / (Cp**2 * delta * D50)
            ft = (0.85/E) * (theta**0.5)
            Rcinv = -k
            karman = 0.44
            g = 9.80665
            A = (2.0*a)/(karman**2) * (1.0 - np.sqrt(g)/(karman*Cp) )
            hf = np.ones((norow, nocon)) * np.nan #initialize
            bankslope_width_r = []
            bankslope_width_l = []
            original_areas = []
            #for each cross-section:
            for j in range(nocon):
                n = np.linspace(-B[j]/2.0,B[j]/2.0,norow)
                power = A[j] * ft[j] * n * Rcinv[:,j]
                hf[:,j] = hc_original[j] * np.exp(power)
                
                #areas of original theory calculations
                xy = [(-B[j]/2.0,0.0)] + zip(n,hf[:,j]) + [(B[j]/2.0,0.0)] #close polygon
                p = Polygon(xy)
                original_areas.append(p.area)
                
                #TODO: DELETE!
                if j==300:
                    store1 = np.array(hf[:,j][::])
                #TODO: DELETE!
                if j==350:
                    store2 = np.array(hf[:,j][::])
                
                #compute banks widths:
                if self.banks:
                    if self.banks[0] > B[j]/3.0:
                        #TODO: delete next line's print!
                        print j, self.banks[0], B[j], self.banks[0]/B[j]
                        bankslope_width_r.append(B[j]/3.0)
                    else:
                        bankslope_width_r.append(self.banks[0])
                    if self.banks[1] > B[j]/3.0:
                        #TODO: delete next line's print!
                        print j, self.banks[1], B[j], self.banks[1]/B[j]
                        bankslope_width_l.append(B[j]/3.0)
                    else:
                        bankslope_width_l.append(self.banks[1])
                else:
                    add = max(hf[:,j]) #find maximum depth calculated
                    #check that banks do not exceed computable size
                    if add > B[j]/3.0:
                        #TODO: delete print!
                        print j, add, B[j], add/B[j]
                        add = B[j]/3.0
                    bankslope_width_r.append(add)
                    bankslope_width_l.append(add)
            bankslope_width_r = np.array(bankslope_width_r)
            bankslope_width_l = np.array(bankslope_width_l)
            
            #If chosen, the banks on the shallow side of the river bend can be excluded
            if remove_shallow_banks:
                sign = np.sign(Rcinv[int(norow/2),:])
                bankslope_width_r[sign>0] = 0.0
                bankslope_width_l[sign<0] = 0.0
                
            """
            #TODO: delete!
            shallowest_points = []
            for j in range(nocon):
                shallowest_points.append(min(hf[:,j]))
            plt.figure('Shallows')
            plt.plot(s,shallowest_points)
            plt.ion()
            plt.show()
            """
            
            ### ZERO-ORDER ####################################################
            if confined_width:
                B_model = B - (bankslope_width_r + bankslope_width_l)
            else:
                B_model = B
            hc_ref = ( float(Q) / ( B_model * C * i_slope**0.5 ) ) ** (2.0/3.0)
            if fix_CbyH:
                href = np.average(hc_ref)
                p = 1./6
                hc = hc_ref**(3./(2*p+3.)) * href**(2*p/(2*p+3.))
                Cp = (hc/href)**p * C #fix Chezy
                #hc = hc_ref**(3./(3.-2*p)) * href**(-2*p/(3.-2*p))
                #Cp = (href/hc)**p * C #fix Chezy
            else:
                hc = hc_ref
                Cp = C * np.ones(hc.shape)
            ###################################################################
            
            ### AXI-SYMMETRIC: ################################################
            E = 0.0944 * ((hc/D50)**0.3)
            delta = (self.rhos-self.rhow)/self.rhow
            theta = 1.0 / (Cp**2 * delta * D50)
            ft = (0.85/E) * (theta**0.5)
            Rcinv = -k
            karman = 0.44
            g = 9.80665
            A = 2.0*a/karman**2 * (1.0 - np.sqrt(g)/(karman*Cp) )
            hf = np.ones((norow, nocon)) * np.nan #initialize
            new_areas = []
            for j in range(nocon): #for each cross-section
                n = np.linspace(-B[j]/2.0,B[j]/2.0,norow) #calculate for whole area; excess will be "cut down" by the bank profiles
                power = A[j] * ft[j] * n * Rcinv[:,j]
                hf[:,j] = hc[j] * np.exp(power)
                #if there are banks described:
                discrete_chunks = B[j] / (norow-1.0)
                #right bank:
                if (bankslope_width_r[j]>0.0):
                    mr = int(np.round(bankslope_width_r[j]/discrete_chunks))
                    if mr == 0:
                        mr = 1 #at least 1
                    if banks_profile == 'linear':
                        sloppy_r = np.linspace( 0.0, hf[mr,j], mr+1 ) #right
                    elif banks_profile == 'hyper':
                        spread = np.linspace( -np.sinh(1.0), np.sinh(1.0), mr+1 )
                        sloppy_r = (np.arcsinh(spread)+1.0) * hf[mr,j]/2.0 #right
                    hf[:mr+1,j] = sloppy_r #attach right bank
                    bankslope_width_r[j] = mr*discrete_chunks
                #left bank:
                if (bankslope_width_l[j]>0.0):
                    ml = int(np.round(bankslope_width_l[j]/discrete_chunks))
                    if ml == 0:
                        ml = 1 #at least 1
                    if banks_profile == 'linear':
                        sloppy_l = np.linspace( 0.0, hf[norow-ml-1,j], ml+1 ) #left
                    elif banks_profile == 'hyper':
                        spread = np.linspace( -np.sinh(1.0), np.sinh(1.0), ml+1)
                        sloppy_l = (np.arcsinh(spread)+1.0) * hf[norow-ml-1,j]/2.0 #left
                    hf[norow-ml-1:,j] = sloppy_l[::-1] #attach left bank
                    bankslope_width_l[j] = ml*discrete_chunks
                    
                #areas
                xy = zip(n,hf[:,j])
                if hf[-1,j] != 0:
                    xy = xy + [(B[j]/2.0,0.0)]
                if hf[0,j] != 0:
                    xy = [(-B[j]/2.0,0.0)] + xy
                new_areas.append(Polygon(xy).area)
                
                #if an adjustment to initially computed areas is chosen:
                if adjust_byArea:
                    Adiff = original_areas[j] - new_areas[j]
                    h_plus = Adiff / ( B[j] - (bankslope_width_l[j]+bankslope_width_r[j])/2.0 )
                    hf[:,j] += h_plus
                    hc[j] += h_plus
                    #FIX banks:
                    if (bankslope_width_r[j]>0.0):
                        if banks_profile == 'linear':
                            sloppy_r = np.linspace( 0.0, hf[mr,j], mr+1 ) #right
                        elif banks_profile == 'hyper':
                            spread = np.linspace( -np.sinh(1.0), np.sinh(1.0), mr+1 )
                            sloppy_r = (np.arcsinh(spread)+1.0) * hf[mr,j]/2.0 #right
                        hf[:mr+1,j] = sloppy_r #attach right bank
                    #left bank:
                    if (bankslope_width_l[j]>0.0):
                        if banks_profile == 'linear':
                            sloppy_l = np.linspace( 0.0, hf[norow-ml-1,j], ml+1 ) #left
                        elif banks_profile == 'hyper':
                            spread = np.linspace( -np.sinh(1.0), np.sinh(1.0), ml+1)
                            sloppy_l = (np.arcsinh(spread)+1.0) * hf[norow-ml-1,j]/2.0 #left
                        hf[norow-ml-1:,j] = sloppy_l[::-1] #attach left bank
                    #AREAS
                    #recalculate final areas for comparison purposes
                    xy = zip(n,hf[:,j])
                    if hf[-1,j] != 0:
                        xy = xy + [(B[j]/2.0,0.0)]
                    if hf[0,j] != 0:
                        xy = [(-B[j]/2.0,0.0)] + xy
                    new_areas[j] = Polygon(xy).area
                        
                        
            #store finalized 0-order:
            if system == 'ft': #if specified system is in feet
                self.bath[branch]['hc'] = hc / 0.3048 #return notation back to feet
            else:
                self.bath[branch]['hc'] = hc                        
                        
            #TODO: delete graph
            plt.figure("MODEL_AREAS")
            plt.hold(True)
            plt.plot(s,original_areas)
            plt.plot(s,new_areas)
            plt.hold(False)
            plt.ion()
            plt.show()
            
            
                
            

##TODO: Inspect: 2D EXPERIMENTAL SMOOTHING:
#            hf = common.smooth2D(hf, 0.0, deg/2, 3.0, 1.0, 5.0) #OR:
#            hf[bankslope_width:norow-bankslope_width+1,:] = \
#                     common.smooth2D(hf[bankslope_width:norow-bankslope_width+1,:], 0.0, deg/2, 5.0, 1.0, 5.0)
#
            ### SMOOTHING ###
            #transverse
            if transmooth:
                for j in range(nocon): #smoothen each cross-section
                    hf[:,j] = common.smooth(hf[:,j], int(0.2*norow)) #Smoothing window: 20% of cross-section width
                #TODO: Should the side banks be returned to expected 0-water level after smoothing?
                #hf[0,:] = 0.0
                #hf[-1,:] = 0.0
            #longitudinal
            if lonsmooth and deg>0:
                for i in range(norow):  #smoothen each row on grid
                    hf[i,:] = common.smooth(hf[i,:], deg)
                
#TODO: delete - creates a few cross-sectional profiles for representation reasons ## all based on water level = 0
            plt.figure("a cross-section")#, figsize=(10,10))
            plt.hold(True)
            for j in range(hf.shape[1]): #smoothen each cross-section
                if j==300: #in range(0,1500,120):
                    bbb = np.linspace(-B[j]/2.0, B[j]/2.0, norow)
                    plt.plot(bbb,-hf[:,j],'.-',label=str(j))
                    plt.plot(bbb,-store1,'.-',label=str(j))
                if j==350: #in range(0,1500,120):
                    bbb = np.linspace(-B[j]/2.0, B[j]/2.0, norow)
                    plt.plot(bbb,-hf[:,j],'.-',label=str(j))
                    plt.plot(bbb,-store2,'.-',label=str(j))
            plt.hold(False)
            axes = plt.gca()
            axes.set_ylim([-60,40])
            plt.axis('equal')
            plt.show()

            if system == 'ft': #if specified system is in feet
                hf /= 0.3048 #return notation back to feet
                if self.banks:
                    self.banks = [j/0.3048 for j in self.banks]
            self.bath[branch]['h'] = hf #water levels
            
            Salong = self.gr._get_s_coordinates(branch)
            #Adjust to bed levels (assuming WL = Water Level at start of river section, following the water flow)
            WLdecrease = ( Salong * i_slope ) / ( (i_slope**2 + 1.0)**0.5 )
            self.bath[branch]['bedlvl'] = (WL-WLdecrease) - hf  #returns feet if feet, or meters if meters
            
            print "Model bed levels calculated for branch: {0}!".format(branch)