diff --git a/README.md b/README.md
index aafc77b..b6fbde9 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,86 @@
-# VBET 2.0
-Valley Bottom Extraction Tool (upated: 02/22/2021)
+# VBET 2 v2.1
+Valley Bottom Extraction Tool (updated: 04/18/2022)
-VBET uses a stream network shapefile and digital elevation model to derive a valley bottom polygon based on two lines of evidence: slope and inundation depth. The values for these parameters are adjusted based on the drainage area of a given segment of the network based on the generalizations that at lower drainage areas (small streams), slopes within valley bottoms are generally steeper than at high drainage areas (larger rivers), where slopes tend to be very flat in valley bottoms; and also that high in the network, at low drainage areas, flood depths tend to be shallower than in larger portions of the network.
+VBET uses a stream network shapefile and digital elevation model to derive a valley bottom
+polygon based on two lines of evidence: slope and inundation depth. The values for these
+parameters are adjusted based on the drainage area of a given segment of the network based
+on the generalizations that at lower drainage areas (small streams), slopes within valley
+bottoms are generally steeper than at high drainage areas (larger rivers), where slopes tend
+to be very flat in valley bottoms; and also that high in the network, at low drainage areas,
+flood depths tend to be shallower than in larger portions of the network.
-As such, the user selects to drainage area threshold values that are used to split the network into 'large', 'medium' and 'small' portions. The user then selects threshold values for slope and inundation depth for each of the three portions of the network.
+As such, the user selects to drainage area threshold values that are used to split the
+network into 'large', 'medium' and 'small' portions. The user then selects threshold values
+for slope and inundation depth for each of the three portions of the network.
### Known issues
-When deriving a stream network from a DEM, segments passing through lakes or resersvoirs sometimes are completely straight with no slope. These segments often give the tool trouble. For now, the best solution is to combine segments so that a segment encompasses more than the flat area, and therefore has some slope, and to manually add vertices and create some small amount of curvature so that it is not perfectly straight.
-
-If additional issues are encountered, please report them in the GitHub Issues page for VBET 2.0.
-
-
-## Data preparation
-Because the tool relies on drainage area, a drainage area raster is required to run the tool. To be accurate, this raster must encompass the entire watershed upstream of the area for which a valley bottom is being delineated. In cases where a small LiDAR dataset is being used to delineate valley bottoms for a portion of a drainage network, a coarser DEM of the entire basin (e.g. the 10m DEMs of the NED from USGS) should be used to generate this raster. This can be accomplished using the common GIS workflow of filling the pits in the DEM, generating flow directions, and then a flow accumulation raster. The flow accumulation raster can be converted to drainage area in a raster calculator using the equation (flow_accumulation * (flow_accumulation_raster_resolution^2))/1000000.
-
-For the best results, the drainage network should align well with the DEM used. If the network was generated using the DEM, this isn't a problem, but when using external datasets such as NHD, there can be a large spatial disparity between the location of the drainage network in the DEM and in the actual dataset. If this is the case, to the extent possible, the user should edit the network, adjusting it so that it aligns with the network location in the DEM. Additionally, the segment length of the network should be suitable given the DEM resolution. That is, with higher resolution DEMs the network should be segmented at smaller lengths than for coarser DEMs (e.g. 10m). A good length is generally 100-500 times the DEM resolution (e.g. for a 10m DEM, we use 1000m segments, and for a 1m DEM we use 200-500m segments, depending on river size). Stream networks *must contain ONLY SINGLEPART FEATURES. If it contains multipart features*, use a "multipart to singlepart" GIS tool to fix this issue. Finally, to work with the detrending algorithm, network segments *must have 5+ vertices*. Very short, or perfectly straight segments usually don't have enough and the tool will throw an exception, notifying the user of which segments need more vertices.
+Stream network segments passing through lakes or reservoirs sometimes are completely
+straight with no slope. These segments break the detrending algorithm. For now, the best
+solution is to combine segments so that a segment encompasses more than the flat area,
+and therefore has some slope, or to delete these segments from the drainage network prior
+to running the model (see Data Preparation below).
+
+If additional issues are encountered, please report them in the GitHub Issues page for VBET-2.
+
+
+## Data Preparation
+### Drainage Area
+Because the tool relies on drainage area (DA), a DA raster **OR** a field in the drainage
+network input containing a value for DA associated with each segment (for example,
+there are various NHD Plus attributes with DA values) is required to run the tool.
+To be accurate, a DA raster must encompass the entire watershed upstream of the area for which
+a valley bottom is being delineated. In cases where a small LiDAR dataset is being used to
+delineate valley bottoms for a portion of a drainage network, a coarser DEM of the entire basin
+(e.g. the 10m DEMs of the NED from USGS) should be used to generate this raster. This can be
+accomplished using the common GIS workflow of filling the pits in the DEM, generating flow
+directions, and then a flow accumulation raster. The flow accumulation raster can be converted
+to drainage area in a raster calculator using the equation `(flow_accumulation *
+(flow_accumulation_raster_resolution^2))/1000000`.
+
+### Stream Network
+For the best results, the drainage network should align well with the DEM used. If the
+network was generated using the DEM, this isn't a problem, but when using external datasets
+such as NHD, there can be a large spatial disparity between the location of the drainage
+network in the DEM and in the actual dataset. If this is the case, to the extent possible,
+the user should edit the network, adjusting it so that it aligns with the network location
+in the DEM. Additionally, the segment length of the network should be suitable given the DEM
+resolution. That is, with higher resolution DEMs the network should be segmented at smaller
+lengths than for coarser DEMs (e.g. 10m). A good length is generally 50-200 times the DEM
+resolution (e.g. for a 10m DEM, we use 500-1000m segments, and for a 1m DEM we use 50-200m
+segments, depending on river size). Stream networks *must contain ONLY SINGLEPART FEATURES. If
+it contains multipart features*, use a "multipart to singlepart" GIS tool to fix this issue.
+Finally, to work with the detrending algorithm, network segments *must have 5+ vertices*.
+Very short, or perfectly straight segments usually don't have enough and the tool will throw
+an exception, notifying the user of which segments need more vertices.
+
+A good work flow for preparing a stream network then is:
+- either download a stream network or derive one using a DEM
+- segment the network to an appropriate length
+- remove or merge segments that are within the flat areas of waterbodies
+- use a "densify" tool to add 5 vertices to each segment to ensure all have enough for detrending
+
+It's also a good idea to make a copy of the network for use with the tool as the tool will further
+clean the network.
## Running the model
-To run the model, go to the [releases](https://github.com/jtgilbert/VBET-2/releases), and download the release appropriate for your OS (Windows 64 bit and Linux 64 bit available). Unzip the download folder in a chosen location. For windows, open the folder and double click on the VBET.exe to run the program. For Linux, open a command prompt inside the folder and type the command: ./VBET to run the program.
-In addition to a shapefile of the valley bottom, the tool produces a text file with the same name as the output with "metadata" appended on the end that contains the files and parameter values used for that particular run of the tool.
+### GUI
+To run the model, go to the [releases](https://github.com/jtgilbert/VBET-2/releases) tab and
+download the release appropriate for your OS (Windows 64 bit and Linux 64 bit available). Unzip
+the download folder in a chosen location. Open the folder and then double-click on the
+VBET.exe to run the program. For Linux, open the unzipped directory, open a command prompt
+inside the folder and type the command: ./VBET to run the program.
+
+In addition to a shapefile of the valley bottom, the tool produces a text file with the same
+name as the output with "metadata" appended on the end that contains the files and parameter
+values used for that particular run of the tool. **If the tool fails for some reason, the error
+message should be recorded in the metadata txt file for diagnosis**.
-If you are using a different OS, or prefer to run the code manually in an IDE, you can download the repository and run it in an IDE. Configure a Python environment with the packages listed below, fill out the parameter values in the 'run_VBET.py' script and then run the script.
+### IDE
+If you are using a different OS, or prefer to run the code manually in an IDE, you can
+download the repository and run it in an IDE. Configure a Python environment with the packages
+listed below, fill out the parameter values in the 'run_VBET.py' script and then run the script
+(after uncommenting lines 34 & 35).
### Required Python packages
#### Python 3
@@ -35,25 +94,44 @@ If you are using a different OS, or prefer to run the code manually in an IDE, y
## Parameters
-- Stream Network: navigate to the stream network shapefile. This can be e.g. the National Hydrologic Dataset (NHD), extracted from a DEM, or manually delineated by the user. The stream network should be segmented so that each reach is approximately the desired segment length. The network should also be projected into a projected coordinate reference system.
-- Digital Elevation Model: navigate to the DEM from which you wish to derive the valley bottom polygon (.tif). The DEM should be in the same projected CRS as the stream network.
-- Scratch Work Folder: Navigate to a path to a folder where temporary files will be stored.
-- Drainage Area Raster: navigate to the drainage area raster for the basin (.tif). This should be in the same CRS as the stream network and DEM.
-- Output: enter a path and file name to store the valley bottom shapefile produced by the tool.
-- Large Drainage Area Threshold: enter a value (int) for a drainage area threshold above which the network will be considered 'large'.
-- Medium Drainage Area Threshold: enter a value (int) for a drainage area threshold above which the network will be considered 'medium', and below which the network will be considered 'small'.
-- Large Slope Threshold: enter a value for the slope threshold in the large portion of the network.
-- Medium Slope Threshold: enter a value for the slope threshold in the medium portion of the network.
-- Small Slope Threshold: enter a value for the slope threshold in the small portion of the network.
-- Large Buffer: enter a value representing the maximum distance from the large portion of the network within which valley bottoms occur.
-- Medium Buffer: enter a value representing the maximum distance from the medium portion of the network within which valley bottoms occur.
-- Small Buffer: enter a value representing the maximum distance from the small portion of the network within which valley bottoms occur.
-- Minimum Buffer: enter a value representing a minimum valley bottom width/channel width for the entire network.
-- Large Depth: enter a value for the depth threshold of the large portion of the network.
-- Medium Depth: enter a value for the depth threshold of the medium portion of the network.
-- Small Depth: enter a value for the depth threshold of the small portion of the network.
-
-Note: the depth parameters are not required; VBET can run based only on slope (the first version did), but results are generally significantly better by including depth (hence the update).
+- **Stream Network**: navigate to the stream network shapefile. This can be e.g. the National
+Hydrologic Dataset (NHD), extracted from a DEM, or manually delineated by the user. The stream
+network should be segmented so that each reach is approximately the desired segment length.
+The network should also be projected into a projected coordinate reference system.
+- **Digital Elevation Model**: navigate to the DEM from which you wish to derive the valley
+bottom polygon (.tif). The DEM should be in the same projected CRS as the stream network.
+- **Scratch Work Folder**: Navigate to a path to a folder where temporary files will be stored.
+- **Drainage Area Raster**: If the network does not already have a field with drainage area, a
+drainage area raster is required. Navigate to the drainage area raster for the basin (.tif).
+This should be in the same CRS as the stream network and DEM.
+- **Output**: enter a path and file name to store the valley bottom shapefile produced by the
+tool.
+- **Existing Drainage Area Field**: if the network has a field with drainage area values for
+each segment, a drainage area raster is not required. Instead, enter the name of the field here.
+ (If you are using a raster leave this blank.)
+- **Large Drainage Area Threshold**: enter a value (int) for a drainage area threshold above
+which the network will be considered 'large'.
+- **Medium Drainage Area Threshold**: enter a value (int) for a drainage area threshold above
+which the network will be considered 'medium', and below which the network will be considered
+'small'.
+- **Large Slope Threshold**: enter a value for the slope threshold in the large portion of
+the network.
+- **Medium Slope Threshold**: enter a value for the slope threshold in the medium portion
+of the network.
+- **Small Slope Threshold**: enter a value for the slope threshold in the small portion of
+the network.
+- **Large Buffer**: enter a value representing the maximum distance from the large portion
+of the network within which valley bottoms occur.
+- **Medium Buffer**: enter a value representing the maximum distance from the medium portion
+of the network within which valley bottoms occur.
+- **Small Buffer**: enter a value representing the maximum distance from the small portion
+of the network within which valley bottoms occur.
+- **Minimum Buffer**: enter a value representing a minimum valley bottom width/channel width
+for the entire network.
+- **Large Depth**: enter a value for the depth threshold of the large portion of the network.
+- **Medium Depth**: enter a value for the depth threshold of the medium portion of the network.
+- **Small Depth**: enter a value for the depth threshold of the small portion of the network.
+
![VBET Output image](/pics/vbet_output.png)
diff --git a/VBET.py b/VBET.py
index c54d2c3..b25dc46 100644
--- a/VBET.py
+++ b/VBET.py
@@ -19,7 +19,6 @@ def __init__(self):
self.buttonBox.accepted.connect(self.vbet)
#self.buttonBox.rejected.connect(self.reject)
-
def file_browser(self, txtControl):
filename = QFileDialog.getOpenFileName(self, 'Open the File', '', 'Shapefiles (*.shp);; Rasters (*.tif);;',
None, QFileDialog.DontUseNativeDialog)
@@ -49,6 +48,7 @@ def vbet(self):
inst.params['sm_buf'] = float(self.lineEdit_smbuf.text())
inst.params['min_buf'] = float(self.lineEdit_minbuf.text())
inst.params['dr_area'] = str(self.lineEdit_da.text())
+ inst.params['da_field'] = str(self.lineEdit_exda.text())
inst.params['lg_depth'] = self.SpinBox_lgdepth.value()
inst.params['med_depth'] = self.SpinBox_meddepth.value()
inst.params['sm_depth'] = self.SpinBox_smdepth.value()
diff --git a/classVBET.py b/classVBET.py
index 4e6bb1c..e70fc27 100644
--- a/classVBET.py
+++ b/classVBET.py
@@ -12,6 +12,8 @@
from scipy.linalg import lstsq
import json
import os.path
+from tqdm import tqdm
+from datetime import datetime
class VBET:
@@ -36,10 +38,47 @@ def __init__(self, **kwargs):
self.sm_buf = kwargs['sm_buf']
self.min_buf = kwargs['min_buf']
self.dr_area = kwargs['dr_area']
+ self.da_field = kwargs['da_field']
self.lg_depth = kwargs['lg_depth']
self.med_depth = kwargs['med_depth']
self.sm_depth = kwargs['sm_depth']
+ # create metadata text file
+ metatxt = '{out}_metadata.txt'.format(out=self.out)
+ L = ['network: {} \n'.format(self.streams),
+ 'dem: {} \n'.format(self.dem),
+ 'output: {} \n'.format(self.out),
+ 'scratch workspace: {} \n'.format(self.scratch),
+ 'large drainage area threshold: {} \n'.format(self.lg_da),
+ 'medium drainage area threshold: {} \n'.format(self.med_da),
+ 'large slope threshold: {} \n'.format(self.lg_slope),
+ 'medium slope threshold: {} \n'.format(self.med_slope),
+ 'small slope threshold: {} \n'.format(self.sm_slope),
+ 'large buffer: {} \n'.format(self.lg_buf),
+ 'medium buffer: {} \n'.format(self.med_buf),
+ 'small buffer: {} \n'.format(self.sm_buf),
+ 'minimum buffer: {} \n'.format(self.min_buf),
+ 'drainage area field: {} \n'.format(self.da_field),
+ 'large depth: {} \n'.format(self.lg_depth),
+ 'medium depth: {} \n'.format(self.med_depth),
+ 'small depth: {} \n'.format(self.sm_depth)
+ ]
+ self.md = open(metatxt, 'w+')
+ self.md.writelines(L)
+ self.md.writelines('\nStarted: {} \n'.format(datetime.now().strftime("%d/%m/%Y %H:%M:%S")))
+
+ # either use selected drainage area field, or pull drainage area from raster
+ if self.da_field is not None:
+ if self.da_field not in self.network.columns:
+ self.md.writelines('\n Exception: Drainage Area field selected for input network does not exist, make '
+ 'sure it is entered correctly \n')
+ self.md.close()
+ raise Exception('Drainage Area field selected for input network does not exist, make sure it is '
+ 'entered correctly')
+ else:
+ self.network['Drain_Area'] = self.network[self.da_field]
+
+ # set crs for output
self.crs_out = self.network.crs
# check that scratch directory exists, make if not
@@ -49,30 +88,46 @@ def __init__(self, **kwargs):
os.mkdir(self.scratch)
# check that datasets are in projected coordinate system
- if self.network.crs is None:
- raise Exception('All geospatial inputs should be have the same projected coordinate reference system')
- if rasterio.open(self.dem).crs is None:
- raise Exception('All geospatial inputs should be have the same projected coordinate reference system')
- if rasterio.open(self.dr_area).crs is None:
- raise Exception('All geospatial inputs should be have the same projected coordinate reference system')
-
- # check that all depth params have a value or are None
- if self.lg_depth is None and (self.med_depth is not None or self.sm_depth is not None):
- raise Exception('All depths (lg, med, sm) must have value or be None')
- elif self.med_depth is None and (self.lg_depth is not None or self.sm_depth is not None):
- raise Exception('All depths (lg, med, sm) must have value or be None')
- elif self.sm_depth is None and (self.lg_depth is not None or self.med_depth is not None):
- raise Exception('All depths (lg, med, sm) must have value or be None')
+ if not self.network.crs.is_projected:
+ self.md.writelines('\n Exception: All geospatial inputs should have the same projected coordinate '
+ 'reference system \n')
+ self.md.close()
+ raise Exception('All geospatial inputs should have the same projected coordinate reference system')
+ if not rasterio.open(self.dem).crs.is_projected:
+ self.md.writelines('\n Exception: All geospatial inputs should have the same projected coordinate '
+ 'reference system \n')
+ self.md.close()
+ raise Exception('All geospatial inputs should have the same projected coordinate reference system')
+ if self.network.crs.to_string() != rasterio.open(self.dem).crs.to_string():
+ self.md.writelines('\n Exception: All geospatial inputs should have the same projected coordinate '
+ 'reference system \n')
+ self.md.close()
+ raise Exception('All geospatial inputs should have the same projected coordinate reference system')
+ if self.dr_area:
+ if not rasterio.open(self.dr_area).crs.is_projected:
+ self.md.writelines('\n Exception: All geospatial inputs should have the same projected coordinate '
+ 'reference system \n')
+ self.md.close()
+ raise Exception('All geospatial inputs should have the same projected coordinate reference system')
+ if self.network.crs.to_string() != rasterio.open(self.dr_area).crs.to_string():
+ self.md.writelines('\n Exception: All geospatial inputs should have the same projected coordinate '
+ 'reference system \n')
+ self.md.close()
+ raise Exception('All geospatial inputs should have the same projected coordinate reference system')
# check that there are no segments with less than 5 vertices
few_verts = []
- for i in range(len(self.network)):
+ for i in self.network.index:
if len(self.network.loc[i].geometry.xy[0]) <= 5:
few_verts.append(i)
if len(few_verts) > 0:
+ self.md.writelines('\n Exception: There are network segments with fewer than 5 vertices. Add vertices in '
+ 'GIS \n')
+ self.md.close()
raise Exception("Network segments with IDs ", few_verts, "don't have enough vertices for DEM detrending. "
"Add vertices in GIS")
+ # add container for individual valley bottom features and add the minimum buffer into it
self.polygons = []
network_geom = self.network['geometry']
@@ -81,6 +136,38 @@ def __init__(self, **kwargs):
for x in range(len(min_buf)):
self.polygons.append(min_buf[x])
+ # save total network length for use in later parameter
+ self.seglengths = 0
+ for x in self.network.index:
+ self.seglengths += self.network.loc[x].geometry.length
+
+ def clean_network(self):
+
+ print('Cleaning up drainage network for VBET input')
+ print('starting with {} network segments'.format(len(self.network)))
+ # minimum length - remove short segments
+ with rasterio.open(self.dem, 'r') as src:
+ xres = src.res[0]
+ self.network = self.network[self.network.geometry.length > 5*xres]
+
+ # get rid of perfectly straight segments
+ sin = []
+ for i in self.network.index:
+ seg_geom = self.network.loc[i, 'geometry']
+ x_coord1 = seg_geom.boundary[0].xy[0][0]
+ y_coord1 = seg_geom.boundary[0].xy[1][0]
+ x_coord2 = seg_geom.boundary[1].xy[0][0]
+ y_coord2 = seg_geom.boundary[1].xy[1][0]
+
+ dist = ((x_coord1 - x_coord2) ** 2 + (y_coord1 - y_coord2) ** 2) ** 0.5
+ sin_val = seg_geom.length / dist
+ sin.append(sin_val)
+ self.network['sinuos'] = sin
+
+ self.network = self.network[self.network['sinuos'] >= 1.00001]
+
+ print('cleaned to {} network segments'.format(len(self.network)))
+
def add_da(self):
"""
Adds a drainage area attribute to each segment of the drainage network
@@ -224,6 +311,8 @@ def raster_overlap(self, array1, array2, ndval):
input arrays have NoData
"""
if array1.shape != array2.shape:
+ self.md.writelines('\n Exception: slope sub raster and depth sub raster are not the same size \n')
+ self.md.close()
raise Exception('rasters are not same size')
out_array = np.full(array1.shape, ndval)
@@ -343,12 +432,15 @@ def valley_bottom(self):
:return: saves a valley bottom shapefile
"""
- for i in self.network.index:
+ self.clean_network()
+
+ print('Generating valley bottom for each network segment')
+ for i in tqdm(self.network.index):
seg = self.network.loc[i]
da = seg['Drain_Area']
geom = seg['geometry']
- print('segment ', i+1, ' of ', len(self.network.index))
+ # print('segment ', i+1, ' of ', len(self.network.index))
ept1 = (geom.boundary[0].xy[0][0], geom.boundary[0].xy[1][0])
ept2 = (geom.boundary[1].xy[0][0], geom.boundary[1].xy[1][0])
@@ -390,10 +482,7 @@ def valley_bottom(self):
slope_sub = self.reclassify(slope, ndval, self.sm_slope)
# set thresholds for hole filling
- seglengths = 0
- for x in self.network.index:
- seglengths += self.network.loc[x].geometry.length
- avlen = int(seglengths / len(self.network))
+ avlen = int(self.seglengths / len(self.network))
if da < self.med_da:
thresh = avlen * self.sm_buf * 0.005
elif self.med_da <= da < self.lg_da:
@@ -401,33 +490,31 @@ def valley_bottom(self):
else: # da >= self.lg_da:
thresh = avlen * self.lg_buf * 0.005
- # only detrend if depth is being used
- if self.med_depth is not None:
- detr = self.detrend(dem, geom) # might want to change this offset
+ # detrend segment dem
+ detr = self.detrend(dem, geom) # might want to change this offset
- if da >= self.lg_da:
- depth = self.reclassify(detr, ndval, self.lg_depth)
- elif self.lg_da > da >= self.med_da:
- depth = self.reclassify(detr, ndval, self.med_depth)
- else:
- depth = self.reclassify(detr, ndval, self.sm_depth)
+ if da >= self.lg_da:
+ depth = self.reclassify(detr, ndval, self.lg_depth)
+ elif self.lg_da > da >= self.med_da:
+ depth = self.reclassify(detr, ndval, self.med_depth)
+ else:
+ depth = self.reclassify(detr, ndval, self.sm_depth)
- overlap = self.raster_overlap(slope_sub, depth, ndval)
+ overlap = self.raster_overlap(slope_sub, depth, ndval)
+ if 1 in overlap:
filled = self.fill_raster_holes(overlap, thresh, ndval)
a = self.raster_to_shp(filled, dem)
self.network.loc[i, 'fp_area'] = a
else:
- filled = self.fill_raster_holes(slope_sub, thresh, ndval)
- a = self.raster_to_shp(filled, dem)
- self.network.loc[i, 'fp_area'] = a
+ self.network.loc[i, 'fp_area'] = 0
demsrc.close()
self.network.to_file(self.streams)
# merge all polygons in folder and dissolve
- print("Creating valley bottom")
- vb = gpd.GeoSeries(cascaded_union(self.polygons)) #
+ print("Merging valley bottom segments")
+ vb = gpd.GeoSeries(unary_union(self.polygons)) #
vb.crs = self.crs_out
vb.to_file(self.scratch + "/tempvb.shp")
@@ -438,19 +525,25 @@ def valley_bottom(self):
vbc.to_file(self.scratch + "/tempvb.shp")
# get rid of small unattached polygons
+ self.network.to_file(self.scratch + "/dissnetwork.shp")
+ network2 = gpd.read_file(self.scratch + "/dissnetwork.shp")
+ network2['dissolve'] = 1
+ network2 = network2.dissolve('dissolve')
vb1 = gpd.read_file(self.scratch + "/tempvb.shp")
vbm2s = vb1.explode()
- sub = vbm2s['geometry'].area >= 0.001*vbm2s['geometry'].area.max()
+ sub = []
+ for i in vbm2s.index:
+ segs = 0
+ for j in network2.index:
+ if network2.loc[j].geometry.intersects(vbm2s.loc[i].geometry):
+ segs += 1
+ if segs > 0:
+ sub.append(True)
+ else:
+ sub.append(False)
+
vbcut = vbm2s[sub].reset_index(drop=True)
vbcut.to_file(self.scratch + "/tempvb.shp")
- # do it a second time?
- #vb2 = gpd.read_file(self.scratch + "/tempvb.shp")
- #vbm2s2 = vb2.explode()
- #sub2 = vbm2s2['geometry'].area >= 0.05*vbm2s2['geometry'].area.max()
- #vbcut2 = vbm2s2[sub2].reset_index(drop=True)
- #vbcut2.to_file(self.scratch + "/tempvb.shp")
-
- # TRY THIS - have to dissolve before chaikins
polys = []
for i in vbcut.index:
@@ -464,8 +557,19 @@ def valley_bottom(self):
p = polys[0]
vbf = gpd.GeoDataFrame(index=[0], crs=self.crs_out, geometry=[p])
+ vbf = vbf.explode()
+ areas = []
+ for i in vbf.index:
+ areas.append(vbf.loc[i].geometry.area/1000000.)
+ vbf['Area_km2'] = areas
vbf.to_file(self.out)
+ # close metadata text tile
+ self.md.writelines('\nFinished: {} \n'.format(datetime.now().strftime("%d/%m/%Y %H:%M:%S")))
+ self.md.close()
+
+ # clean up scratch workspace?
+
return
diff --git a/run_VBET.py b/run_VBET.py
index 300512e..969307a 100644
--- a/run_VBET.py
+++ b/run_VBET.py
@@ -1,53 +1,35 @@
import classVBET
-from datetime import datetime
class RunVBET:
def __init__(self):
self.params = {
- 'network': '/path/to/stream/network.shp',
- 'dem': '/path/to/dem.tif',
- 'out': '/path/to/save/output.shp',
- 'scratch': '/path/of/scratch/workspace',
+ 'network': '/home/jordan/Documents/Riverscapes/scratch/10190004_network.shp',
+ 'dem': '/home/jordan/Documents/Riverscapes/scratch/dem_10190004.tif',
+ 'out': '/home/jordan/Documents/Riverscapes/scratch/vbet2_out2.shp',
+ 'scratch': '/home/jordan/Documents/Riverscapes/scratch/scratch',
'lg_da': 300,
'med_da': 30,
- 'lg_slope': 2,
- 'med_slope': 3,
- 'sm_slope': 4,
+ 'lg_slope': 3,
+ 'med_slope': 4,
+ 'sm_slope': 5,
'lg_buf': 500,
'med_buf': 200,
'sm_buf': 80,
'min_buf': 10,
- 'dr_area': '/path/to/drainage/area/raster.tif',
- 'lg_depth': None,
- 'med_depth': None,
- 'sm_depth': None
+ 'dr_area': None,
+ 'da_field': 'TotDASqKm',
+ 'lg_depth': 3,
+ 'med_depth': 2,
+ 'sm_depth': 1.5
}
def run(self):
- print('started: ', datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
vb = classVBET.VBET(**self.params)
- vb.add_da()
+ if self.params['da_field'] is None:
+ vb.add_da()
vb.valley_bottom()
- print('ended: ', datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
- metatxt = '{out}_metadata.txt'.format(out=self.params['out'])
- L = ['network: {} \n'.format(self.params['network']),
- 'dem: {} \n'.format(self.params['dem']),
- 'output: {} \n'.format(self.params['out']),
- 'scratch workspace: {} \n'.format(self.params['scratch']),
- 'large drainage area threshold: {} \n'.format(self.params['lg_da']),
- 'medium drainage area threshold: {} \n'.format(self.params['med_da']),
- 'large slope threshold: {} \n'.format(self.params['lg_slope']),
- 'medium slope threshold: {} \n'.format(self.params['med_slope']),
- 'small slope threshold: {} \n'.format(self.params['sm_slope']),
- 'large buffer: {} \n'.format(self.params['lg_buf']),
- 'medium buffer: {} \n'.format(self.params['med_buf']),
- 'small buffer: {} \n'.format(self.params['sm_buf']),
- 'minimum buffer: {} \n'.format(self.params['min_buf']),
- 'large depth: {} \n'.format(self.params['lg_depth']),
- 'medium depth: {} \n'.format(self.params['med_depth']),
- 'small depth: {} \n'.format(self.params['sm_depth'])
- ]
- md = open(metatxt, 'w+')
- md.writelines(L)
- md.close()
+
+
+# vbrun = RunVBET()
+# vbrun.run()
diff --git a/vbet.ui b/vbet.ui
index 9dbce26..d3dba23 100644
--- a/vbet.ui
+++ b/vbet.ui
@@ -7,7 +7,7 @@
0
0
857
- 770
+ 848
@@ -432,6 +432,26 @@
-
+
-
+
+
+ Existing Drainage Area Field
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
-
@@ -676,6 +696,22 @@
-
+
-
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
-
diff --git a/vbet_ui.py b/vbet_ui.py
index 201eb20..b26e553 100644
--- a/vbet_ui.py
+++ b/vbet_ui.py
@@ -13,7 +13,7 @@
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
- Dialog.resize(857, 770)
+ Dialog.resize(857, 848)
self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
self.gridLayout_2.setObjectName("gridLayout_2")
self.verticalLayout_4 = QtWidgets.QVBoxLayout()
@@ -143,144 +143,154 @@ def setupUi(self, Dialog):
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.label_exda = QtWidgets.QLabel(Dialog)
+ self.label_exda.setObjectName("label_exda")
+ self.verticalLayout_3.addWidget(self.label_exda)
+ spacerItem21 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem21)
self.label_lgda = QtWidgets.QLabel(Dialog)
self.label_lgda.setObjectName("label_lgda")
self.verticalLayout_3.addWidget(self.label_lgda)
- spacerItem21 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem21)
+ spacerItem22 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem22)
self.label_medda = QtWidgets.QLabel(Dialog)
self.label_medda.setObjectName("label_medda")
self.verticalLayout_3.addWidget(self.label_medda)
- spacerItem22 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem22)
+ spacerItem23 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem23)
self.label_lgslope = QtWidgets.QLabel(Dialog)
self.label_lgslope.setObjectName("label_lgslope")
self.verticalLayout_3.addWidget(self.label_lgslope)
- spacerItem23 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem23)
+ spacerItem24 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem24)
self.label_medslope = QtWidgets.QLabel(Dialog)
self.label_medslope.setObjectName("label_medslope")
self.verticalLayout_3.addWidget(self.label_medslope)
- spacerItem24 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem24)
+ spacerItem25 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem25)
self.label_smslope = QtWidgets.QLabel(Dialog)
self.label_smslope.setObjectName("label_smslope")
self.verticalLayout_3.addWidget(self.label_smslope)
- spacerItem25 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem25)
+ spacerItem26 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem26)
self.label_lgbuf = QtWidgets.QLabel(Dialog)
self.label_lgbuf.setObjectName("label_lgbuf")
self.verticalLayout_3.addWidget(self.label_lgbuf)
- spacerItem26 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem26)
+ spacerItem27 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem27)
self.label_medbuf = QtWidgets.QLabel(Dialog)
self.label_medbuf.setObjectName("label_medbuf")
self.verticalLayout_3.addWidget(self.label_medbuf)
- spacerItem27 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem27)
+ spacerItem28 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem28)
self.label_smbuf = QtWidgets.QLabel(Dialog)
self.label_smbuf.setObjectName("label_smbuf")
self.verticalLayout_3.addWidget(self.label_smbuf)
- spacerItem28 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem28)
+ spacerItem29 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem29)
self.label_minbuf = QtWidgets.QLabel(Dialog)
self.label_minbuf.setObjectName("label_minbuf")
self.verticalLayout_3.addWidget(self.label_minbuf)
- spacerItem29 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem29)
+ spacerItem30 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem30)
self.label_lgdepth = QtWidgets.QLabel(Dialog)
self.label_lgdepth.setObjectName("label_lgdepth")
self.verticalLayout_3.addWidget(self.label_lgdepth)
- spacerItem30 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem30)
+ spacerItem31 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem31)
self.label_meddepth = QtWidgets.QLabel(Dialog)
self.label_meddepth.setObjectName("label_meddepth")
self.verticalLayout_3.addWidget(self.label_meddepth)
- spacerItem31 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem31)
+ spacerItem32 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem32)
self.label_smdepth = QtWidgets.QLabel(Dialog)
self.label_smdepth.setObjectName("label_smdepth")
self.verticalLayout_3.addWidget(self.label_smdepth)
- spacerItem32 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout_3.addItem(spacerItem32)
+ spacerItem33 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem33)
self.horizontalLayout_2.addLayout(self.verticalLayout_3)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
+ self.lineEdit_exda = QtWidgets.QLineEdit(Dialog)
+ self.lineEdit_exda.setObjectName("lineEdit_exda")
+ self.verticalLayout.addWidget(self.lineEdit_exda)
+ spacerItem34 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem34)
self.lineEdit_lgda = QtWidgets.QLineEdit(Dialog)
self.lineEdit_lgda.setClearButtonEnabled(False)
self.lineEdit_lgda.setObjectName("lineEdit_lgda")
self.lineEdit_lgda.setValidator(QtGui.QDoubleValidator())
self.lineEdit_lgda.setText(str(250))
self.verticalLayout.addWidget(self.lineEdit_lgda)
- spacerItem33 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem33)
+ spacerItem35 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem35)
self.lineEdit_medda = QtWidgets.QLineEdit(Dialog)
self.lineEdit_medda.setObjectName("lineEdit_medda")
self.lineEdit_medda.setValidator(QtGui.QDoubleValidator())
self.lineEdit_medda.setText(str(25))
self.verticalLayout.addWidget(self.lineEdit_medda)
- spacerItem34 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem34)
+ spacerItem36 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem36)
self.SpinBox_lgslope = QtWidgets.QDoubleSpinBox(Dialog)
self.SpinBox_lgslope.setProperty("value", 3.0)
self.SpinBox_lgslope.setObjectName("SpinBox_lgslope")
self.verticalLayout.addWidget(self.SpinBox_lgslope)
- spacerItem35 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem35)
+ spacerItem37 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem37)
self.SpinBox_medslope = QtWidgets.QDoubleSpinBox(Dialog)
self.SpinBox_medslope.setProperty("value", 4.0)
self.SpinBox_medslope.setObjectName("SpinBox_medslope")
self.verticalLayout.addWidget(self.SpinBox_medslope)
- spacerItem36 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem36)
+ spacerItem38 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem38)
self.SpinBox_smslope = QtWidgets.QDoubleSpinBox(Dialog)
self.SpinBox_smslope.setProperty("value", 5.0)
self.SpinBox_smslope.setObjectName("SpinBox_smslope")
self.verticalLayout.addWidget(self.SpinBox_smslope)
- spacerItem37 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem37)
+ spacerItem39 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem39)
self.lineEdit_lgbuf = QtWidgets.QLineEdit(Dialog)
self.lineEdit_lgbuf.setObjectName("lineEdit_lgbuf")
self.lineEdit_lgbuf.setValidator(QtGui.QDoubleValidator())
self.verticalLayout.addWidget(self.lineEdit_lgbuf)
- spacerItem38 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem38)
+ spacerItem40 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem40)
self.lineEdit_medbuf = QtWidgets.QLineEdit(Dialog)
self.lineEdit_medbuf.setObjectName("lineEdit_medbuf")
self.lineEdit_medbuf.setValidator(QtGui.QDoubleValidator())
self.verticalLayout.addWidget(self.lineEdit_medbuf)
- spacerItem39 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem39)
+ spacerItem41 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem41)
self.lineEdit_smbuf = QtWidgets.QLineEdit(Dialog)
self.lineEdit_smbuf.setObjectName("lineEdit_smbuf")
self.lineEdit_smbuf.setValidator(QtGui.QDoubleValidator())
self.verticalLayout.addWidget(self.lineEdit_smbuf)
- spacerItem40 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem40)
+ spacerItem42 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem42)
self.lineEdit_minbuf = QtWidgets.QLineEdit(Dialog)
self.lineEdit_minbuf.setObjectName("lineEdit_minbuf")
self.lineEdit_minbuf.setValidator(QtGui.QDoubleValidator())
self.verticalLayout.addWidget(self.lineEdit_minbuf)
- spacerItem41 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem41)
+ spacerItem43 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem43)
self.SpinBox_lgdepth = QtWidgets.QDoubleSpinBox(Dialog)
self.SpinBox_lgdepth.setProperty("value", 4.0)
self.SpinBox_lgdepth.setObjectName("SpinBox_lgdepth")
self.verticalLayout.addWidget(self.SpinBox_lgdepth)
- spacerItem42 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem42)
+ spacerItem44 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem44)
self.SpinBox_meddepth = QtWidgets.QDoubleSpinBox(Dialog)
self.SpinBox_meddepth.setProperty("value", 2.0)
self.SpinBox_meddepth.setObjectName("SpinBox_meddepth")
self.verticalLayout.addWidget(self.SpinBox_meddepth)
- spacerItem43 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem43)
+ spacerItem45 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem45)
self.SpinBox_smdepth = QtWidgets.QDoubleSpinBox(Dialog)
self.SpinBox_smdepth.setProperty("value", 1.5)
self.SpinBox_smdepth.setObjectName("SpinBox_smdepth")
self.verticalLayout.addWidget(self.SpinBox_smdepth)
- spacerItem44 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem44)
+ spacerItem46 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem46)
self.horizontalLayout_2.addLayout(self.verticalLayout)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
@@ -321,6 +331,7 @@ def retranslateUi(self, Dialog):
self.pushButton_da.setText(_translate("Dialog", ". . ."))
self.pushButton_output.setText(_translate("Dialog", ". . ."))
self.label_2.setText(_translate("Dialog", "Enter parameter values:"))
+ self.label_exda.setText(_translate("Dialog", "Existing Drainage Area Field"))
self.label_lgda.setText(_translate("Dialog", "Large Drainage Area Threshold"))
self.label_medda.setText(_translate("Dialog", "Medium Drainage Area Threshold"))
self.label_lgslope.setText(_translate("Dialog", "Large Slope Threshold"))