-
Notifications
You must be signed in to change notification settings - Fork 0
/
WILT Toolbox.pyt
336 lines (274 loc) · 11.3 KB
/
WILT Toolbox.pyt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#-------------------------------------------------------------------
# Author: Seth Younger
#-------------------------------------------------------------------
# Change log:
# 12/1/2020 - better names for outputs and more instructional informaiton
#-------------------------------------------------------------------
import os, time, sys
import arcpy
from arcpy import env, AddMessage
from arcpy.sa import *
class Toolbox(object):
def __init__(self):
"""Define the toolbox (the name of the toolbox is the name of the
.pyt file)."""
self.label = "WILT Toolbox"
self.alias = "WILT"
# List of tool classes associated with this toolbox
self.tools = [WILT, CTI]
class WILT(object):
def __init__(self):
"""Define the tool (tool name is the name of the class)."""
self.label = "WILT"
self.description = "This tool uses a DEM and points representing surface water " + \
"to calculate the Wetness Index Based on Landscape Position and Topography " + \
"or WILT, which is described in Bitew et al. 2020." + \
" https://doi.org/10.1016/j.jenvman.2019.109863. " + \
"Note: The Spatial Analyst and 3D Analyst Extensions need to be activated. " + \
"Water cell points need to be developed for your site using surface water locations, " + \
"including lakes and streams. Well data can also be used where available. "
def getParameterInfo(self):
#Define parameter definitions
WorkSpace = arcpy.Parameter(
displayName="Select a folder to save outputs to",
name="workSpace",
datatype="DEWorkspace",
parameterType="Required",
direction="Input")
WaterCells = arcpy.Parameter(
displayName="Water Cell Points",
name="WaterCells",
datatype="GPFeatureLayer",
parameterType="Required",
direction="Input")
WaterCells.filter.list = ["Point"]
DEM = arcpy.Parameter(
displayName="DEM",
name="DEM",
datatype="GPRasterLayer",
parameterType="Required",
direction="Input")
prefix = arcpy.Parameter(
displayName="Output file prefix",
name="prefix",
datatype="GPString",
parameterType="Required",
direction="Input")
parameters = [WorkSpace, WaterCells, DEM, prefix]
return parameters
def isLicensed(self): #optional
"""Allow the tool to execute, only if the ArcGIS 3D Analyst extension
is available."""
try:
if arcpy.CheckExtension("3D") != "Available":
raise Exception
except Exception:
return False # tool cannot be executed
return True # tool can be executed
"""Allow the tool to execute, only if the ArcGIS Spatial Analyst extension
is available."""
try:
if arcpy.CheckExtension("Spatial") != "Available":
raise Exception
except Exception:
return False # tool cannot be executed
return True # tool can be executed
def updateParameters(self, parameters): #optional
"""Modify the values and properties of parameters before internal
validation is performed. This method is called whenever a parameter
has been changed."""
return
def updateMessages(self, parameters):
"""Modify the messages created by internal validation for each tool
parameter. This method is called after internal validation."""
return
def execute(self, parameters, messages):
# Check out any necessary licenses
arcpy.CheckOutExtension("Spatial")
arcpy.CheckOutExtension("3D")
# Environment settings
env.overwriteOutput = True
# Define variables
# Workspace from parameters
env.workspace = parameters[0].valueAsText
WaterCells = parameters[1].valueAsText
DEM = parameters[2].valueAsText
prefix = parameters[3].valueAsText
# Define model created variables
WaterElev = prefix + "WaterElev.shp"
DEM_Points = prefix + "dem_points.shp"
# Environment settings from the DEM
env.extent = DEM
env.cellSize = DEM
# cellsize of the DEM as a float for calculations
description = arcpy.Describe(DEM)
size = description.children[0].meanCellHeight
AddMessage("The DEM cell size is " + str(size) + " meters")
# Execute Fill
AddMessage("Filling DEM")
FillDEM = Fill(DEM)
# Execute Flow Direction
AddMessage("Running Flow Direction tool")
FlowDir = FlowDirection(FillDEM, "FORCE")
# Execute Flow Accumulation
AddMessage("Running Flow Accumulation Tool")
FlowAcc = FlowAccumulation(FlowDir, "","INTEGER")
# Calculate SCA
AddMessage("Calculating Specific Contributing Area")
sca = (FlowAcc + size) * (size * size)
# Extract water cell elevation to points
AddMessage("Running extract values to points tool")
ExtractValuesToPoints(WaterCells, DEM, WaterElev, "INTERPOLATE", "VALUE_ONLY")
# Krig water surface from water cell elevations
AddMessage("Kriging water surface from water cell elevations")
kModelOrdinary = KrigingModelOrdinary("SPHERICAL")
kRadius = RadiusVariable(12)
# Execute Kriging
GroundwaterT2 = Kriging(WaterElev, "RASTERVALU", kModelOrdinary, env.cellSize, kRadius, "")
GroundwaterT2.save(prefix + "GW_elev_from_streams.tif")
# Calculate water table depth
AddMessage("Calculating groundwater depth")
waterTable = (DEM - GroundwaterT2)
# Calculate water table depths less than 0.2 to 0.2, the rest stay the same
AddMessage("Reclassify GW depths < 0.2m to 0.2m")
waterTable2 = Con(waterTable < 0.2, 0.2, waterTable)
waterTable2.save(prefix + "GW_depth.tif")
# Convert DEM to Points
arcpy.RasterToPoint_conversion(FillDEM, DEM_Points, "Value")
AddMessage("Converting DEM to points")
# Near distance between each DEM cell and water cells
AddMessage("Calculating near distance to water cells")
arcpy.Near_analysis(DEM_Points, WaterCells, "", "NO_LOCATION", "NO_ANGLE")
# Convert near distance to raster
AddMessage("Converting near distance to raster")
DeltaX = arcpy.PointToRaster_conversion(DEM_Points, "NEAR_DIST")
# Calculate Delta X's less than 5 to 5, the rest stay the same
AddMessage("Running Con on near distance raster")
deltaX5 = Con(DeltaX < 5,5, DeltaX)
# ScaGWX
AddMessage("Calculating scaGWX")
scaGWX = sca / (waterTable2 * deltaX5)
# Calculate Slope
AddMessage("Calculating Slope")
slope = Slope(FillDEM, "DEGREE", 1)
# Convert Slope from degrees to Radians
AddMessage("Converting slope from degrees to radians")
slopeRadians = (slope * 0.0174532925)
# Calculate TAN of Slope
arcpy.AddMessage("Calculating Tan of slope")
TanSlope = Tan(slopeRadians)
# Calculate TanSlope's of 0 to 0.001, the rest stay the same
AddMessage("Running Con on TanSlope")
TanSlope_0 = Con(TanSlope <= 0,0.001, TanSlope)
# Calculate SCAGW
AddMessage("Calculating SCAGW")
SCAGW = sca / waterTable2
# Calculate WILT using sqrt(dx^2+dy^2)
AddMessage("Calculating WILT")
#sqrt(waterTable2^2+deltaX5^2)
dx2 = Power(waterTable2, 2)
dy2 = Power(deltaX5, 2)
dy_dx_plus = Plus(dx2, dy2)
dy_dx_sqrt = SquareRoot(dy_dx_plus)
scaGWX2 = sca / dy_dx_sqrt
WILT = Ln(scaGWX2 / TanSlope_0)
AddMessage("Saving WILT")
WILTG.save(prefix + "WILT.tif")
# Check in any necessary licenses
arcpy.CheckInExtension("Spatial")
arcpy.CheckInExtension("3D")
#---------------------------------------------------------------------------
# Compound topographic wetness index
#---------------------------------------------------------------------------
class CTI(object):
def __init__(self):
"""Define the tool (tool name is the name of the class)."""
self.label = "CTI"
self.description = "Calculates the compound topographic index (CTI) or 'soil wetness' transformation. https://wikispaces.psu.edu/display/AnthSpace/Compound+Topographic+Index"
def getParameterInfo(self):
#Define parameter definitions
WorkSpace = arcpy.Parameter(
displayName="Select a folder to save outputs to",
name="workSpace",
datatype="DEWorkspace",
parameterType="Required",
direction="Input")
DEM = arcpy.Parameter(
displayName="DEM",
name="DEM",
datatype="GPRasterLayer",
parameterType="Required",
direction="Input")
CTI = arcpy.Parameter(
displayName="Output CTI",
name="CTI",
datatype="GPRasterLayer",
parameterType="Required",
direction="Output")
prefix = arcpy.Parameter(
displayName="Output file prefix",
name="prefix",
datatype="GPString",
parameterType="Required",
direction="Input")
parameters = [WorkSpace, DEM, CTI, prefix]
return parameters
def isLicensed(self): #optional
"""Allow the tool to execute, only if the ArcGIS 3D Analyst extension
is available."""
try:
if arcpy.CheckExtension("3D") != "Available":
raise Exception
except Exception:
return False # tool cannot be executed
return True # tool can be executed
"""Allow the tool to execute, only if the ArcGIS Spatial Analyst extension
is available."""
try:
if arcpy.CheckExtension("Spatial") != "Available":
raise Exception
except Exception:
return False # tool cannot be executed
return True # tool can be executed
def updateParameters(self, parameters): #optional
"""Modify the values and properties of parameters before internal
validation is performed. This method is called whenever a parameter
has been changed."""
return
def updateMessages(self, parameters):
"""Modify the messages created by internal validation for each tool
parameter. This method is called after internal validation."""
return
def execute(self, parameters, messages):
# Check out any necessary licenses
arcpy.CheckOutExtension("Spatial")
# Environment settings
env.overwriteOutput = True
# Define variables
# Workspace from parameters
env.workspace = parameters[0].valueAsText
DEM = parameters[1].valueAsText
CTI = parameters[2].valueAsText
prefix = parameters[3].valueAsText
# Environment settings from the DEM
env.extent = DEM
env.cellSize = DEM
# cellsize of the DEM as a float for calculations
description = arcpy.Describe(DEM)
size = description.children[0].meanCellHeight
area = (size * size)
AddMessage("The DEM cell size is " + str(size) + " meters")
AddMessage("The DEM cell area is " + str(area) + " square meters")
#Set message about running and run the calculation
AddMessage("Running CTI")
fd = FlowDirection(DEM)
sca = FlowAccumulation(fd)
slope = ( Slope(DEM) * 1.570796 ) / 90
tan_slp = Con( slope > 0, Tan(slope), 0.001 )
corr_sca = ( sca + 1 ) * size
CTI = Ln ( corr_sca / tan_slp )
CTI.save (parameters[2].valueAsText)
#Set message about running
AddMessage("CTI Complete")
# Check in any necessary licenses
arcpy.CheckInExtension("Spatial")