|
| 1 | +--- |
| 2 | +title: "Time series subset, import and export" |
| 3 | +author: "Veronica Andreo" |
| 4 | +date: 2024-08-19 |
| 5 | +date-modified: today |
| 6 | +image: images/export_with_change.png |
| 7 | +lightbox: true |
| 8 | +format: |
| 9 | + ipynb: default |
| 10 | + html: |
| 11 | + toc: true |
| 12 | + code-tools: true |
| 13 | + code-copy: true |
| 14 | + code-fold: false |
| 15 | +categories: [time series, raster, intermediate, Python] |
| 16 | +description: Tutorial on time series subsetting, import and export. |
| 17 | +engine: jupyter |
| 18 | +execute: |
| 19 | + eval: false |
| 20 | +jupyter: python3 |
| 21 | +--- |
| 22 | + |
| 23 | +In this seventh time series tutorial, we will go through time |
| 24 | +series subset, import and export. |
| 25 | + |
| 26 | +::: {.callout-note title="Setup"} |
| 27 | +This tutorial can be run locally or in Google Colab. However, make sure you |
| 28 | +install GRASS 8.4+, download the |
| 29 | +[LST sample dataset](https://zenodo.org/doi/10.5281/zenodo.3564514) |
| 30 | +and set up your project as explained in the |
| 31 | +[first](time_series_management_and_visualization.qmd) time |
| 32 | +series tutorial. |
| 33 | +::: |
| 34 | + |
| 35 | +```{python} |
| 36 | +#| echo: false |
| 37 | +
|
| 38 | +import os |
| 39 | +import sys |
| 40 | +import subprocess |
| 41 | +
|
| 42 | +# Ask GRASS where its Python packages are |
| 43 | +sys.path.append( |
| 44 | + subprocess.check_output(["grass", "--config", "python_path"], text=True).strip() |
| 45 | +) |
| 46 | +# Import the GRASS packages we need |
| 47 | +import grass.script as gs |
| 48 | +import grass.jupyter as gj |
| 49 | +
|
| 50 | +path_to_project = "italy_eu_laea/italy_LST_daily" |
| 51 | +
|
| 52 | +# Start the GRASS Session |
| 53 | +session = gj.init(path_to_project) |
| 54 | +``` |
| 55 | + |
| 56 | +## Subset |
| 57 | + |
| 58 | +For extracting a subset from a STRDS, we use |
| 59 | +[t.rast.extract](https://grass.osgeo.org/grass-stable/manuals/t.rast.extract.html). |
| 60 | +The subset is based on temporal variables like `start_time`, `start_doy`, |
| 61 | +`end_week`, etc. |
| 62 | +This tool outputs a new STRDS and offers the possibility to apply a |
| 63 | +mapcalc operation on the fly. If no *r.mapcalc* expression is defined, |
| 64 | +the selected maps are simply registered in the newly created output STRDS |
| 65 | +to avoid data duplication. |
| 66 | + |
| 67 | +Let's see a couple of examples. Suppose we are only interested in northern summer |
| 68 | +months, i.e., June, July, August. The maps matching the where condition will be |
| 69 | +registered in the new output STRDS. |
| 70 | + |
| 71 | +```{python} |
| 72 | +gs.run_command("t.rast.extract", |
| 73 | + input="lst_daily", |
| 74 | + output="lst_daily_summer", |
| 75 | + where="strftime('%m',start_time)='06' or strftime('%m',start_time)='07' or strftime('%m', start_time)='08'") |
| 76 | +``` |
| 77 | + |
| 78 | +If you remember, we went through several listing examples in the |
| 79 | +[Time series management and visualization](time_series_management_and_visualization.qmd) tutorial. |
| 80 | +To check the subset will actually give us what we want, we can run |
| 81 | +[t.rast.list](https://grass.osgeo.org/grass-stable/manuals/t.rast.list.html) |
| 82 | +with the same `where` condition as above. |
| 83 | + |
| 84 | +```{python} |
| 85 | +#| scrolled: true |
| 86 | +gs.run_command("t.rast.list", |
| 87 | + input="lst_daily_summer", |
| 88 | + columns="name,min,max") |
| 89 | +``` |
| 90 | + |
| 91 | +To check whether a map is registered in different time series objects, we can use |
| 92 | +[t.info](https://grass.osgeo.org/grass-stable/manuals/t.info.html). |
| 93 | + |
| 94 | +```{python} |
| 95 | +gs.run_command("t.info", |
| 96 | + input="lst_2018.243_avg", |
| 97 | + type="raster") |
| 98 | +``` |
| 99 | + |
| 100 | +Let's see now an example including a mapcalc operation. We still want the daily |
| 101 | +maps of summer, but we are only interested in areas where LST was higher than |
| 102 | +25 degrees. |
| 103 | +Note that in this second case, we are creating a new STRDS, i.e., we modify the |
| 104 | +original with the mapcalc expression, so we need to provide `basename` and `suffix` |
| 105 | +for the newly created maps. |
| 106 | + |
| 107 | +```{python} |
| 108 | +gs.run_command("t.rast.extract", |
| 109 | + input="lst_daily", |
| 110 | + output="lst_daily_summer_higher_25", |
| 111 | + basename="lst_daily", |
| 112 | + suffix="gran", |
| 113 | + where="strftime('%m',start_time)='06' or strftime('%m',start_time)='07' or strftime('%m', start_time)='08'", |
| 114 | + expression="if(lst_daily < 25.0, null(), lst_daily)") |
| 115 | +``` |
| 116 | + |
| 117 | +Check that the minimum value of all extracted maps are actually above 25 degrees. |
| 118 | + |
| 119 | +```{python} |
| 120 | +#| scrolled: true |
| 121 | +gs.run_command("t.rast.list", |
| 122 | + input="lst_daily_summer_higher_25", |
| 123 | + columns="name,min,max") |
| 124 | +``` |
| 125 | + |
| 126 | +What tools would you use now if you wanted to know how many summer days had |
| 127 | +temperatures above 25 degrees each year and how does the maximum number of days |
| 128 | +with LST > 25 varies regionally? |
| 129 | + |
| 130 | +**Small hint?** Go back to the time series |
| 131 | +[aggregation](time_series_aggregations.qmd) tutorial. |
| 132 | + |
| 133 | +```{python} |
| 134 | +# Get number of summer days with LST > 25 per year |
| 135 | +gs.run_command("t.rast.aggregate", |
| 136 | + input="lst_daily_summer_higher_25", |
| 137 | + output="count_lst_daily_summer_higher_25", |
| 138 | + basename="count_lst_daily_summer_higher_25", |
| 139 | + suffix="gran", |
| 140 | + method="count", |
| 141 | + granularity="1 year") |
| 142 | +``` |
| 143 | + |
| 144 | +```{python} |
| 145 | +# Get maximum number of days with LST > 25 |
| 146 | +gs.run_command("t.rast.series", |
| 147 | + input="count_lst_daily_summer_higher_25", |
| 148 | + output="max_count_summer_days_lst_higher_25", |
| 149 | + method="maximum") |
| 150 | +``` |
| 151 | + |
| 152 | +```{python} |
| 153 | +# Mask zero values |
| 154 | +gs.run_command("r.mask", |
| 155 | + raster="max_count_summer_days_lst_higher_25", |
| 156 | + maskcats=0, |
| 157 | + flags="i") |
| 158 | +``` |
| 159 | + |
| 160 | +```{python} |
| 161 | +# Visualize the result |
| 162 | +max_count = gj.InteractiveMap(tiles="CartoDB.DarkMatter") |
| 163 | +max_count.add_raster("max_count_summer_days_lst_higher_25") |
| 164 | +max_count.show() |
| 165 | +``` |
| 166 | + |
| 167 | +## Export |
| 168 | + |
| 169 | +There are three tools to export raster time series in different formats. |
| 170 | + |
| 171 | +* [t.rast.export](https://grass.osgeo.org/grass-stable/manuals/t.rast.export.html) exports a strds as a tar archive containing raster maps either as GeoTIFF or GRASS binary files and several metadata files such as: timestamps, CRS info, strds and raster info. The archive can be compressed with gzip or bzip2. The output of *t.rast.export* can then be imported with [t.rast.import](https://grass.osgeo.org/grass-stable/manuals/t.rast.import.html). |
| 172 | +* [t.rast.out.vtk](https://grass.osgeo.org/grass-stable/manuals/t.rast.out.vtk.html) exports a strds as a VTK file to be visualized with any VTK visualizer. |
| 173 | +* [t.rast.out.xyz](https://grass.osgeo.org/grass-stable/manuals/addons/t.rast.out.xyz.html) exports a strds to a CSV file of the form x coord, y coord, value. |
| 174 | + |
| 175 | +Let's see some examples. |
| 176 | + |
| 177 | +```{python} |
| 178 | +# Export strds as an archive |
| 179 | +gs.run_command("t.rast.export", |
| 180 | + input="lst_daily_summer", |
| 181 | + output="lst_daily_summer_2014.tar.bzip2", |
| 182 | + where="start_time < '2015-01-01'") |
| 183 | +``` |
| 184 | + |
| 185 | +```{python} |
| 186 | +!tar tjf lst_daily_summer_2014.tar.bzip2 |
| 187 | +``` |
| 188 | + |
| 189 | +``` |
| 190 | +lst_2014.152_avg.tif |
| 191 | +lst_2014.152_avg.color |
| 192 | +... |
| 193 | +lst_2014.243_avg.tif |
| 194 | +lst_2014.243_avg.color |
| 195 | +list.txt |
| 196 | +proj.txt |
| 197 | +init.txt |
| 198 | +readme.txt |
| 199 | +metadata.txt |
| 200 | +``` |
| 201 | + |
| 202 | +```{python} |
| 203 | +# Export strds as VTK |
| 204 | +gs.run_command("t.rast.out.vtk", |
| 205 | + input="count_lst_daily_summer_higher_25", |
| 206 | + directory="/tmp", |
| 207 | + elevation="elevation") |
| 208 | +``` |
| 209 | + |
| 210 | +The tool *t.rast.out.xyz* is an addon, so we first need to install it with *g.extension*. |
| 211 | + |
| 212 | +```{python} |
| 213 | +# Install extension |
| 214 | +gs.run_command("g.extension", extension="t.rast.out.xyz") |
| 215 | +``` |
| 216 | + |
| 217 | +```{python} |
| 218 | +# Export strds as xyz CSV file |
| 219 | +gs.run_command("t.rast.out.xyz", |
| 220 | + strds="count_lst_daily_summer_higher_25", |
| 221 | + output="count_lst_daily_summer_higher_25.csv") |
| 222 | +``` |
| 223 | + |
| 224 | +```{python} |
| 225 | +!head count_lst_daily_summer_higher_25.csv |
| 226 | +``` |
| 227 | + |
| 228 | +## Import |
| 229 | + |
| 230 | +There are two tools to import raster time series into GRASS: |
| 231 | + |
| 232 | +* [t.rast.import](https://grass.osgeo.org/grass-stable/manuals/t.rast.import.html) imports strds that were exported with *t.rast.export*. It allows to create a new GRASS project with the imported data CRS and set the computational region to the raster maps imported. |
| 233 | +* [t.rast.import.netcdf](https://grass.osgeo.org/grass-stable/manuals/addons/t.rast.import.netcdf.html) imports the content of one or more netCDF files that adhere to the CF convention into a strds. Data can be imported or linked via [r.external](https://grass.osgeo.org/grass-stable/manuals/r.external.html). |
| 234 | + |
| 235 | +Let's see an example with *t.rast.import*. |
| 236 | + |
| 237 | +```{python} |
| 238 | +# Import the exported strds into a new GRASS project |
| 239 | +gs.run_command("t.rast.import", |
| 240 | + input="lst_daily_summer_2014.tar.bzip2", |
| 241 | + output="lst_daily_summer_new", |
| 242 | + title="Daily summer LST", |
| 243 | + description="Daily summer LST for 2014") |
| 244 | +``` |
| 245 | + |
| 246 | +```{python} |
| 247 | +# Check the new strds was created |
| 248 | +gs.run_command("t.list", where="NAME LIKE '%summer%'") |
| 249 | +``` |
| 250 | + |
| 251 | +:::{.callout-note title="What about vector time series?"} |
| 252 | +While not covered in this tutorial, there are also dedicated tools for subsetting, importing and exporting vector time series objects. These are: |
| 253 | + |
| 254 | +* [t.vect.extract](https://grass.osgeo.org/grass-stable/manuals/t.vect.extract.html) |
| 255 | +* [t.vect.export](https://grass.osgeo.org/grass-stable/manuals/t.vect.export.html) |
| 256 | +* [t.vect.import](https://grass.osgeo.org/grass-stable/manuals/t.vect.import.html) |
| 257 | +::: |
| 258 | + |
| 259 | + |
| 260 | +## References |
| 261 | + |
| 262 | +- Gebbert, S., Pebesma, E. 2014. |
| 263 | +_TGRASS: A temporal GIS for field based environmental modeling._ |
| 264 | +Environmental Modelling & Software 53, 1-12. |
| 265 | +[DOI](http://dx.doi.org/10.1016/j.envsoft.2013.11.001). |
| 266 | +- Gebbert, S., Pebesma, E. 2017. _The GRASS GIS temporal framework._ |
| 267 | +International Journal of Geographical Information Science 31, 1273-1292. |
| 268 | +[DOI](http://dx.doi.org/10.1080/13658816.2017.1306862). |
| 269 | +- [Temporal data processing](https://grasswiki.osgeo.org/wiki/Temporal_data_processing) wiki page. |
| 270 | + |
| 271 | + |
| 272 | +*** |
| 273 | + |
| 274 | +:::{.smaller} |
| 275 | +The development of this tutorial was funded by the US |
| 276 | +[National Science Foundation (NSF)](https://www.nsf.gov/), |
| 277 | +award [2303651](https://www.nsf.gov/awardsearch/showAward?AWD_ID=2303651). |
| 278 | +::: |
| 279 | + |
| 280 | + |
0 commit comments