-
Notifications
You must be signed in to change notification settings - Fork 1
/
t.rast.reclass.py
executable file
·343 lines (292 loc) · 10 KB
/
t.rast.reclass.py
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
#!/usr/bin/env python3
############################################################################
#
# MODULE: t.rast.reclass
# AUTHOR(S): Stefan Blumentrath
#
# PURPOSE: Reclassify maps in a SpaceTimeRasterDataset
# COPYRIGHT: (C) 2022 by the Stefan Blumentrath and
# the GRASS GIS Development Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#############################################################################
# %module
# % description: Reclassify maps in a SpaceTimeRasterDataset.
# % keyword: temporal
# % keyword: reclassification
# % keyword: raster
# % keyword: time
# %end
# %option G_OPT_STRDS_INPUT
# %end
# %option G_OPT_STRDS_OUTPUT
# %end
# %option
# % key: rules
# % type: string
# % required: yes
# % multiple: no
# % key_desc: name
# % label: File containing reclass rules
# % description: '-' for standard input
# % gisprompt: old,file,file
# %end
# %option
# % key: semantic_label
# % type: string
# % required: no
# % multiple: no
# % description: Semantic label to be assigned to the reclassified map - also used as a suffix that is appended to input map name
# %end
# %option
# % key: temporaltype
# % type: string
# % required: no
# % multiple: no
# % options: absolute,relative
# % key_desc: name
# % description: The temporal type of the space time dataset
# %end
# %option
# % key: semantictype
# % type: string
# % required: no
# % multiple: no
# % options: min,max,sum,mean
# % description: Semantic type of the space time dataset
# %end
# %option
# % key: title
# % type: string
# % required: no
# % multiple: no
# % description: Title of the new space time dataset
# %end
# %option
# % key: description
# % type: string
# % required: no
# % multiple: no
# % description: Description of the new space time dataset
# %end
# %option G_OPT_M_NPROCS
# % key: nprocs
# % type: integer
# % description: Number of r.reclass processes to run in parallel
# % required: no
# % multiple: no
# % answer: 1
# %end
# %option G_OPT_T_WHERE
# %end
# %flag
# % key: n
# % description: Register Null maps
# %end
# %flag
# % key: e
# % description: Extend existing space time raster dataset
# %end
# %rules
# % required: -e, title
# % excludes: -e, title, description
# % collective: title, description
# %end
# ToDo:
# - add semantic label support to register_map_object_list
# https://grass.osgeo.org/grass83/manuals/libpython/_modules/temporal/register.html#register_maps_in_space_time_dataset
import sys
from copy import deepcopy
from multiprocessing import Pool
from pathlib import Path
import grass.script as gs
from grass.pygrass.modules import Module
############################################################################
def run_reclassification(
reclass_module, input_tuple, register_null, semantic_label, current_mapset
):
"""Run the pygrass modules with input and return RasterDataset
with reclassified map
:param reclass_module: A PyGRASS Module object with a pre-configured
r.reclass module
:param input_tuple: A tuple containg the full map name, start-time and
end-time of the map
:param register_null: Boolean if maps with only NULL should be registered
in the output STRDS (default: True)
:param semantic_label: Semantic label to assign to the output raster maps
:param current_mapset: Name of the current mapset
:return: A string for registering the reclassified map in the output
SpaceTimeRasterDataset or None
"""
output_name = f"{input_tuple[0].split('@')[0]}_{semantic_label or 'rc'}"
if gs.find_file(name=output_name, mapset=".") and gs.overwrite() is False:
gs.fatal(
_(
"Unable to perform reclassification. Output raster "
"map <{name}> exists and overwrite flag was "
"not set".format(name=output_name)
)
)
reclass_module.inputs.input = input_tuple[0]
reclass_module.outputs.output = output_name
reclass_module.run()
# In case of a null map continue, remove it and return None to register
if not register_null:
# Compute statistics
gs.run_command("r.support", flags="s", map=output_name)
new_map_info = gs.raster_info(output_name)
if new_map_info["min"] is None and new_map_info["max"] is None:
gs.run_command("g.remove", flags="f", type="raster", name=output_name)
return None
return (
f"{output_name}|{input_tuple[1]}|{input_tuple[2]}|{semantic_label}"
if semantic_label
else f"{output_name}|{input_tuple[1]}|{input_tuple[2]}"
)
def reclass_temporal_map(
map_list,
reclass_module,
register_null,
semantic_label,
nprocs=1,
overwrite=False,
):
"""Reclassify a list of raster input maps with r.reclass
This is mainly a wrapper to parallelize the run_reclassification function
:param map_list: A list of RasterDataset objects that contain the raster
maps that should be reclassified
:param reclass_module: A PyGRASS Module object with a pre-configured
r.reclass module
:param register_null: Boolean if maps with only NULL should be registered
in the output STRDS (default: True)
:param semantic_label: Semantic label to assign to the output raster maps
:param nprocs: The number of processes used for parallel computation
:param overwrite: Overwrite existing raster maps
:return: A list of strings for registering reclassified maps in the output
SpaceTimeRasterDataset
"""
current_mapset = gs.gisenv()["MAPSET"]
with Pool(min(nprocs, len(map_list))) as p:
output_list = p.starmap(
run_reclassification,
[
(
deepcopy(reclass_module),
(
raster_map.get_id(),
*raster_map.get_temporal_extent_as_tuple(),
),
register_null,
semantic_label,
current_mapset,
)
for raster_map in map_list
],
)
return output_list
def main():
# Get the options
input = options["input"]
output = options["output"]
where = options["where"]
# base = options["basename"]
register_null = flags["n"]
nprocs = int(options["nprocs"])
# Initialize TGIS
tgis.init()
# Connect to TGIS DB
dbif = tgis.SQLDatabaseInterfaceConnection()
dbif.connect()
# Open input STRDS
sp = tgis.open_old_stds(input, "strds", dbif)
map_list = sp.get_registered_maps_as_objects(
where=where, order="start_time", dbif=dbif
)
if not map_list:
dbif.close()
gs.fatal(_("Space time raster dataset <{}> is empty".format(input)))
# We will create the strds later, but need to check here
tgis.check_new_stds(output, "strds", dbif, gs.overwrite())
# Create Module object that will be deep copied
# and be put into the process queue
reclass_module = Module(
"r.reclass",
overwrite=gs.overwrite,
quiet=True,
run_=False,
)
# Get reclassification rules
reclass_rules = options["rules"]
if reclass_rules == "-":
reclass_module.inputs.rules = "-"
reclass_str = str(sys.__stdin__.read())
if not reclass_str:
gs.fatal(_("Empty or no reclass rules provided"))
elif "=" not in reclass_str:
gs.fatal(_("Invalid reclass rules provided"))
reclass_module.inputs["stdin"].value = reclass_str
elif not Path(reclass_rules).exists():
gs.fatal(_("Invalid reclass rules provided"))
else:
reclass_module.inputs.rules = reclass_rules
# Run reclassification
output_list = reclass_temporal_map(
map_list,
reclass_module,
register_null,
options["semantic_label"],
nprocs=nprocs,
overwrite=gs.overwrite(),
)
# Register produced maps
if output_list:
# Create new or overwrite existing
output_strds = tgis.factory.dataset_factory(
"strds", f"{output}@{gs.gisenv()['MAPSET']}"
)
if not output_strds.is_in_db(dbif) or (gs.overwrite() and not flags["e"]):
# Get basic metadata
temporal_type, semantic_type, title, description = sp.get_initial_values()
# Create new STRDS
output_strds = tgis.open_new_stds(
output,
"strds",
options["temporaltype"] or temporal_type,
options["title"] or title,
options["description"] or description,
options["semantictype"] or semantic_type,
dbif,
gs.overwrite(),
)
# Append to existing
elif output_strds.is_in_db(dbif) and flags["e"]:
output_strds = tgis.open_old_stds(output, "strds", dbif)
# Register reclassified maps
register_file_path = gs.tempfile(create=False)
with open(register_file_path, "w") as register_file:
register_file.write("\n".join([ol for ol in output_list if ol]))
tgis.register_maps_in_space_time_dataset(
"raster",
output,
file=register_file_path,
dbif=dbif,
)
# Update the raster metadata table entries with aggregation type
output_strds.update_from_registered_maps(dbif=dbif)
else:
gs.warning(_("No output maps to register"))
dbif.close()
if __name__ == "__main__":
options, flags = gs.parser()
# lazy imports
import grass.temporal as tgis
main()