stds_export.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. """
  2. Export functions for space time datasets
  3. Usage:
  4. .. code-block:: python
  5. import grass.temporal as tgis
  6. input="temp_1950_2012@PERMANENT"
  7. output="/tmp/temp_1950_2012.tar.gz"
  8. compression="gzip"
  9. directory="/tmp"
  10. where=None
  11. format_="GTiff"
  12. type_="strds"
  13. tgis.export_stds(input, output, compression, directory, where, format_, type_)
  14. (C) 2012-2021 by the GRASS Development Team
  15. This program is free software under the GNU General Public
  16. License (>=v2). Read the file COPYING that comes with GRASS
  17. for details.
  18. :authors: Soeren Gebbert
  19. """
  20. import shutil
  21. import os
  22. import tarfile
  23. import tempfile
  24. import grass.script as gscript
  25. from grass.exceptions import CalledModuleError
  26. from .open_stds import open_old_stds
  27. proj_file_name = "proj.txt"
  28. init_file_name = "init.txt"
  29. metadata_file_name = "metadata.txt"
  30. read_file_name = "readme.txt"
  31. list_file_name = "list.txt"
  32. tmp_tar_file_name = "archive"
  33. # This global variable is for unique vector map export,
  34. # since single vector maps may have several layer
  35. # and therefore several attribute tables
  36. exported_maps = {}
  37. ############################################################################
  38. def _export_raster_maps_as_gdal(
  39. rows, tar, list_file, new_cwd, fs, format_, type_, **kwargs
  40. ):
  41. kwargs = {key: value for key, value in kwargs.items() if value is not None}
  42. for row in rows:
  43. name = row["name"]
  44. start = row["start_time"]
  45. end = row["end_time"]
  46. max_val = row["max"]
  47. min_val = row["min"]
  48. datatype = row["datatype"]
  49. if not end:
  50. end = start
  51. string = "%s%s%s%s%s\n" % (name, fs, start, fs, end)
  52. # Write the filename, the start_time and the end_time
  53. list_file.write(string)
  54. try:
  55. if format_ == "GTiff":
  56. # Export the raster map with r.out.gdal as tif
  57. out_name = name + ".tif"
  58. if datatype == "CELL" and not type_:
  59. nodata = max_val + 1
  60. if nodata < 256 and min_val >= 0:
  61. gdal_type = "Byte"
  62. elif nodata < 65536 and min_val >= 0:
  63. gdal_type = "UInt16"
  64. elif min_val >= 0:
  65. gdal_type = "UInt32"
  66. else:
  67. gdal_type = "Int32"
  68. gscript.run_command(
  69. "r.out.gdal",
  70. flags="c",
  71. input=name,
  72. output=out_name,
  73. nodata=nodata,
  74. type=gdal_type,
  75. format="GTiff",
  76. **kwargs,
  77. )
  78. elif type_:
  79. gscript.run_command(
  80. "r.out.gdal",
  81. flags="cf",
  82. input=name,
  83. output=out_name,
  84. type=type_,
  85. format="GTiff",
  86. **kwargs,
  87. )
  88. else:
  89. gscript.run_command(
  90. "r.out.gdal",
  91. flags="c",
  92. input=name,
  93. output=out_name,
  94. format="GTiff",
  95. **kwargs,
  96. )
  97. elif format_ == "AAIGrid":
  98. # Export the raster map with r.out.gdal as Arc/Info ASCII Grid
  99. out_name = name + ".asc"
  100. gscript.run_command(
  101. "r.out.gdal",
  102. flags="c",
  103. input=name,
  104. output=out_name,
  105. format="AAIGrid",
  106. **kwargs,
  107. )
  108. except CalledModuleError:
  109. shutil.rmtree(new_cwd)
  110. tar.close()
  111. gscript.fatal(_("Unable to export raster map <%s>" % name))
  112. tar.add(out_name)
  113. # Export the color rules
  114. out_name = name + ".color"
  115. try:
  116. gscript.run_command("r.colors.out", map=name, rules=out_name)
  117. except CalledModuleError:
  118. shutil.rmtree(new_cwd)
  119. tar.close()
  120. gscript.fatal(
  121. _(
  122. "Unable to export color rules for raster "
  123. "map <%s> r.out.gdal" % name
  124. )
  125. )
  126. tar.add(out_name)
  127. ############################################################################
  128. def _export_raster_maps(rows, tar, list_file, new_cwd, fs):
  129. for row in rows:
  130. name = row["name"]
  131. start = row["start_time"]
  132. end = row["end_time"]
  133. if not end:
  134. end = start
  135. string = "%s%s%s%s%s\n" % (name, fs, start, fs, end)
  136. # Write the filename, the start_time and the end_time
  137. list_file.write(string)
  138. # Export the raster map with r.pack
  139. try:
  140. gscript.run_command("r.pack", input=name, flags="c")
  141. except CalledModuleError:
  142. shutil.rmtree(new_cwd)
  143. tar.close()
  144. gscript.fatal(_("Unable to export raster map <%s> with r.pack" % name))
  145. tar.add(name + ".pack")
  146. ############################################################################
  147. def _export_vector_maps_as_gml(rows, tar, list_file, new_cwd, fs):
  148. for row in rows:
  149. name = row["name"]
  150. start = row["start_time"]
  151. end = row["end_time"]
  152. layer = row["layer"]
  153. if not layer:
  154. layer = 1
  155. if not end:
  156. end = start
  157. string = "%s%s%s%s%s\n" % (name, fs, start, fs, end)
  158. # Write the filename, the start_time and the end_time
  159. list_file.write(string)
  160. # Export the vector map with v.out.ogr
  161. try:
  162. gscript.run_command(
  163. "v.out.ogr",
  164. input=name,
  165. output=(name + ".xml"),
  166. layer=layer,
  167. format="GML",
  168. )
  169. except CalledModuleError:
  170. shutil.rmtree(new_cwd)
  171. tar.close()
  172. gscript.fatal(
  173. _("Unable to export vector map <%s> as " "GML with v.out.ogr" % name)
  174. )
  175. tar.add(name + ".xml")
  176. tar.add(name + ".xsd")
  177. ############################################################################
  178. def _export_vector_maps_as_gpkg(rows, tar, list_file, new_cwd, fs):
  179. for row in rows:
  180. name = row["name"]
  181. start = row["start_time"]
  182. end = row["end_time"]
  183. layer = row["layer"]
  184. if not layer:
  185. layer = 1
  186. if not end:
  187. end = start
  188. string = "%s%s%s%s%s\n" % (name, fs, start, fs, end)
  189. # Write the filename, the start_time and the end_time
  190. list_file.write(string)
  191. # Export the vector map with v.out.ogr
  192. try:
  193. gscript.run_command(
  194. "v.out.ogr",
  195. input=name,
  196. output=(name + ".gpkg"),
  197. layer=layer,
  198. format="GPKG",
  199. )
  200. except CalledModuleError:
  201. shutil.rmtree(new_cwd)
  202. tar.close()
  203. gscript.fatal(
  204. _("Unable to export vector map <%s> as " "GPKG with v.out.ogr" % name)
  205. )
  206. tar.add(name + ".gpkg")
  207. ############################################################################
  208. def _export_vector_maps(rows, tar, list_file, new_cwd, fs):
  209. for row in rows:
  210. name = row["name"]
  211. start = row["start_time"]
  212. end = row["end_time"]
  213. layer = row["layer"]
  214. # Export unique maps only
  215. if name in exported_maps:
  216. continue
  217. if not layer:
  218. layer = 1
  219. if not end:
  220. end = start
  221. string = "%s:%s%s%s%s%s\n" % (name, layer, fs, start, fs, end)
  222. # Write the filename, the start_time and the end_time
  223. list_file.write(string)
  224. # Export the vector map with v.pack
  225. try:
  226. gscript.run_command("v.pack", input=name, flags="c")
  227. except CalledModuleError:
  228. shutil.rmtree(new_cwd)
  229. tar.close()
  230. gscript.fatal(_("Unable to export vector map <%s> with v.pack" % name))
  231. tar.add(name + ".pack")
  232. exported_maps[name] = name
  233. ############################################################################
  234. def _export_raster3d_maps(rows, tar, list_file, new_cwd, fs):
  235. for row in rows:
  236. name = row["name"]
  237. start = row["start_time"]
  238. end = row["end_time"]
  239. if not end:
  240. end = start
  241. string = "%s%s%s%s%s\n" % (name, fs, start, fs, end)
  242. # Write the filename, the start_time and the end_time
  243. list_file.write(string)
  244. # Export the raster 3d map with r3.pack
  245. try:
  246. gscript.run_command("r3.pack", input=name, flags="c")
  247. except CalledModuleError:
  248. shutil.rmtree(new_cwd)
  249. tar.close()
  250. gscript.fatal(_("Unable to export raster map <%s> with r3.pack" % name))
  251. tar.add(name + ".pack")
  252. ############################################################################
  253. def export_stds(
  254. input,
  255. output,
  256. compression,
  257. directory,
  258. where,
  259. format_="pack",
  260. type_="strds",
  261. datatype=None,
  262. **kwargs,
  263. ):
  264. """Export space time datasets as tar archive with optional compression
  265. This method should be used to export space time datasets
  266. of type raster and vector as tar archive that can be reimported
  267. with the method import_stds().
  268. :param input: The name of the space time dataset to export
  269. :param output: The name of the archive file
  270. :param compression: The compression of the archive file:
  271. - "no" no compression
  272. - "gzip" GNU zip compression
  273. - "bzip2" Bzip compression
  274. :param directory: The working directory used for extraction and packing
  275. :param where: The temporal WHERE SQL statement to select a subset
  276. of maps from the space time dataset
  277. :param format_: The export format:
  278. - "GTiff" Geotiff format, only for raster maps
  279. - "AAIGrid" Arc/Info ASCII Grid format, only for raster maps
  280. - "pack" The GRASS raster, 3D raster or vector Pack format,
  281. this is the default setting
  282. - "GML" GML file export format, only for vector maps,
  283. v.out.ogr export option
  284. - "GPKG" GPKG file export format, only for vector maps,
  285. v.out.ogr export option
  286. :param type_: The space time dataset type
  287. - "strds" Space time raster dataset
  288. - "str3ds" Space time 3D raster dataset
  289. - "stvds" Space time vector dataset
  290. :param datatype: Force the output datatype for r.out.gdal
  291. """
  292. # Save current working directory path
  293. old_cwd = os.getcwd()
  294. # Create the temporary directory and jump into it
  295. new_cwd = tempfile.mkdtemp(dir=directory)
  296. os.chdir(new_cwd)
  297. if type_ == "strds":
  298. columns = "name,start_time,end_time,min,max,datatype"
  299. elif type_ == "stvds":
  300. columns = "name,start_time,end_time,layer"
  301. else:
  302. columns = "name,start_time,end_time"
  303. sp = open_old_stds(input, type_)
  304. rows = sp.get_registered_maps(columns, where, "start_time", None)
  305. if compression == "gzip":
  306. flag = "w:gz"
  307. elif compression == "bzip2":
  308. flag = "w:bz2"
  309. else:
  310. flag = "w:"
  311. # Open the tar archive to add the files
  312. tar = tarfile.open(tmp_tar_file_name, flag)
  313. list_file = open(list_file_name, "w")
  314. fs = "|"
  315. if rows:
  316. if type_ == "strds":
  317. if format_ == "GTiff" or format_ == "AAIGrid":
  318. _export_raster_maps_as_gdal(
  319. rows, tar, list_file, new_cwd, fs, format_, datatype, **kwargs
  320. )
  321. else:
  322. _export_raster_maps(rows, tar, list_file, new_cwd, fs)
  323. elif type_ == "stvds":
  324. if format_ == "GML":
  325. _export_vector_maps_as_gml(rows, tar, list_file, new_cwd, fs)
  326. elif format_ == "GPKG":
  327. _export_vector_maps_as_gpkg(rows, tar, list_file, new_cwd, fs)
  328. else:
  329. _export_vector_maps(rows, tar, list_file, new_cwd, fs)
  330. elif type_ == "str3ds":
  331. _export_raster3d_maps(rows, tar, list_file, new_cwd, fs)
  332. list_file.close()
  333. # Write projection and metadata
  334. proj = gscript.read_command("g.proj", flags="j")
  335. proj_file = open(proj_file_name, "w")
  336. proj_file.write(proj)
  337. proj_file.close()
  338. init_file = open(init_file_name, "w")
  339. # Create the init string
  340. string = ""
  341. # This is optional, if not present strds will be assumed for backward
  342. # compatibility
  343. string += "%s=%s\n" % ("stds_type", sp.get_type())
  344. # This is optional, if not present gtiff will be assumed for
  345. # backward compatibility
  346. string += "%s=%s\n" % ("format", format_)
  347. string += "%s=%s\n" % ("temporal_type", sp.get_temporal_type())
  348. string += "%s=%s\n" % ("semantic_type", sp.get_semantic_type())
  349. if sp.is_time_relative():
  350. string += "%s=%s\n" % ("relative_time_unit", sp.get_relative_time_unit())
  351. # replace sp.metadata.get_number_of_maps() with len(rows)
  352. # sp.metadata.get_number_of_maps() doesn't work with where option
  353. string += "%s=%s\n" % ("number_of_maps", len(rows))
  354. north, south, east, west, top, bottom = sp.get_spatial_extent_as_tuple()
  355. string += "%s=%s\n" % ("north", north)
  356. string += "%s=%s\n" % ("south", south)
  357. string += "%s=%s\n" % ("east", east)
  358. string += "%s=%s\n" % ("west", west)
  359. init_file.write(string)
  360. init_file.close()
  361. metadata = gscript.read_command("t.info", type=type_, input=sp.get_id())
  362. metadata_file = open(metadata_file_name, "w")
  363. metadata_file.write(metadata)
  364. metadata_file.close()
  365. read_file = open(read_file_name, "w")
  366. if type_ == "strds":
  367. read_file.write(
  368. "This space time raster dataset was exported with "
  369. "t.rast.export of GRASS GIS 7\n"
  370. )
  371. elif type_ == "stvds":
  372. read_file.write(
  373. "This space time vector dataset was exported with "
  374. "t.vect.export of GRASS GIS 7\n"
  375. )
  376. elif type_ == "str3ds":
  377. read_file.write(
  378. "This space time 3D raster dataset was exported "
  379. "with t.rast3d.export of GRASS GIS 7\n"
  380. )
  381. read_file.write("\n")
  382. read_file.write("Files:\n")
  383. if type_ == "strds":
  384. if format_ == "GTiff":
  385. # 123456789012345678901234567890
  386. read_file.write(" *.tif -- GeoTIFF raster files\n")
  387. read_file.write(" *.color -- GRASS GIS raster color rules\n")
  388. elif format_ == "pack":
  389. read_file.write(" *.pack -- GRASS raster files packed with r.pack\n")
  390. elif type_ == "stvds":
  391. # 123456789012345678901234567890
  392. if format_ == "GML":
  393. read_file.write(" *.xml -- Vector GML files\n")
  394. else:
  395. read_file.write(" *.pack -- GRASS vector files packed with v.pack\n")
  396. elif type_ == "str3ds":
  397. read_file.write(" *.pack -- GRASS 3D raster files packed with r3.pack\n")
  398. read_file.write(
  399. "%13s -- Projection information in PROJ.4 format\n" % (proj_file_name)
  400. )
  401. read_file.write(
  402. "%13s -- GRASS GIS space time %s dataset information\n"
  403. % (init_file_name, sp.get_new_map_instance(None).get_type())
  404. )
  405. read_file.write(
  406. "%13s -- Time series file, lists all maps by name "
  407. "with interval\n" % (list_file_name)
  408. )
  409. read_file.write(
  410. " time stamps in ISO-Format. Field separator is |\n"
  411. )
  412. read_file.write("%13s -- The output of t.info\n" % (metadata_file_name))
  413. read_file.write("%13s -- This file\n" % (read_file_name))
  414. read_file.close()
  415. # Append the file list
  416. tar.add(list_file_name)
  417. tar.add(proj_file_name)
  418. tar.add(init_file_name)
  419. tar.add(read_file_name)
  420. tar.add(metadata_file_name)
  421. tar.close()
  422. os.chdir(old_cwd)
  423. # Move the archive to its destination
  424. shutil.move(os.path.join(new_cwd, tmp_tar_file_name), output)
  425. # Remove the temporary created working directory
  426. shutil.rmtree(new_cwd)