| 1 | #!/usr/bin/env python
|
|---|
| 2 | # -*- coding: utf-8 -*-
|
|---|
| 3 | ############################################################################
|
|---|
| 4 | #
|
|---|
| 5 | # MODULE: t.rast.neighbors
|
|---|
| 6 | # AUTHOR(S): Soeren Gebbert
|
|---|
| 7 | #
|
|---|
| 8 | # PURPOSE: Performs a neighborhood analysis for each map in a space time
|
|---|
| 9 | # raster dataset.
|
|---|
| 10 | # COPYRIGHT: (C) 2013 by the GRASS Development Team
|
|---|
| 11 | #
|
|---|
| 12 | # This program is free software; you can redistribute it and/or modify
|
|---|
| 13 | # it under the terms of the GNU General Public License as published by
|
|---|
| 14 | # the Free Software Foundation; either version 2 of the License, or
|
|---|
| 15 | # (at your option) any later version.
|
|---|
| 16 | #
|
|---|
| 17 | # This program is distributed in the hope that it will be useful,
|
|---|
| 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 20 | # GNU General Public License for more details.
|
|---|
| 21 | #
|
|---|
| 22 | #############################################################################
|
|---|
| 23 |
|
|---|
| 24 | #%module
|
|---|
| 25 | #% description: Performs a neighborhood analysis for each map in a space time raster dataset.
|
|---|
| 26 | #% keyword: temporal
|
|---|
| 27 | #% keyword: aggregation
|
|---|
| 28 | #% keyword: raster
|
|---|
| 29 | #% keyword: time
|
|---|
| 30 | #%end
|
|---|
| 31 |
|
|---|
| 32 | #%option G_OPT_STRDS_INPUT
|
|---|
| 33 | #%end
|
|---|
| 34 |
|
|---|
| 35 | #%option G_OPT_STRDS_OUTPUT
|
|---|
| 36 | #%end
|
|---|
| 37 |
|
|---|
| 38 | #%option G_OPT_T_WHERE
|
|---|
| 39 | #%end
|
|---|
| 40 |
|
|---|
| 41 | #%option
|
|---|
| 42 | #% key: size
|
|---|
| 43 | #% type: integer
|
|---|
| 44 | #% description: Neighborhood size
|
|---|
| 45 | #% required: no
|
|---|
| 46 | #% multiple: no
|
|---|
| 47 | #% answer: 3
|
|---|
| 48 | #%end
|
|---|
| 49 |
|
|---|
| 50 | #%option
|
|---|
| 51 | #% key: method
|
|---|
| 52 | #% type: string
|
|---|
| 53 | #% description: Aggregate operation to be performed on the raster maps
|
|---|
| 54 | #% required: yes
|
|---|
| 55 | #% multiple: no
|
|---|
| 56 | #% options: average,median,mode,minimum,maximum,range,stddev,sum,count,variance,diversity,interspersion,quart1,quart3,perc90,quantile
|
|---|
| 57 | #% answer: average
|
|---|
| 58 | #%end
|
|---|
| 59 |
|
|---|
| 60 | #%option
|
|---|
| 61 | #% key: basename
|
|---|
| 62 | #% type: string
|
|---|
| 63 | #% label: Basename of the new generated output maps
|
|---|
| 64 | #% description: A numerical suffix separated by an underscore will be attached to create a unique identifier
|
|---|
| 65 | #% required: yes
|
|---|
| 66 | #% multiple: no
|
|---|
| 67 | #% gisprompt:
|
|---|
| 68 | #%end
|
|---|
| 69 |
|
|---|
| 70 | #%option
|
|---|
| 71 | #% key: suffix
|
|---|
| 72 | #% type: string
|
|---|
| 73 | #% description: Suffix to add at basename: set 'gran' for granularity, 'time' for the full time format, 'num' for numerical suffix with a specific number of digits (default %05)
|
|---|
| 74 | #% answer: gran
|
|---|
| 75 | #% required: no
|
|---|
| 76 | #% multiple: no
|
|---|
| 77 | #%end
|
|---|
| 78 |
|
|---|
| 79 | #%option
|
|---|
| 80 | #% key: nprocs
|
|---|
| 81 | #% type: integer
|
|---|
| 82 | #% description: Number of r.neighbor processes to run in parallel
|
|---|
| 83 | #% required: no
|
|---|
| 84 | #% multiple: no
|
|---|
| 85 | #% answer: 1
|
|---|
| 86 | #%end
|
|---|
| 87 |
|
|---|
| 88 | #%flag
|
|---|
| 89 | #% key: n
|
|---|
| 90 | #% description: Register Null maps
|
|---|
| 91 | #%end
|
|---|
| 92 |
|
|---|
| 93 | #%flag
|
|---|
| 94 | #% key: r
|
|---|
| 95 | #% description: Ignore the current region settings and use the raster map regions
|
|---|
| 96 | #%end
|
|---|
| 97 |
|
|---|
| 98 | from __future__ import print_function
|
|---|
| 99 |
|
|---|
| 100 | import copy
|
|---|
| 101 | import grass.script as grass
|
|---|
| 102 |
|
|---|
| 103 |
|
|---|
| 104 | ############################################################################
|
|---|
| 105 |
|
|---|
| 106 | def main():
|
|---|
| 107 | # lazy imports
|
|---|
| 108 | import grass.temporal as tgis
|
|---|
| 109 | import grass.pygrass.modules as pymod
|
|---|
| 110 |
|
|---|
| 111 | # Get the options
|
|---|
| 112 | input = options["input"]
|
|---|
| 113 | output = options["output"]
|
|---|
| 114 | where = options["where"]
|
|---|
| 115 | size = options["size"]
|
|---|
| 116 | base = options["basename"]
|
|---|
| 117 | register_null = flags["n"]
|
|---|
| 118 | use_raster_region = flags["r"]
|
|---|
| 119 | method = options["method"]
|
|---|
| 120 | nprocs = options["nprocs"]
|
|---|
| 121 | time_suffix = options["suffix"]
|
|---|
| 122 |
|
|---|
| 123 | # Make sure the temporal database exists
|
|---|
| 124 | tgis.init()
|
|---|
| 125 | # We need a database interface
|
|---|
| 126 | dbif = tgis.SQLDatabaseInterfaceConnection()
|
|---|
| 127 | dbif.connect()
|
|---|
| 128 |
|
|---|
| 129 | overwrite = grass.overwrite()
|
|---|
| 130 |
|
|---|
| 131 | sp = tgis.open_old_stds(input, "strds", dbif)
|
|---|
| 132 | maps = sp.get_registered_maps_as_objects(where=where, dbif=dbif)
|
|---|
| 133 |
|
|---|
| 134 | if not maps:
|
|---|
| 135 | dbif.close()
|
|---|
| 136 | grass.warning(_("Space time raster dataset <%s> is empty") % sp.get_id())
|
|---|
| 137 | return
|
|---|
| 138 |
|
|---|
| 139 | new_sp = tgis.check_new_stds(output, "strds", dbif=dbif,
|
|---|
| 140 | overwrite=overwrite)
|
|---|
| 141 | # Configure the r.neighbor module
|
|---|
| 142 | neighbor_module = pymod.Module("r.neighbors", input="dummy",
|
|---|
| 143 | output="dummy", run_=False,
|
|---|
| 144 | finish_=False, size=int(size),
|
|---|
| 145 | method=method, overwrite=overwrite,
|
|---|
| 146 | quiet=True)
|
|---|
| 147 |
|
|---|
| 148 | gregion_module = pymod.Module("g.region", raster="dummy", run_=False,
|
|---|
| 149 | finish_=False,)
|
|---|
| 150 |
|
|---|
| 151 | # The module queue for parallel execution
|
|---|
| 152 | process_queue = pymod.ParallelModuleQueue(int(nprocs))
|
|---|
| 153 |
|
|---|
| 154 | count = 0
|
|---|
| 155 | num_maps = len(maps)
|
|---|
| 156 | new_maps = []
|
|---|
| 157 |
|
|---|
| 158 | # run r.neighbors all selected maps
|
|---|
| 159 | for map in maps:
|
|---|
| 160 | count += 1
|
|---|
| 161 | if sp.get_temporal_type() == 'absolute' and time_suffix == 'gran':
|
|---|
| 162 | suffix = tgis.create_suffix_from_datetime(map.temporal_extent.get_start_time(),
|
|---|
| 163 | sp.get_granularity())
|
|---|
| 164 | map_name = "{ba}_{su}".format(ba=base, su=suffix)
|
|---|
| 165 | elif sp.get_temporal_type() == 'absolute' and time_suffix == 'time':
|
|---|
| 166 | suffix = tgis.create_time_suffix(map)
|
|---|
| 167 | map_name = "{ba}_{su}".format(ba=base, su=suffix)
|
|---|
| 168 | else:
|
|---|
| 169 | map_name = tgis.create_numeric_suffix(base, count, time_suffix)
|
|---|
| 170 |
|
|---|
| 171 | new_map = tgis.open_new_map_dataset(map_name, None, type="raster",
|
|---|
| 172 | temporal_extent=map.get_temporal_extent(),
|
|---|
| 173 | overwrite=overwrite, dbif=dbif)
|
|---|
| 174 | new_maps.append(new_map)
|
|---|
| 175 |
|
|---|
| 176 | mod = copy.deepcopy(neighbor_module)
|
|---|
| 177 | mod(input=map.get_id(), output=new_map.get_id())
|
|---|
| 178 |
|
|---|
| 179 | if use_raster_region is True:
|
|---|
| 180 | reg = copy.deepcopy(gregion_module)
|
|---|
| 181 | reg(raster=map.get_id())
|
|---|
| 182 | print(reg.get_bash())
|
|---|
| 183 | print(mod.get_bash())
|
|---|
| 184 | mm = pymod.MultiModule([reg, mod], sync=False, set_temp_region=True)
|
|---|
| 185 | process_queue.put(mm)
|
|---|
| 186 | else:
|
|---|
| 187 | print(mod.get_bash())
|
|---|
| 188 | process_queue.put(mod)
|
|---|
| 189 |
|
|---|
| 190 | # Wait for unfinished processes
|
|---|
| 191 | process_queue.wait()
|
|---|
| 192 | proc_list = process_queue.get_finished_modules()
|
|---|
| 193 |
|
|---|
| 194 | # Check return status of all finished modules
|
|---|
| 195 | error = 0
|
|---|
| 196 | for proc in proc_list:
|
|---|
| 197 | if proc.popen.returncode != 0:
|
|---|
| 198 | grass.error(_("Error running module: %\n stderr: %s") %(proc.get_bash(), proc.outputs.stderr))
|
|---|
| 199 | error += 1
|
|---|
| 200 |
|
|---|
| 201 | if error > 0:
|
|---|
| 202 | grass.fatal(_("Error running modules."))
|
|---|
| 203 |
|
|---|
| 204 | # Open the new space time raster dataset
|
|---|
| 205 | ttype, stype, title, descr = sp.get_initial_values()
|
|---|
| 206 | new_sp = tgis.open_new_stds(output, "strds", ttype, title,
|
|---|
| 207 | descr, stype, dbif, overwrite)
|
|---|
| 208 | num_maps = len(new_maps)
|
|---|
| 209 | # collect empty maps to remove them
|
|---|
| 210 | empty_maps = []
|
|---|
| 211 |
|
|---|
| 212 | # Register the maps in the database
|
|---|
| 213 | count = 0
|
|---|
| 214 | for map in new_maps:
|
|---|
| 215 | count += 1
|
|---|
| 216 |
|
|---|
| 217 | if count%10 == 0:
|
|---|
| 218 | grass.percent(count, num_maps, 1)
|
|---|
| 219 |
|
|---|
| 220 | # Do not register empty maps
|
|---|
| 221 | map.load()
|
|---|
| 222 | if map.metadata.get_min() is None and \
|
|---|
| 223 | map.metadata.get_max() is None:
|
|---|
| 224 | if not register_null:
|
|---|
| 225 | empty_maps.append(map)
|
|---|
| 226 | continue
|
|---|
| 227 |
|
|---|
| 228 | # Insert map in temporal database
|
|---|
| 229 | map.insert(dbif)
|
|---|
| 230 | new_sp.register_map(map, dbif)
|
|---|
| 231 |
|
|---|
| 232 | # Update the spatio-temporal extent and the metadata table entries
|
|---|
| 233 | new_sp.update_from_registered_maps(dbif)
|
|---|
| 234 | grass.percent(1, 1, 1)
|
|---|
| 235 |
|
|---|
| 236 | # Remove empty maps
|
|---|
| 237 | if len(empty_maps) > 0:
|
|---|
| 238 | names = ""
|
|---|
| 239 | count = 0
|
|---|
| 240 | for map in empty_maps:
|
|---|
| 241 | if count == 0:
|
|---|
| 242 | count += 1
|
|---|
| 243 | names += "%s" % (map.get_name())
|
|---|
| 244 | else:
|
|---|
| 245 | names += ",%s" % (map.get_name())
|
|---|
| 246 |
|
|---|
| 247 | grass.run_command("g.remove", flags='f', type='raster', name=names, quiet=True)
|
|---|
| 248 |
|
|---|
| 249 | dbif.close()
|
|---|
| 250 |
|
|---|
| 251 | ############################################################################
|
|---|
| 252 |
|
|---|
| 253 | if __name__ == "__main__":
|
|---|
| 254 | options, flags = grass.parser()
|
|---|
| 255 | main()
|
|---|