-
Notifications
You must be signed in to change notification settings - Fork 15
/
shp_to_inp.R
397 lines (353 loc) · 18.5 KB
/
shp_to_inp.R
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
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
#' Convert *.shp files to SWMM's *.inp file
#'
#' Reads *.shp files and other information needed for SWMM's *.inp file and returns a list of class inp. If paths are not specified default values are taken.
#'
#' @param path_options Name (incl. path) to a .txt file with SWMM sections. Write section name in lower case and in squared brackets. The following sections are allowed: options, report, raingages, evaporation, pollutant, landuse, buildup, washoff, coverages.
#' @param path_polygon Name (incl. path) to a .shp file with polygons features. At least the following subcatchment related columns must be specified: Name, Outlet, Area, RouteTo.
#' @param subcatchment_typologies R data.frame or tibble with further subcatchment related parameters. If subcatchment_typologies is given, polygon feature has to include a column named Type. Parameters defined in subcatchment_typologies parameters are merged to subcatchments by Type.
#' @param path_point Name (incl. path) to a .shp file with point features. At least the following junction related columns must be specified: Name, Bottom and Top or Ymax.
#' @param junction_parameters R data.frame or tibble with further junction related parameters (e.g. Surcharge depth).
#' @param path_outfall Name (incl. path) to a .shp file with point features. At least the following outfall related columns must be specified: Name, Bottom, Type, Gated.
#' @param path_line Name (incl. path) to a .shp file with line features. At least the following conduit related coulumns must be specified: Name, Length, Type, FromNode, ToNode, OutOffset, Geom1.
#' @param conduit_material R data.frame or tibble with further conduit related parameters (e.g. roughness). If conduit_material is given, line feature has to include a column named Material. Parameters defined in conduit_material parameters are merged to conduits by Material.
#' @param path_timeseries Name (incl. path) to a .dat file with a timeseries in SWMM format.
#' @param infiltration R data.frame or tibble with infiltration parameters related to soil properties. If infiltration is given, polygon feature has to include a column named soil. Infiltration parameters are merged to subcatchments by soil name.
#' @param path_pumps Name (incl. path) to a .shp file with line features. All parameters must be given: Name, FromNode, ToNode, Pcurve, status, Startup, Shutoff.
#' @param path_pump_curve Name (incl. path) to a .txt file with pump curve information. Having the following structure: "Name of pump" "PUMP1-4" "x" "y", without header.
#' @param path_weirs Name (incl. path) to a .shp file with line features. All parameters must be given: Name, FromNode, ToNode, Type, CrestHt, Cd, Gated, EC, Cd2, Sur.
#' @param path_storage Name (incl. path) to a .txt file with storage curve information. Having the following structure: "Name of storage" "Storage" "x" "y", without header.
#' @param path_storage_curve Name (incl. path) to a .txt file with storage curve information. Having the following structure: "Name of storage unit" "Storage" "x" "y", without header.
#' @return A list of class inp.
#' @export
#' @rdname shp_to_inp
shp_to_inp <- function(path_options = NULL,
path_polygon = NULL,
subcatchment_typologies = NULL,
path_point = NULL,
junction_parameters = NULL,
path_outfall = NULL,
path_line = NULL,
conduit_material = NULL,
path_timeseries = NULL,
infiltration = NULL,
path_pumps = NULL,
path_pump_curve = NULL,
path_weirs = NULL,
path_storage = NULL,
path_storage_curve = NULL) {
# ... check missing arguments, add default or generate error messages, in some cases default values are added later...
# check if options are available, otherwise add default:
if (missing(path_options)) {
warning("Options are missing, default values are taken.")
list_of_sections <- list()
# ... add default values for sections options, report, evaporation
# options
list_of_sections[["options"]] <- tibble::tibble(
FLOW_UNITS = "CMS",
INFILTRATION = "HORTON", # capital letters!
FLOW_ROUTING = "KINWAVE",
LINK_OFFSETS = "DEPTH",
FORCE_MAIN_EQUATION = "H-W",
IGNORE_RAINFALL = "NO",
IGNORE_SNOWMELT = "YES",
IGNORE_GROUNDWATER = "YES",
IGNORE_RDII = "YES",
IGNORE_ROUTING = "YES",
IGNORE_QUALITY = "YES",
ALLOW_PONDING = "NO",
SKIP_STEADY_STATE = "NO",
SYS_FLOW_TOL = "5",
LAT_FLOW_TOL = "5",
START_DATE = "1/1/2017",
START_TIME = "0:00:00",
END_DATE = "1/1/2017",
END_TIME = "1:00:00",
REPORT_START_DATE = "1/1/2017",
REPORT_START_TIME = "1:00:00",
SWEEP_START = "1/1",
SWEEP_END = "12/31",
DRY_DAYS = "14",
REPORT_STEP = "0:15:00",
WET_STEP = "0:05:00",
DRY_STEP = "1:00:00",
ROUTING_STEP = "0:00:05",
LENGTHENING_STEP = "0",
VARIABLE_STEP = "0",
MINIMUM_STEP = "0.5",
INERTIAL_DAMPING = "NONE",
NORMAL_FLOW_LIMITED = "BOTH",
MIN_SURFAREA = "0",
MIN_SLOPE = "0",
MAX_TRIALS = "8",
HEAD_TOLERANCE = "0.0015",
THREADS = "1",
TEMPDIR = " "
)
list_of_sections[["report"]] <- tibble::tibble(
INPUT = "NO",
CONTROLS = "NO",
SUBCATCHMENTS = "ALL",
NODES = "ALL",
LINKS = "ALL"
)
list_of_sections[["evaporation"]] <- tibble::tibble(
CONSTANT = 0,
DRY_ONLY = 0
)
} else {
# ...use information from options (option.txt)
options <- readLines(path_options)
# ...delete empty lines
options <- options[options != ""]
# find section lines
section_lines <- grep("\\[", options, value = F)
section_end <- c(section_lines[-1] - 1, length(options))
# separate section title
section_title <- gsub(
pattern = "\\[|\\]",
replacement = "",
x = options[section_lines]
)
# list all section blocks
list_of_sections <- vector(length = length(section_lines), mode = "list")
names(list_of_sections) <- section_title
# make tibble and transpose it
for (i in 1:length(section_lines)) {
list_of_sections[[i]] <- options[(section_lines[i] + 1):(section_end[i])] %>%
cbind(list_of_sections[[i]]) %>%
tibble::as.tibble(.) %>%
tidyr::separate(., 1, c("Variable", "Value"), "\t", extra = "merge", fill = "left")
# define column vector to keep the order later
cols <- list_of_sections[[i]][, 1] %>% unlist(.) %>% as.character(.)
# transpose and keep order of original data
list_of_sections[[i]] <- tidyr::gather(list_of_sections[[i]], Variable, Value) %>%
tidyr::spread_(names(.)[1], "Value") %>%
.[, cols]
# separate rows with more than one entry (e.g. in pollution section)
if (any(grepl("\t", list_of_sections[[i]]))) {
list_of_sections[[i]] <- tidyr::separate_rows(list_of_sections[[i]], (1:ncol(list_of_sections[[i]])), sep = "\t")
}
}
}
# check if polygon shape is available, return error message or read shape and add subcatchment to sections:
if (is.null(path_polygon)) {
warning("Define path to polygon file including filename and ending otherwise sections subcatchments, subareas, infiltration are missing.")
# specify object subcatchment which is called when calling: assign_parameters.coverages
subcatchment <- NULL
} else {
# read the polygon file
subcatchment <- sf::st_read(path_polygon, stringsAsFactors = F, quiet = TRUE) %>%
tibble::as_tibble() %>%
compare_to_dictionary()
# special case which should only occur if an inp has been exported using swmmr.
# in this case Area_subcatchment is Ar_sbct and Area_LID_usage is Ar_ld_s.
# Function was required to allow automatic tests with Example4.inp.
if ("Ar_sbct" %in% colnames(subcatchment)) {
colnames(subcatchment) <- gsub("Ar_sbct", "Area", colnames(subcatchment))
}
# check the structure of polygon file:
if (all(c("Name","Outlet","Area","RouteTo") %in% colnames(subcatchment))) {
list_of_sections[['subcatchments']] <- subcatchment # subcatchment_typologies
list_of_sections[['subareas']] <- subcatchment # subcatchment_typologies
list_of_sections[['polygons']] <- subcatchment
} else {
stop("The polygon file has to include at least the columns named: Name, Outlet, Area, RouteTo. For optional column names ckeck the documentation.")
}
# check infiltration model
if (list_of_sections$options$INFILTRATION == "Horton" | list_of_sections$options$INFILTRATION == "HORTON") {
list_of_sections[['infiltration']] <- list("Horton", subcatchment) # infiltration
} else {
if (list_of_sections$options$INFILTRATION == "Green_Ampt" | list_of_sections$options$INFILTRATION == "GREEN_AMPT") {
list_of_sections[['infiltration']] <- list("Green_Ampt", subcatchment)
} else {
warning("Function is only running with Horton or Green_Ampt infiltration.")
}
}
}
# ... infiltration parameter
if (is.null(infiltration)) {
if (list_of_sections$options$INFILTRATION == "Horton" | list_of_sections$options$INFILTRATION == "HORTON") {
if (!("MaxRate" %in% colnames(subcatchment)) | !("MinRate" %in% colnames(subcatchment)) | !("Decay" %in% colnames(subcatchment)) | !("DryTime" %in% colnames(subcatchment)) | !("MaxInfl" %in% colnames(subcatchment))) {
warning("All or some Horton infiltration parameters are not defined, infiltration default values are taken.")
}
}
if (list_of_sections$options$INFILTRATION == "Green_Ampt" | list_of_sections$options$INFILTRATION == "GREEN_AMPT") {
if (!("Suction" %in% colnames(subcatchment)) | !("HydCon" %in% colnames(subcatchment)) | !("IMDmax" %in% colnames(subcatchment))) {
warning("All or some Green_Ampt infiltration parameters are not defined, infiltration default values are taken.")
}
}
} else {
if (!("Soil" %in% colnames(subcatchment))) {
stop("column Soil is missing in polygon.shp")
}
}
# ... check for optional subcatchment_typologies:
if (is.null(subcatchment_typologies)) {
if (!("N_Imperv" %in% colnames(subcatchment)) | !("N_Perv" %in% colnames(subcatchment)) | !("S_Imperv" %in% colnames(subcatchment)) | !("S_Perv" %in% colnames(subcatchment)) | !("Pct_Zero" %in% colnames(subcatchment)) | !("RouteTo" %in% colnames(subcatchment)) | !("PctRouted" %in% colnames(subcatchment)) | !("Rain_Gage" %in% colnames(subcatchment)) | !("CurbLen" %in% colnames(subcatchment)) | !("Snowpack" %in% colnames(subcatchment)) | !("PercImperv" %in% colnames(subcatchment)) | !("Slope" %in% colnames(subcatchment)) | !("Width" %in% colnames(subcatchment))) {
warning("N_Imperv, N_Perv, S_Imperv, S_Perv, Rain_Gage, CurbLen, Snowpack, PercImperv, Slope or Width are not defined in polygon.shp or Subcatchment_typologies. Check polygon.shp for completeness otherwise missing parameters in the sections subcatchment and subareas will be filled with default values.")
}
}
if (!(is.null(subcatchment_typologies))) {
if (!("Type" %in% colnames(subcatchment))) {
stop("column Type is missing in polygon.shp")
}
}
# ... and for the junction point shape:
if (is.null(path_point) == T) {
warning("Define path to point file including filename and ending otherwise sections junctions and coordinates are missing.")
# specify object junction which is called when testing column names for warn message in junction_parameters
junctions <- NULL
} else {
# read junction point file
junctions <- sf::st_read(path_point, stringsAsFactors = F, quiet = TRUE) %>%
tibble::as_tibble() %>%
compare_to_dictionary()
# check column names:
if (all(c("Name", "Bottom") %in% colnames(junctions))) {
if ("Top" %in% colnames(junctions) | "Ymax" %in% colnames(junctions)) {
list_of_sections[["junctions"]] <- junctions
list_of_sections[["coordinates"]] <- junctions[, c("Name", "geometry")]
}
} else {
stop("The point file has to include at least the columns named: Name, Bottom and Top or Ymax.")
}
}
if (is.null(junction_parameters)) {
if (!("Y" %in% colnames(junctions)) | !("Ysur" %in% colnames(junctions)) | !("Apond" %in% colnames(junctions))) {
warning(" Y, Ysur or Apond are not defined in point.shp or junction_parameters. Check point.shp for completeness otherwise missing parameters in the section junctions will be filled with default values.")
}
}
# ... also do it for the outfall point shape:
if (is.null(path_outfall)) {
warning("Define path to outfall file including filename and ending otherwise section outfall is missing.")
} else {
# outfalls
outfalls <- sf::st_read(path_outfall, stringsAsFactors = F, quiet = TRUE) %>%
tibble::as_tibble() %>%
compare_to_dictionary()
# check for completeness:
if (all(c("Name", "Bottom", "Type") %in% colnames(outfalls))) {
list_of_sections[["outfalls"]] <- outfalls
list_of_sections[["coordinates"]] <- rbind(list_of_sections[["coordinates"]], outfalls[, c("Name", "geometry")])
} else {
stop("The outfall point shape has to include at least the columns named: Name, Bottom, Type.")
}
}
# ... checking, reading or adding default values for optional function arguments:
# ... timeseries
if (is.null(path_timeseries)) {
warning("Define path to timeseries file including filename and ending, otherwise default values are taken.")
list_of_sections[["timeseries"]] <- tibble::tibble(
Name = "default_rain",
date = " ",
time_min = 1,
rain_mm = 1
)
} else {
# add path to timeseries file
name_RG <- gsub("TIMESERIES ", "", list_of_sections[["raingages"]]$Source)
list_of_sections[["timeseries"]] <- paste0(name_RG, " ", "FILE ", path_timeseries)
}
# ...add Pumps section if path_pump exists
if (is.null(path_pumps) == F) {
# read shape
pumps <- sf::st_read(path_pumps, stringsAsFactors = F, quiet = TRUE) %>%
tibble::as_tibble() %>%
compare_to_dictionary()
# add a section to list_of_sections
list_of_sections[["pumps"]] <- pumps
}
# ... add pump curve
if (is.null(path_pump_curve) == F) {
# add table of pump curves
list_of_sections[["curves"]] <- suppressMessages(readr::read_table2(path_pump_curve, col_names = c("Name", "Type", "X", "Y")))
}
# ...add weirs if path_weirs exists
if (is.null(path_weirs) == F) {
# read shape
weirs <- sf::st_read(path_weirs, stringsAsFactors = F, quiet = TRUE) %>%
tibble::as_tibble() %>%
compare_to_dictionary()
# add section to list_of_sections
list_of_sections[["weirs"]] <- weirs
}
# ...add storages
if (is.null(path_storage) == F) {
# read shape
storage <- sf::st_read(path_storage, stringsAsFactors = F, quiet = TRUE) %>%
tibble::as_tibble() %>%
compare_to_dictionary()
# add section
list_of_sections[["storage"]] <- storage
list_of_sections[["coordinates"]] <- rbind(list_of_sections[["coordinates"]], storage[, c("Name", "geometry")])
}
# ... add storage curve
if (is.null(path_storage_curve) == F) {
if ("curves" %in% names(list_of_sections)) {
# add table of storage curves to existing curve table
storage_curves <- suppressMessages(readr::read_table2(path_storage_curve, col_names = c("Name", "Type", "X", "Y")))
list_of_sections[["curves"]] <- rbind(list_of_sections[["curves"]], storage_curves)
} else {
# add table of storage curves to a new list entry
list_of_sections[["curves"]] <- suppressMessages(readr::read_table2(path_storage_curve, col_names = c("Name", "Type", "X", "Y")))
}
}
# ... do the same for the conduit line shape:
if (is.null(path_line)) {
warning("Define path to line file including filename and ending, otherwise section conduits is missing.")
# specify object conduits which is called when testing column names for warn message in conduit_material
conduits <- NULL
} else {
# read the line shape file
# conduits
conduits <- sf::st_read(path_line, stringsAsFactors = F, quiet = TRUE) %>%
tibble::as_tibble() %>%
compare_to_dictionary()
# check column names
if (all(c("Name", "Length", "Shape", "FromNode", "ToNode", "OutOffset", "Geom1") %in% colnames(conduits))) {
list_of_sections[["conduits"]] <- conduits
list_of_sections[["xsections"]] <- conduits
} else {
stop("The line file has to include at least the columns named: Name, Length, Shape, FromNode, ToNode, OutOffset, Geom1.")
}
}
# ...check for material properties:
if (is.null(conduit_material)) {
if (!("Roughness" %in% colnames(conduits))) {
warning("Roughness is not defined in line.shp or Conduit_material. Check line.shp for completeness otherwise missing parameters in the sections conduits will be filled with default values.")
}
} else {
if (!("Material" %in% colnames(conduits))) {
stop("column Material is missing in line.shp")
}
}
# ...further processing of entries in list_of_sections...
res <- list_of_sections %>%
# define classes
purrr::imap(function(.x, .y) {
class(.x) <- c(.y, class(.x))
return(.x)
}) %>%
# assign section parameters individually
purrr::map(., ~ assign_parameters(.x,
infiltration,
subcatchment,
subcatchment_typologies,
conduit_material,
junction_parameters)) %>%
# reclass to tibbles for consistency
purrr::map( ~ {class(.x) <- c("tbl_df", "tbl", "data.frame");.x})
# adjust order of sections
section_order <- c("title", "options", "evaporation", "raingages", "subcatchments",
"subareas", "infiltration", "aquifers", "groundwater",
"LID_controls", "LID_usage", "junctions", "outfalls", "storage",
"conduits", "pumps", "weirs", "xsections", "controls", "DWF",
"pollutants", "landuses", "coverages", "loadings", "buildup",
"washoff", "inflows", "timeseries", "curves", "patterns",
"report", "tags", "map", "coordinates", "vertices", "polygons",
"labels", "symbols", "backdrop")
res <- res[section_order[section_order %in% names(res)]]
# assign class attribute
class(res) <- "inp"
return(res)
}