extract.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. """
  2. Extract functions for space time raster, 3d raster and vector datasets
  3. (C) 2012-2013 by the GRASS Development Team
  4. This program is free software under the GNU General Public
  5. License (>=v2). Read the file COPYING that comes with GRASS
  6. for details.
  7. :authors: Soeren Gebbert
  8. """
  9. from .core import (
  10. get_tgis_message_interface,
  11. get_current_mapset,
  12. SQLDatabaseInterfaceConnection,
  13. )
  14. from .abstract_map_dataset import AbstractMapDataset
  15. from .open_stds import open_old_stds, check_new_stds, open_new_stds
  16. from .datetime_math import create_suffix_from_datetime
  17. from .datetime_math import create_time_suffix
  18. from .datetime_math import create_numeric_suffix
  19. from multiprocessing import Process
  20. import grass.script as gscript
  21. from grass.exceptions import CalledModuleError
  22. ############################################################################
  23. def extract_dataset(
  24. input,
  25. output,
  26. type,
  27. where,
  28. expression,
  29. base,
  30. time_suffix,
  31. nprocs=1,
  32. register_null=False,
  33. layer=1,
  34. vtype="point,line,boundary,centroid,area,face",
  35. ):
  36. """Extract a subset of a space time raster, raster3d or vector dataset
  37. A mapcalc expression can be provided to process the temporal extracted
  38. maps.
  39. Mapcalc expressions are supported for raster and raster3d maps.
  40. :param input: The name of the input space time raster/raster3d dataset
  41. :param output: The name of the extracted new space time raster/raster3d
  42. dataset
  43. :param type: The type of the dataset: "raster", "raster3d" or vector
  44. :param where: The temporal SQL WHERE statement for subset extraction
  45. :param expression: The r(3).mapcalc expression or the v.extract where
  46. statement
  47. :param base: The base name of the new created maps in case a mapclac
  48. expression is provided
  49. :param time_suffix: string to choose which suffix to use: gran, time, num%*
  50. (where * are digits)
  51. :param nprocs: The number of parallel processes to be used for mapcalc
  52. processing
  53. :param register_null: Set this number True to register empty maps
  54. (only raster and raster3d maps)
  55. :param layer: The vector layer number to be used when no timestamped
  56. layer is present, default is 1
  57. :param vtype: The feature type to be extracted for vector maps, default
  58. is point,line,boundary,centroid,area and face
  59. """
  60. # Check the parameters
  61. msgr = get_tgis_message_interface()
  62. if expression and not base:
  63. msgr.fatal(_("You need to specify the base name of new created maps"))
  64. mapset = get_current_mapset()
  65. dbif = SQLDatabaseInterfaceConnection()
  66. dbif.connect()
  67. sp = open_old_stds(input, type, dbif)
  68. # Check the new stds
  69. new_sp = check_new_stds(output, type, dbif, gscript.overwrite())
  70. if type == "vector":
  71. rows = sp.get_registered_maps("id,name,mapset,layer", where, "start_time", dbif)
  72. else:
  73. rows = sp.get_registered_maps("id", where, "start_time", dbif)
  74. new_maps = {}
  75. if rows:
  76. num_rows = len(rows)
  77. msgr.percent(0, num_rows, 1)
  78. # Run the mapcalc expression
  79. if expression:
  80. count = 0
  81. proc_count = 0
  82. proc_list = []
  83. for row in rows:
  84. count += 1
  85. if count % 10 == 0:
  86. msgr.percent(count, num_rows, 1)
  87. if sp.get_temporal_type() == "absolute" and time_suffix == "gran":
  88. old_map = sp.get_new_map_instance(row["id"])
  89. old_map.select(dbif)
  90. suffix = create_suffix_from_datetime(
  91. old_map.temporal_extent.get_start_time(), sp.get_granularity()
  92. )
  93. map_name = "{ba}_{su}".format(ba=base, su=suffix)
  94. elif sp.get_temporal_type() == "absolute" and time_suffix == "time":
  95. old_map = sp.get_new_map_instance(row["id"])
  96. old_map.select(dbif)
  97. suffix = create_time_suffix(old_map)
  98. map_name = "{ba}_{su}".format(ba=base, su=suffix)
  99. else:
  100. map_name = create_numeric_suffix(base, count, time_suffix)
  101. # We need to modify the r(3).mapcalc expression
  102. if type != "vector":
  103. expr = expression
  104. expr = expr.replace(sp.base.get_map_id(), row["id"])
  105. expr = expr.replace(sp.base.get_name(), row["id"])
  106. expr = "%s = %s" % (map_name, expr)
  107. # We need to build the id
  108. map_id = AbstractMapDataset.build_id(map_name, mapset)
  109. else:
  110. map_id = AbstractMapDataset.build_id(map_name, mapset, row["layer"])
  111. new_map = sp.get_new_map_instance(map_id)
  112. # Check if new map is in the temporal database
  113. if new_map.is_in_db(dbif):
  114. if gscript.overwrite():
  115. # Remove the existing temporal database entry
  116. new_map.delete(dbif)
  117. new_map = sp.get_new_map_instance(map_id)
  118. else:
  119. msgr.error(
  120. _(
  121. "Map <%s> is already in temporal database"
  122. ", use overwrite flag to overwrite"
  123. )
  124. % (new_map.get_map_id())
  125. )
  126. continue
  127. # Add process to the process list
  128. if type == "raster":
  129. msgr.verbose(_('Applying r.mapcalc expression: "%s"') % expr)
  130. proc_list.append(Process(target=run_mapcalc2d, args=(expr,)))
  131. elif type == "raster3d":
  132. msgr.verbose(_('Applying r3.mapcalc expression: "%s"') % expr)
  133. proc_list.append(Process(target=run_mapcalc3d, args=(expr,)))
  134. elif type == "vector":
  135. msgr.verbose(
  136. _('Applying v.extract where statement: "%s"') % expression
  137. )
  138. if row["layer"]:
  139. proc_list.append(
  140. Process(
  141. target=run_vector_extraction,
  142. args=(
  143. row["name"] + "@" + row["mapset"],
  144. map_name,
  145. row["layer"],
  146. vtype,
  147. expression,
  148. ),
  149. )
  150. )
  151. else:
  152. proc_list.append(
  153. Process(
  154. target=run_vector_extraction,
  155. args=(
  156. row["name"] + "@" + row["mapset"],
  157. map_name,
  158. layer,
  159. vtype,
  160. expression,
  161. ),
  162. )
  163. )
  164. proc_list[proc_count].start()
  165. proc_count += 1
  166. # Join processes if the maximum number of processes are
  167. # reached or the end of the loop is reached
  168. if proc_count == nprocs or count == num_rows:
  169. proc_count = 0
  170. exitcodes = 0
  171. for proc in proc_list:
  172. proc.join()
  173. exitcodes += proc.exitcode
  174. if exitcodes != 0:
  175. dbif.close()
  176. msgr.fatal(_("Error in computation process"))
  177. # Empty process list
  178. proc_list = []
  179. # Store the new maps
  180. new_maps[row["id"]] = new_map
  181. msgr.percent(0, num_rows, 1)
  182. temporal_type, semantic_type, title, description = sp.get_initial_values()
  183. new_sp = open_new_stds(
  184. output,
  185. type,
  186. sp.get_temporal_type(),
  187. title,
  188. description,
  189. semantic_type,
  190. dbif,
  191. gscript.overwrite(),
  192. )
  193. # collect empty maps to remove them
  194. empty_maps = []
  195. # Register the maps in the database
  196. count = 0
  197. for row in rows:
  198. count += 1
  199. if count % 10 == 0:
  200. msgr.percent(count, num_rows, 1)
  201. old_map = sp.get_new_map_instance(row["id"])
  202. old_map.select(dbif)
  203. if expression:
  204. # Register the new maps
  205. if row["id"] in new_maps:
  206. new_map = new_maps[row["id"]]
  207. # Read the raster map data
  208. new_map.load()
  209. # In case of a empty map continue, do not register empty
  210. # maps
  211. if type == "raster" or type == "raster3d":
  212. if (
  213. new_map.metadata.get_min() is None
  214. and new_map.metadata.get_max() is None
  215. ):
  216. if not register_null:
  217. empty_maps.append(new_map)
  218. continue
  219. elif type == "vector":
  220. if (
  221. new_map.metadata.get_number_of_primitives() == 0
  222. or new_map.metadata.get_number_of_primitives() is None
  223. ):
  224. if not register_null:
  225. empty_maps.append(new_map)
  226. continue
  227. # Set the time stamp
  228. new_map.set_temporal_extent(old_map.get_temporal_extent())
  229. if type == "raster":
  230. # Set the semantic label
  231. semantic_label = old_map.metadata.get_semantic_label()
  232. if semantic_label is not None:
  233. new_map.set_semantic_label(semantic_label)
  234. # Insert map in temporal database
  235. new_map.insert(dbif)
  236. new_sp.register_map(new_map, dbif)
  237. else:
  238. new_sp.register_map(old_map, dbif)
  239. # Update the spatio-temporal extent and the metadata table entries
  240. new_sp.update_from_registered_maps(dbif)
  241. msgr.percent(num_rows, num_rows, 1)
  242. # Remove empty maps
  243. if len(empty_maps) > 0:
  244. names = ""
  245. count = 0
  246. for map in empty_maps:
  247. if count == 0:
  248. names += "%s" % (map.get_name())
  249. else:
  250. names += ",%s" % (map.get_name())
  251. count += 1
  252. if type == "raster":
  253. gscript.run_command(
  254. "g.remove", flags="f", type="raster", name=names, quiet=True
  255. )
  256. elif type == "raster3d":
  257. gscript.run_command(
  258. "g.remove", flags="f", type="raster_3d", name=names, quiet=True
  259. )
  260. elif type == "vector":
  261. gscript.run_command(
  262. "g.remove", flags="f", type="vector", name=names, quiet=True
  263. )
  264. dbif.close()
  265. ###############################################################################
  266. def run_mapcalc2d(expr):
  267. """Helper function to run r.mapcalc in parallel"""
  268. try:
  269. gscript.run_command(
  270. "r.mapcalc", expression=expr, overwrite=gscript.overwrite(), quiet=True
  271. )
  272. except CalledModuleError:
  273. exit(1)
  274. def run_mapcalc3d(expr):
  275. """Helper function to run r3.mapcalc in parallel"""
  276. try:
  277. gscript.run_command(
  278. "r3.mapcalc", expression=expr, overwrite=gscript.overwrite(), quiet=True
  279. )
  280. except CalledModuleError:
  281. exit(1)
  282. def run_vector_extraction(input, output, layer, type, where):
  283. """Helper function to run r.mapcalc in parallel"""
  284. try:
  285. gscript.run_command(
  286. "v.extract",
  287. input=input,
  288. output=output,
  289. layer=layer,
  290. type=type,
  291. where=where,
  292. overwrite=gscript.overwrite(),
  293. quiet=True,
  294. )
  295. except CalledModuleError:
  296. exit(1)