aggregation.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. """!@package grass.temporal
  2. @brief GRASS Python scripting module (temporal GIS functions)
  3. Temporal GIS related functions to be used in Python scripts.
  4. Usage:
  5. @code
  6. import grass.temporal as tgis
  7. tgis.aggregate_raster_maps(dataset, mapset, inputs, base, start, end,
  8. count, method, register_null, dbif)
  9. ...
  10. @endcode
  11. (C) 2012-2013 by the GRASS Development Team
  12. This program is free software under the GNU General Public
  13. License (>=v2). Read the file COPYING that comes with GRASS
  14. for details.
  15. @author Soeren Gebbert
  16. """
  17. from space_time_datasets import *
  18. ###############################################################################
  19. def collect_map_names(sp, dbif, start, end, sampling):
  20. """!Gather all maps from dataset using a specific sample method
  21. @param sp The space time raster dataset to select aps from
  22. @param dbif The temporal database interface to use
  23. @param start The start time of the sample interval, may be relative or
  24. absolute
  25. @param end The end time of the sample interval, may be relative or
  26. absolute
  27. @param sampling The sampling methods to use
  28. """
  29. use_start = False
  30. use_during = False
  31. use_overlap = False
  32. use_contain = False
  33. use_equal = False
  34. use_follows = False
  35. use_precedes = False
  36. # Initialize the methods
  37. if sampling:
  38. for name in sampling.split(","):
  39. if name == "start":
  40. use_start = True
  41. if name == "during":
  42. use_during = True
  43. if name == "overlap":
  44. use_overlap = True
  45. if name == "contain":
  46. use_contain = True
  47. if name == "equal":
  48. use_equal = True
  49. if name == "follows":
  50. use_follows = True
  51. if name == "precedes":
  52. use_precedes = True
  53. else:
  54. use_start = True
  55. if sp.get_map_time() != "interval":
  56. use_start = True
  57. use_during = False
  58. use_overlap = False
  59. use_contain = False
  60. use_equal = False
  61. use_follows = False
  62. use_precedes = False
  63. where = create_temporal_relation_sql_where_statement(start, end,
  64. use_start,
  65. use_during,
  66. use_overlap,
  67. use_contain,
  68. use_equal,
  69. use_follows,
  70. use_precedes)
  71. rows = sp.get_registered_maps("id", where, "start_time", dbif)
  72. if not rows:
  73. return None
  74. names = []
  75. for row in rows:
  76. names.append(row["id"])
  77. return names
  78. ###############################################################################
  79. def aggregate_raster_maps(inputs, base, start, end, count, method,
  80. register_null, dbif, offset=0):
  81. """!Aggregate a list of raster input maps with r.series
  82. @param inputs The names of the raster maps to be aggregated
  83. @param base The basename of the new created raster maps
  84. @param start The start time of the sample interval, may be relative or
  85. absolute
  86. @param end The end time of the sample interval, may be relative or
  87. absolute
  88. @param count The number to be attached to the basename of the new
  89. created raster map
  90. @param method The aggreation method to be used by r.series
  91. @param register_null If true null maps will be registered in the space
  92. time raster dataset, if false not
  93. @param dbif The temporal database interface to use
  94. @param offset Offset to be added to the map counter to create the map ids
  95. """
  96. msgr = get_tgis_message_interface()
  97. msgr.verbose(_("Aggregate %s raster maps") % (len(inputs)))
  98. output = "%s_%i" % (base, int(offset) + count)
  99. mapset = get_current_mapset()
  100. map_id = output + "@" + mapset
  101. new_map = RasterDataset(map_id)
  102. # Check if new map is in the temporal database
  103. if new_map.is_in_db(dbif):
  104. if core.overwrite() == True:
  105. # Remove the existing temporal database entry
  106. new_map.delete(dbif)
  107. new_map = RasterDataset(map_id)
  108. else:
  109. msgr.error(_("Raster map <%(name)s> is already in temporal database, " \
  110. "use overwrite flag to overwrite"%({"name":new_map.get_name()})))
  111. return
  112. msgr.verbose(_("Compute aggregation of maps between %(st)s - %(end)s" % {
  113. 'st': str(start), 'end': str(end)}))
  114. # Create the r.series input file
  115. filename = core.tempfile(True)
  116. file = open(filename, 'w')
  117. for name in inputs:
  118. string = "%s\n" % (name)
  119. file.write(string)
  120. file.close()
  121. # Run r.series
  122. if len(inputs) > 1000 :
  123. ret = core.run_command("r.series", flags="z", file=filename,
  124. output=output, overwrite=core.overwrite(),
  125. method=method)
  126. else:
  127. ret = core.run_command("r.series", file=filename,
  128. output=output, overwrite=core.overwrite(),
  129. method=method)
  130. if ret != 0:
  131. dbif.close()
  132. msgr.fatal(_("Error while r.series computation"))
  133. # Read the raster map data
  134. new_map.load()
  135. # In case of a null map continue, do not register null maps
  136. if new_map.metadata.get_min() is None and new_map.metadata.get_max() is None:
  137. if not register_null:
  138. core.run_command("g.remove", rast=output)
  139. return None
  140. return new_map
  141. ##############################################################################
  142. def aggregate_by_topology(granularity_list, granularity, map_list, topo_list, basename, time_suffix,
  143. offset=0, method="average", nprocs=1, spatial=None, dbif=None,
  144. overwrite=False):
  145. """!Aggregate a list of raster input maps with r.series
  146. @param granularity_list A list of AbstractMapDataset objects.
  147. The temporal extents of the objects are used
  148. to build the spatio-temporal topology with the map list objects
  149. @param granularity The granularity of the granularity list
  150. @param map_list A list of RasterDataset objects that contain the raster
  151. maps that should be aggregated
  152. @param topo_list A list of strings of topological relations that are
  153. used to select the raster maps for aggregation
  154. @param basename The basename of the new generated raster maps
  155. @param time_suffix Use the granularity truncated start time of the
  156. actual granule to create the suffix for the basename
  157. @param offset Use a numerical offset for suffix generation (overwritten by time_suffix)
  158. @param method The aggregation method of r.series (average,min,max, ...)
  159. @param nprocs The number of processes used for parallel computation
  160. @param spatial This indicates if the spatial topology is created as well:
  161. spatial can be None (no spatial topology), "2D" using west, east,
  162. south, north or "3D" using west, east, south, north, bottom, top
  163. @param dbif The database interface to be used
  164. @param overwrite Overwrite existing raster maps
  165. @return A list of RasterDataset objects that contain the new map names and
  166. the temporal extent for map registration
  167. """
  168. import grass.script as gcore
  169. import grass.pygrass.modules as pymod
  170. import copy
  171. msgr = get_tgis_message_interface()
  172. dbif, connected = init_dbif(dbif)
  173. topo_builder = SpatioTemporalTopologyBuilder()
  174. topo_builder.build(mapsA=granularity_list, mapsB=map_list, spatial=spatial)
  175. # The module queue for parallel execution
  176. process_queue = pymod.ParallelModuleQueue(int(nprocs))
  177. # Dummy process object that will be deep copied
  178. # and be put into the process queue
  179. r_series = pymod.Module("r.series", output="spam", method=[method],
  180. overwrite=overwrite, quiet=True, run_=False,
  181. finish_=False)
  182. g_copy = pymod.Module("g.copy", rast=['spam', 'spamspam'],
  183. quiet=True, run_=False, finish_=False)
  184. output_list = []
  185. count = 0
  186. for granule in granularity_list:
  187. msgr.percent(count, len(granularity_list), 1)
  188. count += 1
  189. aggregation_list = []
  190. if "equal" in topo_list and granule.equal:
  191. for map_layer in granule.equal:
  192. aggregation_list.append(map_layer.get_name())
  193. if "contains" in topo_list and granule.contains:
  194. for map_layer in granule.contains:
  195. aggregation_list.append(map_layer.get_name())
  196. if "during" in topo_list and granule.during:
  197. for map_layer in granule.during:
  198. aggregation_list.append(map_layer.get_name())
  199. if "starts" in topo_list and granule.starts:
  200. for map_layer in granule.starts:
  201. aggregation_list.append(map_layer.get_name())
  202. if "started" in topo_list and granule.started:
  203. for map_layer in granule.started:
  204. aggregation_list.append(map_layer.get_name())
  205. if "finishes" in topo_list and granule.finishes:
  206. for map_layer in granule.finishes:
  207. aggregation_list.append(map_layer.get_name())
  208. if "finished" in topo_list and granule.finished:
  209. for map_layer in granule.finished:
  210. aggregation_list.append(map_layer.get_name())
  211. if "overlaps" in topo_list and granule.overlaps:
  212. for map_layer in granule.overlaps:
  213. aggregation_list.append(map_layer.get_name())
  214. if "overlapped" in topo_list and granule.overlapped:
  215. for map_layer in granule.overlapped:
  216. aggregation_list.append(map_layer.get_name())
  217. if aggregation_list:
  218. msgr.verbose(_("Aggregate %(len)i raster maps from %(start)s to %(end)s") \
  219. %({"len":len(aggregation_list),
  220. "start":str(granule.temporal_extent.get_start_time()),
  221. "end":str(granule.temporal_extent.get_end_time())}))
  222. if granule.is_time_absolute() is True and time_suffix is True:
  223. suffix = create_suffix_from_datetime(granule.temporal_extent.get_start_time(),
  224. granularity)
  225. else:
  226. suffix = str(count + int(offset))
  227. output_name = "%s_%s"%(basename, suffix)
  228. map_layer = RasterDataset("%s@%s"%(output_name,
  229. get_current_mapset()))
  230. map_layer.set_temporal_extent(granule.get_temporal_extent())
  231. if map_layer.map_exists() is True and overwrite is False:
  232. msgr.fatal(_("Unable to perform aggregation. Output raster map <%(name)s> "\
  233. "exists and overwrite flag is not set"%({"name":output_name})))
  234. output_list.append(map_layer)
  235. if len(aggregation_list) > 1:
  236. # Create the r.series input file
  237. filename = gcore.tempfile(True)
  238. file = open(filename, 'w')
  239. for name in aggregation_list:
  240. string = "%s\n" % (name)
  241. file.write(string)
  242. file.close()
  243. mod = copy.deepcopy(r_series)
  244. mod(file=filename, output=output_name)
  245. if len(aggregation_list) > 1000 :
  246. mod(flags="z")
  247. process_queue.put(mod)
  248. else:
  249. mod = copy.deepcopy(g_copy)
  250. mod(rast=[aggregation_list[0], output_name])
  251. process_queue.put(mod)
  252. if connected:
  253. dbif.close()
  254. msgr.percent(1, 1, 1)
  255. return output_list