t.rast.aggregate.ds(1grass)

πŸ’– t.rast.aggregate.ds πŸ’–

okay bestie, welcome to t.rast.aggregate.ds β€” the GRASS User's Manual page for it, and it's lowkey one of the most useful temporal tools in the whole toolbox. it aggregates the data of an existing space time raster dataset using the time intervals of a second space time dataset. yes, that's a whole vibe. let's get into it 🫢

✨ NAME ✨

t.rast.aggregate.ds β€” Aggregates data of an existing space time raster dataset using the time intervals of a second space time dataset.

🏷️ KEYWORDS 🏷️

temporal, aggregation, raster, time

🌈 SYNOPSIS 🌈

t.rast.aggregate.ds
t.rast.aggregate.ds --help
t.rast.aggregate.ds [-n] input=name sample=name  [type=name]
output=name basename=string  [suffix=name]  method=string
[offset=integer]   [nprocs=integer]   [sampling=name[,name,...]]
[where=sql_query]   [--overwrite]  [--help]  [--verbose]  [--quiet]
[--ui]

🚩 Flags 🚩

  • -n β€” Register Null maps. it's giving "even the empty ones deserve a spot", and honestly, correct.
  • --overwrite β€” Allow output files to overwrite existing files. rude but necessary energy πŸ’…
  • --help β€” Print usage summary.
  • --verbose β€” Verbose module output. not me wanting every single detail, and getting it.
  • --quiet β€” Quiet module output. the introvert flag fr fr 🀫
  • --ui β€” Force launching GUI dialog.

πŸŽ€ Parameters πŸŽ€

  • input=name [required] β€” Name of the input space time raster dataset. this is the main character ✨
  • sample=name [required] β€” Time intervals from this space time dataset (raster, vector or raster3d) are used for aggregation computation. so it's the sample dataset, and it can be any of the three flavors, not just raster, periodt.
  • type=name β€” Type of the space time dataset from which aggregation will be copied. Options: strds, stvds, str3ds Default: strds
  • output=name [required] β€” Name of the output space time raster dataset.
  • basename=string [required] β€” Basename of the new generated output maps. A numerical suffix separated by an underscore will be attached to create a unique identifier. so like, it'll do the naming for you, don't stress 🧚
  • suffix=name β€” 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). Default: gran
  • method=string [required] β€” Aggregate operation to be performed on the raster maps. 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 Default: average
  • offset=integer β€” Offset that is used to create the output map ids, output map id is generated as: basename_ (count + offset). Default: 0
  • nprocs=integer β€” Number of r.mapcalc processes to run in parallel. Default: 1
  • sampling=name[,name,...] β€” The method to be used for sampling the input dataset. Options: equal, overlaps, overlapped, starts, started, finishes, finished, during, contains Default: contains
  • where=sql_query β€” WHERE conditions of SQL statement without 'where' keyword used in the temporal GIS framework. Example: start_time > '2001-01-01 12:30:00'

πŸ’œ DESCRIPTION πŸ’œ

So here's the tea, bestie: t.rast.aggregate.ds works exactly like t.rast.aggregate β€” same energy, same slay β€” but instead of you defining a fixed granularity for the temporal aggregation, it takes the time intervals of all the maps registered in a second space time dataset (which can be an STRDS, STR3DS or STVDS) and uses those to aggregate the maps of the input space time raster dataset. it's the "copy their homework but change it slightly" of temporal aggregation, except it's fully legit and iconic πŸ’…

πŸ“ NOTES πŸ“

Okay so this part matters, don't skip it:

The sampling method must be specified from the sampler dataset point of view. It defines the temporal relationships between intervals of the sampling dataset and the input space time raster dataset. iykyk β€” perspective is everything here, so make sure you're thinking from the sample's side, not the input's 🌸

🎬 EXAMPLES 🎬

🌧️ Precipitation aggregation 🌧️

In this example we create 7 raster maps that will be registered in a single space time raster dataset named precipitation_daily using a daily temporal granularity. The names of the raster maps are stored in a text file that is used for raster map registration.

A space time vector dataset is created out of two vector maps with different temporal resolution. The maps are created using v.random. The first map has a granule of 3 days, the second a granule of 4 days.

(more of this iconic saga coming up in the next part, don't go anywhere πŸ‘€)

so like, plot twist: the space time raster dataset precipitation_daily has daily temporal granularity, and we're gonna aggregate it using a space time vector dataset, and the glow-up result is the output space time raster dataset precipitation_agg πŸ’–

The aggregation method is set to sum so we can stack up all the precipitation values from every interval in the space time vector dataset. And then sampling=contains? Bestie that option is the whole point β€” it makes sure only raster maps that fall temporally during the time intervals of the space time vector dataset get considered for the computation. So yeah, it's set to contains, which here means time stamped vector map layers temporally contain the raster map layers ✨

First we gotta make the rasters and register them:

MAPS="map_1 map_2 map_3 map_4 map_5 map_6 map_7"
for map in ${MAPS} ; do
    r.mapcalc expression="${map} = 1"
    echo ${map} >> map_list.txt
done
t.create type=strds temporaltype=absolute \
         output=precipitation_daily \
         title="Daily precipitation" \
         description="Test dataset with daily precipitation"
t.register -i type=raster input=precipitation_daily \
           file=map_list.txt start="2012-08-20" increment="1 days"
t.info type=strds input=precipitation_daily

and t.info gives us this absolute masterpiece of a table:

 +-------------------- Space Time Raster Dataset -----------------------------+
 |                                                                            |
 +-------------------- Basic information -------------------------------------+
 | Id: ........................ precipitation_daily@PERMANENT
 | Name: ...................... precipitation_daily
 | Mapset: .................... PERMANENT
 | Creator: ................... soeren
 | Temporal type: ............. absolute
 | Creation time: ............. 2014-11-23 16:48:17.686979
 | Modification time:.......... 2014-11-23 16:48:18.302978
 | Semantic type:.............. mean
 +-------------------- Absolute time -----------------------------------------+
 | Start time:................. 2012-09-10 00:00:00
 | End time:................... 2012-09-17 00:00:00
 | Granularity:................ 1 day
 | Temporal type of maps:...... interval
 +-------------------- Spatial extent ----------------------------------------+
 | North:...................... 80.0
 | South:...................... 0.0
 | East:.. .................... 120.0
 | West:....................... 0.0
 | Top:........................ 0.0
 | Bottom:..................... 0.0
 +-------------------- Metadata information ----------------------------------+
 | Raster register table:...... raster_map_register_3225725979b14b5db343a00835b882c7
 | North-South resolution min:. 10.0
 | North-South resolution max:. 10.0
 | East-west resolution min:... 10.0
 | East-west resolution max:... 10.0
 | Minimum value min:.......... 1.0
 | Minimum value max:.......... 1.0
 | Maximum value min:.......... 1.0
 | Maximum value max:.......... 1.0
 | Aggregation type:........... None
 | Number of registered maps:.. 7
 |
 | Title:
 | Daily precipitation
 | Description:
 | Test dataset with daily precipitation
 | Command history:
 | # 2014-11-23 16:48:17
 | t.create type="strds" temporaltype="absolute"
 |     output="precipitation_daily" title="Daily precipitation"
 |     description="Test dataset with daily precipitation"
 | # 2014-11-23 16:48:18
 | t.register -i type="rast" input="precipitation_daily"
 |     file="map_list.txt" start="2012-08-20" increment="1 days"
 |
 +----------------------------------------------------------------------------+

Seven maps, all value 1.0, no cap πŸ”₯


okay now the vector side of the story β€” we're making random points and putting those on a timeline too:

v.random output=points_1 n=20
v.random output=points_2 n=20
t.create type=stvds temporaltype=absolute \
         output=points \
         title="Points" \
         description="Points for aggregation"
t.register -i type=vector input=points \
           map=points_1 start="2012-08-20" increment="3 days"
t.register -i type=vector input=points \
           map=points_2 start="2012-08-23" increment="4 days"
t.info type=stvds input=points

and t.info spills the tea again:

 +-------------------- Space Time Vector Dataset -----------------------------+
 |                                                                            |
 +-------------------- Basic information -------------------------------------+
 | Id: ........................ points@PERMANENT
 | Name: ...................... points
 | Mapset: .................... PERMANENT
 | Creator: ................... soeren
 | Temporal type: ............. absolute
 | Creation time: ............. 2014-11-23 16:48:49.193903
 | Modification time:.......... 2014-11-23 16:48:50.185671
 | Semantic type:.............. mean
 +-------------------- Absolute time -----------------------------------------+
 | Start time:................. 2012-08-20 00:00:00
 | End time:................... 2012-08-27 00:00:00
 | Granularity:................ 1 day
 | Temporal type of maps:...... interval
 +-------------------- Spatial extent ----------------------------------------+
 | North:...................... 79.283411
 | South:...................... 5.724954
 | East:.. .................... 118.881168
 | West:....................... 0.016755
 | Top:........................ 0.0
 | Bottom:..................... 0.0
 +-------------------- Metadata information ----------------------------------+
 | Vector register table:...... vector_map_register_6f02d33e0ee243d1a521aaaca39ecb31
 | Number of points ........... 40
 | Number of lines ............ 0
 | Number of boundaries ....... 0
 | Number of centroids ........ 0
 | Number of faces ............ 0
 | Number of kernels .......... 0
 | Number of primitives ....... 40
 | Number of nodes ............ 0
 | Number of areas ............ 0
 | Number of islands .......... 0
 | Number of holes ............ 0
 | Number of volumes .......... 0
 | Number of registered maps:.. 2
 |
 | Title:
 | Points
 | Description:
 | Points for aggregation
 | Command history:
 | # 2014-11-23 16:48:49
 | t.create type="stvds" temporaltype="absolute"
 |     output="points" title="Points" description="Points for aggregation"
 | # 2014-11-23 16:48:49
 | t.register -i type="vect" input="points"
 |     map="points_1" start="2012-08-20" increment="3 days"
 | # 2014-11-23 16:48:50
 | t.register -i type="vect" input="points"
 |     map="points_2" start="2012-08-23" increment="4 days"
 |
 +----------------------------------------------------------------------------+

40 points, 2 registered maps, Number of lines ............ 0 β€” she's a points-only queen, we love that πŸŽ€


and now the main character moment. drumroll please πŸ₯ the actual aggregation:

t.rast.aggregate.ds input=precipitation_daily \
                    output=precipitation_agg \
                    sample=points type=stvds \
                    basename=prec_agg \
                    method=sum sampling=contains
t.support input=precipitation_agg \
          title="Aggregated precipitation" \
          description="Aggregated precipitation dataset"
t.info type=strds input=precipitation_agg

and the receipts fr fr:

 +-------------------- Space Time Raster Dataset -----------------------------+
 |                                                                            |
 +-------------------- Basic information -------------------------------------+
 | Id: ........................ precipitation_agg@PERMANENT
 | Name: ...................... precipitation_agg
 | Mapset: .................... PERMANENT
 | Creator: ................... soeren
 | Temporal type: ............. absolute
 | Creation time: ............. 2014-11-23 16:53:23.488799
 | Modification time:.......... 2014-11-23 16:53:28.714886
 | Semantic type:.............. mean
 +-------------------- Absolute time -----------------------------------------+
 | Start time:................. 2012-08-20 00:00:00
 | End time:................... 2012-08-27 00:00:00
 | Granularity:................ 1 day
 | Temporal type of maps:...... interval
 +-------------------- Spatial extent ----------------------------------------+
 | North:...................... 80.0
 | South:...................... 0.0
 | East:.. .................... 120.0
 | West:....................... 0.0
 | Top:........................ 0.0
 | Bottom:..................... 0.0
 +-------------------- Metadata information ----------------------------------+
 | Raster register table:...... raster_map_register_7b025eb7431747c98c5c1ad971e8c282
 | North-South resolution min:. 10.0
 | North-South resolution max:. 10.0
 | East-west resolution min:... 10.0
 | East-west resolution max:... 10.0
 | Minimum value min:.......... 3.0
 | Minimum value max:.......... 4.0
 | Maximum value min:.......... 3.0
 | Maximum value max:.......... 4.0
 | Aggregation type:........... sum
 | Number of registered maps:.. 2
 |
 | Title:
 | Aggregated precipitation
 | Description:
 | Aggregated precipitation dataset
 | Command history:
 | # 2014-11-23 16:53:23
 | t.rast.aggregate.ds input="precipitation_daily"
 |     output="precipitation_agg" sample="points" type="stvds" basename="prec_agg"
 |     method="sum" sampling="contains"
 | # 2014-11-23 16:53:28
 | t.support input="precipitation_agg"
 |     title="Aggregated precipitation"
 |     description="Aggregated precipitation dataset"
 |
 +----------------------------------------------------------------------------+

LOOK at those values bestie 😭 Minimum value min:.......... 3.0, Minimum value max:.......... 4.0, Maximum value min:.......... 3.0, Maximum value max:.......... 4.0, and Aggregation type:........... sum. She came out with exactly 2 registered maps and a sum aggregation and she's thriving. iconic, no notes πŸ’…βœ¨

🌈 MODIS satellite sensor daily data aggregation to 8 days 🌈

okay bestie, this one is the classic: daily data ➑️ eight-day data. This whole "eight-day week" thing is literally what some MODIS satellite sensor products use, so if you've ever wondered how they smash 8 days into one granule, this is it. πŸ›°οΈβœ¨

heads up, the example is written in shell language, so buckle up for a little bash moment πŸ’…

First we make a bunch of seed maps, one per 8-day step, for two whole years:

# NOTE: the example is written in shell language
# create maps every 8 days as seed maps
for year in `seq 2000 2001` ; do
   for doy in `seq -w 1 8 365` ; do
      r.mapcalc expression="8day_${year}_${doy} = rand(0.0,40.0)"
   done
done

Then we take the year and the day-of-year out of each map name, turn that into a real YYYY-MM-DD start date and end date, and dump it all into a file with the map names. Parsing era πŸŽ€:

# From de name of each map, we take year and doy, and convert it
# to a YYYY-MM-DD date for start and end, and create a file with
# mapnames, start date and end date
g.list type=raster pattern=8day_20??_* > names_list
for NAME in `cat names_list` ; do
   # Parse
   YEAR=`echo $NAME | cut -d’_’ -f2`
   DOY=`echo $NAME | cut -d’_’ -f3`
   # convert YYYY_DOY to YYYY-MM-DD
   DOY=`echo "$DOY" | sed ’s/^0*//’`
   doy_end=0
   if [ $DOY -le "353" ] ; then
      doy_end=$(( $DOY + 8 ))
   elif [ $DOY -eq "361" ] ; then
      if [ $[$YEAR % 4] -eq 0 ] && [ $[$YEAR % 100] -ne 0 ] || [ $[$YEAR % 400] -eq 0 ] ; then
         doy_end=$(( $DOY + 6 ))
      else
            doy_end=$(( $DOY + 5 ))
      fi
   fi
   DATE_START=`date -d "${YEAR}-01-01 +$(( ${DOY} - 1 ))days" +%Y-%m-%d`
   DATE_END=`date -d "${YEAR}-01-01 +$(( ${doy_end} -1 ))days" +%Y-%m-%d`
   # text file with mapnames, start date and end date
   echo "$NAME|$DATE_START|$DATE_END" >> list_map_start_end_time.txt
done
# check the list created.
cat list_map_start_end_time.txt

and the list it spits out looks like this πŸ‘‡ (note the leap-year shenanigans):

8day_2000_001|2000-01-01|2000-01-09
8day_2000_009|2000-01-09|2000-01-17
...
8day_2000_353|2000-12-18|2000-12-26
8day_2000_361|2000-12-26|2001-01-01
8day_2001_001|2001-01-01|2001-01-09
8day_2001_009|2001-01-09|2001-01-17
...
8day_2001_345|2001-12-11|2001-12-19
8day_2001_353|2001-12-19|2001-12-27
8day_2001_361|2001-12-27|2002-01-01

important vibe check incoming 🚨: every map except the last one in each year covers a real 8-day interval. But the aggregation restarts from scratch every January 1st, so that final map of the year is a lil short king. πŸ‘‘

# all maps except for the last map in each year represent 8-days
# intervals. But the aggregation starts all over again every
# January 1st.

Now we build an 8-day MODIS-like STRDS. slay:

# create 8-day MODIS-like strds
t.create type=strds temporaltype=absolute \
   output=8day_ts title="8 day time series" \
   description="STRDS with MODIS like 8 day aggregation"

register the maps into it:

# register maps
t.register type=raster input=8day_ts \
   file=list_map_start_end_time.txt

and check that everything landed where it should:

# check
t.info input=8day_ts
t.rast.list input=8day_ts

okay and now the main character moment β€” we copy that aggregation over to a daily time series. ✨

# finally, copy the aggregation to a daily time series
t.rast.aggregate.ds input=daily_ts sample=8day_ts \
   output=8day_agg basename=8day_agg method=average \
   sampling=contains suffix=gran

gotta give it some metadata love too, our dataset deserves lore:

# add metadata
t.support input=8day_agg \
   title="8 day aggregated ts" \
   description="8 day MODIS-like aggregated dataset"

and finally, the map list check inside the brand new aggregated STRDS:

# check map list in newly created aggregated strds
t.rast.list input=8day_agg

which gives you this, no cap:

name|mapset|start_time|end_time
8day_agg_2000_01_01|modis|2000-01-01 00:00:00|2000-01-09 00:00:00
8day_agg_2000_01_09|modis|2000-01-09 00:00:00|2000-01-17 00:00:00
8day_agg_2000_01_17|modis|2000-01-17 00:00:00|2000-01-25 00:00:00
...
8day_agg_2000_12_18|modis|2000-12-18 00:00:00|2000-12-26 00:00:00
8day_agg_2000_12_26|modis|2000-12-26 00:00:00|2001-01-01 00:00:00
8day_agg_2001_01_01|modis|2001-01-01 00:00:00|2001-01-09 00:00:00
...
8day_agg_2001_12_11|modis|2001-12-11 00:00:00|2001-12-19 00:00:00
8day_agg_2001_12_19|modis|2001-12-19 00:00:00|2001-12-27 00:00:00
8day_agg_2001_12_27|modis|2001-12-27 00:00:00|2002-01-01 00:00:00

look at those timestamps line up. iconic. πŸ’…βœ¨


πŸ’– SEE ALSO πŸ’–

go read these next, bestie, they're the whole friend group:

  • t.rast.aggregate
  • t.create
  • t.info

🌸 AUTHOR 🌸

SΓΆren Gebbert, ThΓΌnen Institute of Climate-Smart Agriculture


πŸ¦‹ SOURCE CODE πŸ¦‹

Available at: t.rast.aggregate.ds source code (history)

Latest change: Tuesday Jan 27 12:40:13 2026 in commit:

5e8b13d47dd4fc81efa5abefe90c34f7a8291fb9

Main index | Temporal index | Topics index | Keywords index | Graphical index | Full index

Β© 2003-2026 GRASS Development Team, GRASS 8.5.0 Reference Manual


and that's the whole page, bestie, you read the entire thing 🫢✨ shoutout to GRASS 8.5.0 for the reference manual and to that Tuesday Jan 27 12:40:13 2026 commit for keeping it fresh β€” t.rast.aggregate.ds(1grass), signing off, mwah πŸ’‹πŸŽ€

πŸ“¦ where this page comes from