|
| 1 | +# @param measures list with elements green_roof, unpaved, to_swale representing |
| 2 | +# the target percentages of the total areas corresponding to each measure. |
| 3 | +# A value of NA means that the corresponding measure column is not touched. |
| 4 | +apply_measures_to_blocks <- function(blocks, measures, dbg = FALSE, check = FALSE) |
| 5 | +{ |
| 6 | + #dbg = FALSE; check = FALSE |
| 7 | + |
| 8 | + # Define helper functions |
| 9 | + { |
| 10 | + report_problem <- function(...) { |
| 11 | + problem <- paste(unlist(list(...)), collapse = "") |
| 12 | + warning(problem, call. = FALSE) |
| 13 | + } |
| 14 | + |
| 15 | + debug <- function(...) { |
| 16 | + if (dbg) { |
| 17 | + writeLines(...) |
| 18 | + } |
| 19 | + } |
| 20 | + |
| 21 | + share_of_sum <- function(x) { |
| 22 | + # Do not divide by zero, return vector of zeros instead. |
| 23 | + if ((s <- sum(x)) == 0) { |
| 24 | + rep(0, length(x)) |
| 25 | + } else { |
| 26 | + x/s |
| 27 | + } |
| 28 | + } |
| 29 | + |
| 30 | + check_if_target_was_reached <- function(blocks, measure) { |
| 31 | + obtained <- kwb.rabimo::get_measure_stats(blocks)[[measure]]$mean |
| 32 | + target <- measures[[measure]] |
| 33 | + if (!isTRUE(all.equal(obtained, target))) { |
| 34 | + warning( |
| 35 | + sprintf("Target value %0.2f for '%s' ", target, measure), |
| 36 | + sprintf("could not be achieved. Actual value: %0.2f", obtained), |
| 37 | + call. = FALSE |
| 38 | + ) |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + check_for_negative_values <- function(blocks, measure) { |
| 43 | + is_negative <- blocks[[measure]] < 0 |
| 44 | + if (any(is_negative)) { |
| 45 | + warning(call. = FALSE, sprintf( |
| 46 | + "There are %d negative values in column '%s'", |
| 47 | + sum(is_negative), measure |
| 48 | + )) |
| 49 | + } |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + # The prefix "a_" refers to absolute area (in square metres) |
| 54 | + |
| 55 | + # Provide the total areas and roof areas in advance. They are not changed by |
| 56 | + # the measures. |
| 57 | + a_total <- blocks$total_area |
| 58 | + a_roof <- blocks$total_area * blocks$roof |
| 59 | + |
| 60 | + # 1. Handle measure "green roof" |
| 61 | + if (!is.na(measures$green_roof)) { |
| 62 | + |
| 63 | + a_green_roof <- a_roof * blocks$green_roof |
| 64 | + |
| 65 | + # Total green roof area to add (if value >= 0) or to remove (if value < 0) |
| 66 | + a_green_roof_change <- sum(measures$green_roof * a_total) - sum(a_green_roof) |
| 67 | + |
| 68 | + # Roof area that can be converted to green roof area |
| 69 | + if (a_green_roof_change >= 0) { |
| 70 | + |
| 71 | + # increase green roof area |
| 72 | + a_green_roof_potential <- a_roof - a_green_roof |
| 73 | + |
| 74 | + if (a_green_roof_change > sum(a_green_roof_potential)) { |
| 75 | + report_problem(sprintf( |
| 76 | + "Not enough (non-green) roof area available (%0.2f m2 missing)", |
| 77 | + a_green_roof_change - sum(a_green_roof_potential) |
| 78 | + )) |
| 79 | + } |
| 80 | + |
| 81 | + } else { |
| 82 | + |
| 83 | + # decrease green roof area |
| 84 | + a_green_roof_potential <- a_green_roof |
| 85 | + |
| 86 | + if (- a_green_roof_change > sum(a_green_roof_potential)) { |
| 87 | + report_problem(sprintf( |
| 88 | + "Not enough green roof area available (%0.2f m2 missing)", |
| 89 | + - a_green_roof_change - sum(a_green_roof_potential) |
| 90 | + )) |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + # Distribute change in green roof area to the different blocks |
| 95 | + a_green_roof_new <- a_green_roof + share_of_sum(a_green_roof_potential) * a_green_roof_change |
| 96 | + |
| 97 | + # Update column "green_roof" (as fraction of roof area) |
| 98 | + blocks$green_roof <- ifelse(a_roof == 0, 0, a_green_roof_new / a_roof) |
| 99 | + } |
| 100 | + |
| 101 | + # 2. Handle measure "Unsealing" |
| 102 | + if (!is.na(measures$unpaved)) { |
| 103 | + |
| 104 | + # current paved/unpaved areas |
| 105 | + a_paved <- a_total * blocks$pvd |
| 106 | + a_unpaved <- a_total - a_roof - a_paved |
| 107 | + |
| 108 | + # Required increase/decrease in unpaved area |
| 109 | + a_unpaved_change <- measures$unpaved * sum(a_total) - sum(a_unpaved) |
| 110 | + unpave <- a_unpaved_change >= 0 |
| 111 | + |
| 112 | + debug(sprintf( |
| 113 | + "%s area to be %s: %0.2f m2", |
| 114 | + ifelse(unpave, "Paved", "Unpaved"), |
| 115 | + ifelse(unpave, "unpaved", "paved"), |
| 116 | + abs(a_unpaved_change) |
| 117 | + )) |
| 118 | + |
| 119 | + if (unpave) { |
| 120 | + |
| 121 | + a_potential <- a_paved |
| 122 | + |
| 123 | + if (a_unpaved_change > sum(a_potential)) { |
| 124 | + report_problem(sprintf( |
| 125 | + "Not enough paved area available to be unpaved (%0.2f m2 missing)", |
| 126 | + a_unpaved_change - sum(a_potential) |
| 127 | + )) |
| 128 | + } |
| 129 | + |
| 130 | + } else { |
| 131 | + |
| 132 | + # actually pave instead of unpave! |
| 133 | + a_potential <- a_unpaved |
| 134 | + |
| 135 | + # a_unpaved_change is negative here |
| 136 | + if (- a_unpaved_change > sum(a_potential)) { |
| 137 | + report_problem(sprintf( |
| 138 | + "Not enough unpaved area available to be paved (%0.2f m2 missing)", |
| 139 | + - a_unpaved_change - sum(a_potential) |
| 140 | + )) |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + # Distribute change in paved/unpaved area to the different blocks |
| 145 | + a_paved_new <- a_paved - share_of_sum(a_potential) * a_unpaved_change |
| 146 | + |
| 147 | + blocks$pvd <- ifelse(a_total == 0, 0, a_paved_new / a_total) |
| 148 | + } |
| 149 | + |
| 150 | + # 3. Handle measure "Connection to swales" |
| 151 | + if (!is.na(measures$to_swale)) { |
| 152 | + a_sealed <- a_roof + a_total * blocks$pvd |
| 153 | + a_to_swale <- blocks$to_swale * a_sealed |
| 154 | + a_to_swale_change <- sum(measures$to_swale * a_total) - sum(a_to_swale) |
| 155 | + |
| 156 | + increase <- a_to_swale_change >= 0 |
| 157 | + |
| 158 | + debug(sprintf( |
| 159 | + "Sealed area to be %s swales: %0.2f m2", |
| 160 | + ifelse(increase, "connected to", "disconnected from"), |
| 161 | + abs(a_to_swale_change) |
| 162 | + )) |
| 163 | + |
| 164 | + if (increase) { |
| 165 | + |
| 166 | + a_to_swale_potential <- a_sealed - a_to_swale |
| 167 | + |
| 168 | + if (a_to_swale_change > sum(a_to_swale_potential)) { |
| 169 | + report_problem(sprintf( |
| 170 | + "Not enough sealed area available to be connected to swales (%0.2f m2 missing)", |
| 171 | + a_to_swale_change - sum(a_to_swale_potential) |
| 172 | + )) |
| 173 | + } |
| 174 | + |
| 175 | + } else { |
| 176 | + |
| 177 | + # a_to_swale_change is negative here |
| 178 | + a_to_swale_potential <- a_to_swale |
| 179 | + |
| 180 | + if (- a_to_swale_change > sum(a_to_swale_potential)) { |
| 181 | + report_problem(sprintf( |
| 182 | + "Not enough swale-connected sealed area available to be disconnected (%0.2f m2 missing)", |
| 183 | + abs(a_to_swale_change) - sum(a_to_swale_potential) |
| 184 | + )) |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + # distribute |
| 189 | + a_to_swale_new <- a_to_swale + share_of_sum(a_to_swale_potential) * a_to_swale_change |
| 190 | + |
| 191 | + # Update column "to_swale" |
| 192 | + blocks$to_swale <- ifelse(a_sealed == 0, 0, a_to_swale_new / a_sealed) |
| 193 | + } |
| 194 | + |
| 195 | + # Targets reached? |
| 196 | + if (check) { |
| 197 | + for (measure in names(measures)[!is.na(measures)]) { |
| 198 | + check_if_target_was_reached(blocks, measure) |
| 199 | + check_for_negative_values(blocks, measure) |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + blocks |
| 204 | +} |
0 commit comments