Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
1bede77
Driveby: rename
Jun 15, 2026
d4b0f98
polygon centerline boilerplate
Jun 15, 2026
5fe2ac2
boilerplate
Jun 15, 2026
fed860e
Merge branch 'main' into centerline
Jun 16, 2026
572f7ba
update comment
Jun 22, 2026
27ffb0e
driveby: update comments
Jun 22, 2026
9a58a2b
mvp
Jun 22, 2026
202cde1
feature class to numpy array and delaunay triangulation
Jun 23, 2026
6dbbb95
Merge branch 'main' into centerline
Jun 24, 2026
d234efd
s
Jun 24, 2026
2ba1e87
Merge branch 'main' into centerline
Jun 24, 2026
e604863
rename scripts/ to src/
Jun 24, 2026
8eb870f
rename
Jun 24, 2026
a2bd3ac
Merge branch 'main' into centerline
bcmertz Jun 24, 2026
d62823f
Merge branch 'main' into centerline
Jun 29, 2026
350079e
bug: drive-by handle None unit
Jul 6, 2026
1e277ae
bug: separate strings
Jul 6, 2026
a64132c
bug: drive-by add warnings
Jul 6, 2026
4340941
no bare exception and use warn where appropriate
Jul 7, 2026
da51bb4
working versions of delaunay triangulation, bounding box, and voronoi
Jul 8, 2026
4dbc168
wip - use helper geometry functions
Jul 8, 2026
2a879d3
ensure statistics will be correct
Jul 13, 2026
1865c23
allow for multiple workspaces to be submitted
Jul 13, 2026
5073eef
PIXEL_TYPE enum instead of dictionary
Jul 13, 2026
f1aac11
update metadata
Jul 14, 2026
05c5a2b
use convert_area
Jul 14, 2026
b9680c8
describe types
Jul 14, 2026
a1bf51f
use convert_length
Jul 14, 2026
9dbc72d
drive-by: no longer need to define output coordinate system, this is …
Jul 14, 2026
8ca0a41
Merge branch 'main' into centerline
Jul 16, 2026
4637b00
create unit classes
Jul 16, 2026
8d044ea
use unit classes
Jul 16, 2026
3a30092
update __init__ with unit classes
Jul 16, 2026
ccd73d5
Create separate map of enum to display text and methods. Use enum for…
Jul 16, 2026
a01f796
Merge branch 'centerline' of github.com:bcmertz/SWCD-Tools into cente…
Jul 16, 2026
fe4a8e6
update metadata
Jul 16, 2026
31045a9
use class methods for units
Jul 16, 2026
67b026b
fix: check for empty list properly
Jul 16, 2026
8aa34a1
use unit classes
Jul 16, 2026
c218a34
warn on empty output
Jul 16, 2026
9441317
use LinearUnit and remove Z_UNIT parameter
Jul 16, 2026
b357833
use units classes
Jul 16, 2026
ab6eda3
use units class
Jul 16, 2026
dda0fc0
use units classes
Jul 16, 2026
d1b9a17
remove unused imports
Jul 16, 2026
2131d97
doc
Jul 17, 2026
d204843
rm unused
Jul 17, 2026
141c76e
reconfigure package structure
Jul 17, 2026
576fb7a
rm unused
Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SWCD Tools.PolygonCenterline.pyt.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata xml:lang="en"><Esri><CreaDate>20260622</CreaDate><CreaTime>17115200</CreaTime><ArcGISFormat>1.0</ArcGISFormat><SyncOnce>TRUE</SyncOnce></Esri></metadata>
85 changes: 43 additions & 42 deletions SWCD Tools.pyt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

from src import *
from src import AgAssessment, BufferTools, FluvialGeomorphology, Hydrology, TerrainAnalysis, TerrainModification, TileDrainage, Utilities

class Toolbox(object):
def __init__(self):
Expand All @@ -10,47 +10,48 @@ class Toolbox(object):
self.alias = "SWCD Tools"

tools = [
SlopePolygon,
ContourPolygon,
BurnCulverts,
CollectRasters,
ExportLayouts,
StreamNetwork,
StreamElevation,
GenerateCrossSections,
LocalMinimums,
RelativeElevationModel,
TopographicPositionIndex,
PointPlots,
ShrubClusters,
BufferPotential,
StreamPowerIndex,
LeastAction,
RunoffCurveNumber,
CalculateEFH2,
WatershedSize,
StreambankDetection,
LandscapePosition,
VBET,
WatershedDelineation,
SubBasinDelineation,
RemoveUnused,
DecisionTree,
ImageDifferencingSetup,
ImageDifferencing,
ImageDifferencingClouds,
TopographicWetness,
PotentialWetlands,
BermAnalysis,
DamRemoval,
DefineParcels,
Agland,
NonAg,
GeocodeAddress,
Forest,
Process,
Export,
Restart
AgAssessment.DefineParcels,
AgAssessment.Agland,
AgAssessment.NonAg,
AgAssessment.Forest,
AgAssessment.Process,
AgAssessment.Export,
AgAssessment.Restart,
BufferTools.PointPlots,
BufferTools.ShrubClusters,
BufferTools.BufferPotential,
FluvialGeomorphology.StreamNetwork,
FluvialGeomorphology.LeastAction,
FluvialGeomorphology.StreambankDetection,
FluvialGeomorphology.StreamElevation,
FluvialGeomorphology.GenerateCrossSections,
FluvialGeomorphology.PolygonCenterline,
Hydrology.CalculateEFH2,
Hydrology.RunoffCurveNumber,
Hydrology.SubBasinDelineation,
Hydrology.WatershedDelineation,
Hydrology.WatershedSize,
TerrainModification.BermAnalysis,
TerrainModification.DamRemoval,
TerrainModification.BurnCulverts,
TerrainAnalysis.StreamPowerIndex,
TerrainAnalysis.LandscapePosition,
TerrainAnalysis.VBET,
TerrainAnalysis.TopographicWetness,
TerrainAnalysis.RelativeElevationModel,
TerrainAnalysis.PotentialWetlands,
TerrainAnalysis.TopographicPositionIndex,
TileDrainage.DecisionTree,
TileDrainage.ImageDifferencingSetup,
TileDrainage.ImageDifferencing,
TileDrainage.ImageDifferencingClouds,
Utilities.SlopePolygon,
Utilities.ContourPolygon,
Utilities.ExportLayouts,
Utilities.LocalMinimums,
Utilities.GeocodeAddress,
Utilities.RemoveUnused,
Utilities.CollectRasters,
]

# List of tool classes associated with this toolbox
Expand Down
2 changes: 1 addition & 1 deletion SWCD Tools.pyt.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<?xml version="1.0"?>
<metadata xml:lang="en"><Esri><CreaDate>20251119</CreaDate><CreaTime>09340100</CreaTime><ArcGISFormat>1.0</ArcGISFormat><SyncOnce>TRUE</SyncOnce><ModDate>20260625</ModDate><ModTime>181028</ModTime><scaleRange><minScale>150000000</minScale><maxScale>5000</maxScale></scaleRange><ArcGISProfile>ItemDescription</ArcGISProfile></Esri><toolbox name="SWCD Tools" alias="SWCD Tools"><arcToolboxHelpPath>c:\program files\arcgis\pro\Resources\Help\gp</arcToolboxHelpPath><toolsets/></toolbox><dataIdInfo><idCitation><resTitle>SWCD Tools</resTitle></idCitation><idPurp>A collection of ArcGIS tools related to conservation and natural resources</idPurp><searchKeys><keyword>Soil and Water Toolbox GIS ArcGIS</keyword></searchKeys></dataIdInfo><distInfo><distributor><distorFormat><formatName>ArcToolbox Toolbox</formatName></distorFormat></distributor></distInfo><mdHrLv><ScopeCd value="005"></ScopeCd></mdHrLv><mdDateSt Sync="TRUE">20251221</mdDateSt></metadata>
<metadata xml:lang="en"><Esri><CreaDate>20251119</CreaDate><CreaTime>09340100</CreaTime><ArcGISFormat>1.0</ArcGISFormat><SyncOnce>TRUE</SyncOnce><ModDate>20260716</ModDate><ModTime>133353</ModTime><scaleRange><minScale>150000000</minScale><maxScale>5000</maxScale></scaleRange><ArcGISProfile>ItemDescription</ArcGISProfile></Esri><toolbox name="SWCD Tools" alias="SWCD Tools"><arcToolboxHelpPath>c:\program files\arcgis\pro\Resources\Help\gp</arcToolboxHelpPath><toolsets/></toolbox><dataIdInfo><idCitation><resTitle>SWCD Tools</resTitle></idCitation><idPurp>A collection of ArcGIS tools related to conservation and natural resources</idPurp><searchKeys><keyword>Soil and Water Toolbox GIS ArcGIS</keyword></searchKeys></dataIdInfo><distInfo><distributor><distorFormat><formatName>ArcToolbox Toolbox</formatName></distorFormat></distributor></distInfo><mdHrLv><ScopeCd value="005"></ScopeCd></mdHrLv><mdDateSt Sync="TRUE">20251221</mdDateSt></metadata>
10 changes: 5 additions & 5 deletions src/AgAssessment/Agland.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import arcpy

from .DefineParcels import AG_ASSESSMENT_GDB_NAME
from ..helpers import license, reload_module, log, error
from ..helpers import license, reload_module, log, warn, error
from ..helpers import setup_environment as setup
from ..helpers import validate_spatial_reference as validate

Expand Down Expand Up @@ -64,16 +64,16 @@ def execute(self, parameters, messages):
m = None
try:
m = project.listMaps(parcel)[0]
except:
log("unable to find map for {}, results may be incomplete".format(parcel))
except Exception:
warn("unable to find map for {}, results may be incomplete".format(parcel))
continue

# get parcel layer or drop off of map
parcel_lyr = None
try:
parcel_lyr = m.listLayers("*_{}".format(parcel))[0]
except:
log("no appropriate parcel layer found for {}, results may be incomplete".format(parcel))
except Exception:
warn("no appropriate parcel layer found for {}, results may be incomplete".format(parcel))
continue

# check how many pieces are selected
Expand Down
2 changes: 1 addition & 1 deletion src/AgAssessment/DefineParcels.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def execute(self, parameters, messages):
# turn off parcel layer
try:
parcel_layer.visible = False
except:
except Exception:
# parcel_layer is a shapefile
pass

Expand Down
10 changes: 5 additions & 5 deletions src/AgAssessment/Forest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import arcpy

from .DefineParcels import AG_ASSESSMENT_GDB_NAME
from ..helpers import license, reload_module, log, error
from ..helpers import license, reload_module, log, warn, error
from ..helpers import setup_environment as setup
from ..helpers import validate_spatial_reference as validate

Expand Down Expand Up @@ -64,16 +64,16 @@ def execute(self, parameters, messages):
m = None
try:
m = project.listMaps(parcel)[0]
except:
log("unable to find map for {}, results may be incomplete".format(parcel))
except Exception:
warn("unable to find map for {}, results may be incomplete".format(parcel))
continue

# get parcel layer or drop off of map
parcel_lyr = None
try:
parcel_lyr = m.listLayers("*_{}".format(parcel))[0]
except:
log("no appropriate parcel layer found for {}, results may be incomplete".format(parcel))
except Exception:
warn("no appropriate parcel layer found for {}, results may be incomplete".format(parcel))
continue

# check how many pieces are selected
Expand Down
10 changes: 5 additions & 5 deletions src/AgAssessment/NonAg.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import arcpy

from .DefineParcels import AG_ASSESSMENT_GDB_NAME
from ..helpers import license, reload_module, log, error
from ..helpers import license, reload_module, log, warn, error
from ..helpers import setup_environment as setup
from ..helpers import validate_spatial_reference as validate

Expand Down Expand Up @@ -64,16 +64,16 @@ def execute(self, parameters, messages):
m = None
try:
m = project.listMaps(parcel)[0]
except:
log("unable to find map for {}, results may be incomplete".format(parcel))
except Exception:
warn("unable to find map for {}, results may be incomplete".format(parcel))
continue

# get parcel layer or drop off of map
parcel_lyr = None
try:
parcel_lyr = m.listLayers("*_{}".format(parcel))[0]
except:
log("no appropriate parcel layer found for {}, results may be incomplete".format(parcel))
except Exception:
warn("no appropriate parcel layer found for {}, results may be incomplete".format(parcel))
continue

# check how many pieces are selected
Expand Down
4 changes: 2 additions & 2 deletions src/AgAssessment/Process.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def execute(self, parameters, messages):
m = None
try:
m = project.listMaps(parcel)[0]
except:
except Exception:
warn("unable to find map for {}, results may be incomplete".format(parcel))
continue

Expand All @@ -151,7 +151,7 @@ def execute(self, parameters, messages):
try:
lyt = project.listLayouts(parcel)[0]
layouts.append(lyt)
except:
except Exception:
warn("couldn't find layout for parcel {}, results may be incomplete".format(parcel))
continue

Expand Down
14 changes: 7 additions & 7 deletions src/AgAssessment/Restart.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import arcpy

from .DefineParcels import AG_ASSESSMENT_GDB_NAME
from ..helpers import license, reload_module, log, empty_workspace
from ..helpers import license, reload_module, log, warn, empty_workspace
from ..helpers import setup_environment as setup

class Restart(object):
Expand Down Expand Up @@ -81,8 +81,8 @@ def execute(self, parameters, messages):
with open(cache_file_path) as file:
cache = json.load(file)
parcels = cache["parcels"]
except:
log("Unable to find cache file and complete transaction. Please manually refresh the tool or manually clear out data.")
except Exception:
warn("Unable to find cache file and complete transaction. Please manually refresh the tool or manually clear out data.")
return

# clear out maps, layouts, and feature classes
Expand All @@ -92,8 +92,8 @@ def execute(self, parameters, messages):
lyt = None
try:
lyt = project.listLayouts(parcel)[0]
except:
log("couldn't find layout for parcel {}, results may be incomplete".format(parcel))
except Exception:
warn("couldn't find layout for parcel {}, results may be incomplete".format(parcel))
continue

# delete map
Expand All @@ -103,8 +103,8 @@ def execute(self, parameters, messages):
m = None
try:
m = project.listMaps(parcel)[0]
except:
log("unable to find map for parcel {}, results may be incomplete".format(parcel))
except Exception:
warn("unable to find map for parcel {}, results may be incomplete".format(parcel))
continue

# delete map
Expand Down
17 changes: 8 additions & 9 deletions src/BufferTools/BufferPotential.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# --------------------------------------------------------------------------------
import arcpy

from ..helpers import license, empty_workspace, reload_module, log, raster_and_layer
from ..helpers import license, empty_workspace, reload_module, log, warn, raster_and_layer, ArealUnit, LinearUnit
from ..helpers import setup_environment as setup
from ..helpers import validate_spatial_reference as validate

Expand Down Expand Up @@ -162,9 +162,8 @@ def execute(self, parameters, messages):

log("reading in parameters")
stream = parameters[0].value
min_width = parameters[1].valueAsText
min_acres, min_acres_unit = parameters[2].valueAsText.split(" ")
min_acres = float(min_acres) * arcpy.ArealUnitConversionFactor(min_acres_unit, "AcresUS")
min_width = LinearUnit(parameters[1].valueAsText)
min_area = ArealUnit(parameters[2].valueAsText)
extent = parameters[3].value
output_file = parameters[4].valueAsText
land_use_raster, _ = raster_and_layer(parameters[5].value)
Expand All @@ -188,7 +187,7 @@ def execute(self, parameters, messages):

# pairwise buffer stream
log("creating buffer polygon around stream")
arcpy.analysis.PairwiseBuffer(stream, scratch_stream_buffer, min_width, "ALL", "", "GEODESIC", "")
arcpy.analysis.PairwiseBuffer(stream, scratch_stream_buffer, str(min_width), "ALL", "", "GEODESIC", "")

# clip land uses to buffer
log("extracting land use data inside buffer area")
Expand All @@ -211,7 +210,7 @@ def execute(self, parameters, messages):
land_use_sql_query += " Or {} = '{}'".format(land_use_field, value)
scratch_land_use = arcpy.sa.ExtractByAttributes(land_use_raster_clip, land_use_sql_query)
else:
log("no valid land uses found in area, please try again with land uses found in analysis area")
warn("no valid land uses found in area, please try again with land uses found in analysis area")
return

# convert land usage output to polygon
Expand Down Expand Up @@ -241,11 +240,11 @@ def execute(self, parameters, messages):

# calculate acreage
log("calculating acreage of planting areas")
arcpy.management.AddField(scratch_dissolve, "Acres", "FLOAT", 2, 2)
arcpy.management.CalculateGeometryAttributes(scratch_dissolve, geometry_property=[["Acres", "AREA_GEODESIC"]], area_unit="ACRES_US")
arcpy.management.AddField(scratch_dissolve, min_area.unit, "FLOAT", 2, 2)
arcpy.management.CalculateGeometryAttributes(scratch_dissolve, geometry_property=[[min_area.unit, "AREA_GEODESIC"]], area_unit=min_area.full_unit())

# drop acreage < threshold
sql_query = "Acres >= {}".format(min_acres)
sql_query = "{} >= {}".format(min_area.unit, min_area.area)
arcpy.analysis.Select(scratch_dissolve, output_file, sql_query)

# add output to map
Expand Down
6 changes: 3 additions & 3 deletions src/BufferTools/PointPlots.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import arcpy
import platform

from ..helpers import license, empty_workspace, set_required_parameter, reload_module, log
from ..helpers import license, empty_workspace, set_required_parameter, reload_module, log, warn
from ..helpers import setup_environment as setup
from ..helpers import validate_spatial_reference as validate

Expand Down Expand Up @@ -141,8 +141,8 @@ def execute(self, parameters, messages):
log("create sampling locations")
arcpy.management.CreateSpatialSamplingLocations(scratch_buffer, output_points, sampling_method="STRAT_POLY", strata_id_field=None, strata_count_method="PROP_AREA", num_samples=num, geometry_type="POINT", min_distance="{} Feet".format(radius*2))

except:
log("Failed to create {} point plots with a radius of {} feet. It is likely because the buffer is too narrow to fit all of the point plots.".format(num, radius))
except Exception:
warn("Failed to create {} point plots with a radius of {} feet. It is likely because the buffer is too narrow to fit all of the point plots.".format(num, radius))

radius = 11.8
num = int(math.ceil(acreage * 10))
Expand Down
Loading