1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3############################################################################
4#
5# MODULE:       t.rast.aggregate
6# AUTHOR(S):    Soeren Gebbert
7#
8# PURPOSE:      Temporally aggregates the maps of a space time raster dataset by a user defined granularity.
9# COPYRIGHT:    (C) 2011-2017 by the GRASS Development Team
10#
11#  This program is free software; you can redistribute it and/or modify
12#  it under the terms of the GNU General Public License as published by
13#  the Free Software Foundation; either version 2 of the License, or
14#  (at your option) any later version.
15#
16#  This program is distributed in the hope that it will be useful,
17#  but WITHOUT ANY WARRANTY; without even the implied warranty of
18#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19#  GNU General Public License for more details.
20#
21#############################################################################
22
23#%module
24#% description: Aggregates temporally the maps of a space time raster dataset by a user defined granularity.
25#% keyword: temporal
26#% keyword: aggregation
27#% keyword: raster
28#% keyword: time
29#%end
30
31#%option G_OPT_STRDS_INPUT
32#%end
33
34#%option G_OPT_STRDS_OUTPUT
35#%end
36
37#%option
38#% key: basename
39#% type: string
40#% label: Basename of the new generated output maps
41#% description: Either a numerical suffix or the start time (s-flag) separated by an underscore will be attached to create a unique identifier
42#% required: yes
43#% multiple: no
44#% gisprompt:
45#%end
46
47#%option
48#% key: suffix
49#% type: string
50#% 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)
51#% answer: gran
52#% required: no
53#% multiple: no
54#%end
55
56#%option
57#% key: granularity
58#% type: string
59#% description: Aggregation granularity, format absolute time "x years, x months, x weeks, x days, x hours, x minutes, x seconds" or an integer value for relative time
60#% required: yes
61#% multiple: no
62#%end
63
64#%option
65#% key: method
66#% type: string
67#% description: Aggregate operation to be performed on the raster maps
68#% required: yes
69#% multiple: no
70#% options: average,count,median,mode,minimum,min_raster,maximum,max_raster,stddev,range,sum,variance,diversity,slope,offset,detcoeff,quart1,quart3,perc90,quantile,skewness,kurtosis
71#% answer: average
72#%end
73
74#%option
75#% key: offset
76#% type: integer
77#% description: Offset that is used to create the output map ids, output map id is generated as: basename_ (count + offset)
78#% required: no
79#% multiple: no
80#% answer: 0
81#%end
82
83#%option
84#% key: nprocs
85#% type: integer
86#% description: Number of r.series processes to run in parallel
87#% required: no
88#% multiple: no
89#% answer: 1
90#%end
91
92#%option
93#% key: file_limit
94#% type: integer
95#% description: The maximum number of open files allowed for each r.series process
96#% required: no
97#% multiple: no
98#% answer: 1000
99#%end
100
101#%option G_OPT_T_SAMPLE
102#% options: equal,overlaps,overlapped,starts,started,finishes,finished,during,contains
103#% answer: contains
104#%end
105
106#%option G_OPT_T_WHERE
107#%end
108
109#%flag
110#% key: n
111#% description: Register Null maps
112#%end
113
114import grass.script as gcore
115
116
117############################################################################
118
119def main():
120    # lazy imports
121    import grass.temporal as tgis
122
123    # Get the options
124    input = options["input"]
125    output = options["output"]
126    where = options["where"]
127    gran = options["granularity"]
128    base = options["basename"]
129    register_null = flags["n"]
130    method = options["method"]
131    sampling = options["sampling"]
132    offset = options["offset"]
133    nprocs = options["nprocs"]
134    file_limit = options["file_limit"]
135    time_suffix = options["suffix"]
136
137    topo_list = sampling.split(",")
138
139    tgis.init()
140
141    dbif = tgis.SQLDatabaseInterfaceConnection()
142    dbif.connect()
143
144    sp = tgis.open_old_stds(input, "strds", dbif)
145
146    map_list = sp.get_registered_maps_as_objects(where=where, order="start_time", dbif=dbif)
147
148    if not map_list:
149        dbif.close()
150        gcore.fatal(_("Space time raster dataset <%s> is empty") % input)
151
152    # We will create the strds later, but need to check here
153    tgis.check_new_stds(output, "strds",   dbif,  gcore.overwrite())
154
155    start_time = map_list[0].temporal_extent.get_start_time()
156
157    if sp.is_time_absolute():
158        start_time = tgis.adjust_datetime_to_granularity(start_time,  gran)
159
160    # We use the end time first
161    end_time = map_list[-1].temporal_extent.get_end_time()
162    has_end_time = True
163
164    # In case no end time is available, then we use the start time of the last map layer
165    if end_time is None:
166        end_time = map_list[- 1].temporal_extent.get_start_time()
167        has_end_time = False
168
169    granularity_list = []
170
171    # Build the granularity list
172    while True:
173        if has_end_time is True:
174            if start_time >= end_time:
175                break
176        else:
177            if start_time > end_time:
178                break
179
180        granule = tgis.RasterDataset(None)
181        start = start_time
182        if sp.is_time_absolute():
183            end = tgis.increment_datetime_by_string(start_time, gran)
184            granule.set_absolute_time(start, end)
185        else:
186            end = start_time + int(gran)
187            granule.set_relative_time(start, end,  sp.get_relative_time_unit())
188        start_time = end
189
190        granularity_list.append(granule)
191
192    output_list = tgis.aggregate_by_topology(granularity_list=granularity_list,  granularity=gran,
193                                                                       map_list=map_list,
194                                                                       topo_list=topo_list,  basename=base, time_suffix=time_suffix,
195                                                                       offset=offset,  method=method,  nprocs=nprocs,  spatial=None,
196                                                                       overwrite=gcore.overwrite(), file_limit=file_limit)
197
198    if output_list:
199        temporal_type, semantic_type, title, description = sp.get_initial_values()
200        output_strds = tgis.open_new_stds(output, "strds", temporal_type,
201                                                                 title, description, semantic_type,
202                                                                 dbif, gcore.overwrite())
203        if register_null:
204            register_null=False
205        else:
206            register_null=True
207
208        tgis.register_map_object_list("rast", output_list,  output_strds, register_null,
209                                                       sp.get_relative_time_unit(),  dbif)
210
211        # Update the raster metadata table entries with aggregation type
212        output_strds.set_aggregation_type(method)
213        output_strds.metadata.update(dbif)
214
215    dbif.close()
216
217if __name__ == "__main__":
218    options, flags = gcore.parser()
219    main()
220