tgis_space_time_datasets.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. """!@package grass.script.tgis_space_time_dataset
  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. from grass.script import tgis_space_time_dataset as grass
  7. strds = grass.space_time_raster_dataset("soils_1950_2010")
  8. ...
  9. @endcode
  10. (C) 2008-2011 by the GRASS Development Team
  11. This program is free software under the GNU General Public
  12. License (>=v2). Read the file COPYING that comes with GRASS
  13. for details.
  14. @author Soeren Gebbert
  15. """
  16. import getpass
  17. import raster
  18. import vector
  19. import raster3d
  20. from tgis_abstract_datasets import *
  21. ###############################################################################
  22. class raster_dataset(abstract_map_dataset):
  23. """Raster dataset class
  24. This class provides functions to select, update, insert or delete raster
  25. map informations and valid time stamps into the SQL temporal database.
  26. """
  27. def __init__(self, ident):
  28. self.reset(ident)
  29. def get_type(self):
  30. return "raster"
  31. def get_new_instance(self, ident):
  32. """Return a new instance with the type of this class"""
  33. return raster_dataset(ident)
  34. def get_new_stds_instance(self, ident):
  35. """Return a new space time dataset instance in which maps are stored with the type of this class"""
  36. return space_time_raster_dataset(ident)
  37. def get_stds_register(self):
  38. """Return the space time dataset register table name in which stds are listed in which this map is registered"""
  39. return self.metadata.get_strds_register()
  40. def set_stds_register(self, name):
  41. """Set the space time dataset register table name in which stds are listed in which this map is registered"""
  42. self.metadata.set_strds_register(name)
  43. def reset(self, ident):
  44. """Reset the internal structure and set the identifier"""
  45. self.ident = ident
  46. self.base = raster_base(ident=ident)
  47. self.absolute_time = raster_absolute_time(ident=ident)
  48. self.relative_time = raster_relative_time(ident=ident)
  49. self.spatial_extent = raster_spatial_extent(ident=ident)
  50. self.metadata = raster_metadata(ident=ident)
  51. def load(self):
  52. """Load all info from an existing raster map into the internal structure"""
  53. # Get the data from an existing raster map
  54. kvp = raster.raster_info(self.ident)
  55. # Fill base information
  56. self.base.set_name(self.ident.split("@")[0])
  57. self.base.set_mapset(self.ident.split("@")[1])
  58. self.base.set_creator(str(getpass.getuser()))
  59. # Fill spatial extent
  60. self.set_spatial_extent(north=kvp["north"], south=kvp["south"], \
  61. east=kvp["east"], west=kvp["west"])
  62. # Fill metadata
  63. self.metadata.set_nsres(kvp["nsres"])
  64. self.metadata.set_ewres(kvp["ewres"])
  65. self.metadata.set_datatype(kvp["datatype"])
  66. self.metadata.set_min(kvp["min"])
  67. self.metadata.set_max(kvp["max"])
  68. rows = int((kvp["north"] - kvp["south"])/kvp["nsres"] + 0.5)
  69. cols = int((kvp["east"] - kvp["west"])/kvp["ewres"] + 0.5)
  70. ncells = cols * rows
  71. self.metadata.set_cols(cols)
  72. self.metadata.set_rows(rows)
  73. self.metadata.set_number_of_cells(ncells)
  74. ###############################################################################
  75. class raster3d_dataset(abstract_map_dataset):
  76. """Raster3d dataset class
  77. This class provides functions to select, update, insert or delete raster3d
  78. map informations and valid time stamps into the SQL temporal database.
  79. """
  80. def __init__(self, ident):
  81. self.reset(ident)
  82. def get_type(self):
  83. return "raster3d"
  84. def get_new_instance(self, ident):
  85. """Return a new instance with the type of this class"""
  86. return raster3d_dataset(ident)
  87. def get_new_stds_instance(self, ident):
  88. """Return a new space time dataset instance in which maps are stored with the type of this class"""
  89. return space_time_raster3d_dataset(ident)
  90. def get_stds_register(self):
  91. """Return the space time dataset register table name in which stds are listed in which this map is registered"""
  92. return self.metadata.get_str3ds_register()
  93. def set_stds_register(self, name):
  94. """Set the space time dataset register table name in which stds are listed in which this map is registered"""
  95. self.metadata.set_str3ds_register(name)
  96. def reset(self, ident):
  97. """Reset the internal structure and set the identifier"""
  98. self.ident = ident
  99. self.base = raster3d_base(ident=ident)
  100. self.absolute_time = raster3d_absolute_time(ident=ident)
  101. self.relative_time = raster3d_relative_time(ident=ident)
  102. self.spatial_extent = raster3d_spatial_extent(ident=ident)
  103. self.metadata = raster3d_metadata(ident=ident)
  104. def load(self):
  105. """Load all info from an existing raster3d map into the internal structure"""
  106. # Get the data from an existing raster map
  107. kvp = raster3d.raster3d_info(self.ident)
  108. # Fill base information
  109. self.base.set_name(self.ident.split("@")[0])
  110. self.base.set_mapset(self.ident.split("@")[1])
  111. self.base.set_creator(str(getpass.getuser()))
  112. # Fill spatial extent
  113. self.set_spatial_extent(north=kvp["north"], south=kvp["south"], \
  114. east=kvp["east"], west=kvp["west"],\
  115. top=kvp["top"], bottom=kvp["bottom"])
  116. # Fill metadata
  117. self.metadata.set_nsres(kvp["nsres"])
  118. self.metadata.set_ewres(kvp["ewres"])
  119. self.metadata.set_tbres(kvp["tbres"])
  120. self.metadata.set_datatype(kvp["datatype"])
  121. self.metadata.set_min(kvp["min"])
  122. self.metadata.set_max(kvp["max"])
  123. rows = int((kvp["north"] - kvp["south"])/kvp["nsres"] + 0.5)
  124. cols = int((kvp["east"] - kvp["west"])/kvp["ewres"] + 0.5)
  125. depths = int((kvp["top"] - kvp["bottom"])/kvp["tbres"] + 0.5)
  126. ncells = cols * rows * depths
  127. self.metadata.set_cols(cols)
  128. self.metadata.set_rows(rows)
  129. self.metadata.set_depths(depths)
  130. self.metadata.set_number_of_cells(ncells)
  131. ###############################################################################
  132. class vector_dataset(abstract_map_dataset):
  133. """Vector dataset class
  134. This class provides functions to select, update, insert or delete vector
  135. map informations and valid time stamps into the SQL temporal database.
  136. """
  137. def __init__(self, ident):
  138. self.reset(ident)
  139. def get_type(self):
  140. return "vector"
  141. def get_new_instance(self, ident):
  142. """Return a new instance with the type of this class"""
  143. return vector_dataset(ident)
  144. def get_new_stds_instance(self, ident):
  145. """Return a new space time dataset instance in which maps are stored with the type of this class"""
  146. return space_time_vector_dataset(ident)
  147. def get_stds_register(self):
  148. """Return the space time dataset register table name in which stds are listed in which this map is registered"""
  149. return self.metadata.get_stvds_register()
  150. def set_stds_register(self, name):
  151. """Set the space time dataset register table name in which stds are listed in which this map is registered"""
  152. self.metadata.set_stvds_register(name)
  153. def reset(self, ident):
  154. """Reset the internal structure and set the identifier"""
  155. self.ident = ident
  156. self.base = vector_base(ident=ident)
  157. self.absolute_time = vector_absolute_time(ident=ident)
  158. self.relative_time = vector_relative_time(ident=ident)
  159. self.spatial_extent = vector_spatial_extent(ident=ident)
  160. self.metadata = vector_metadata(ident=ident)
  161. def load(self):
  162. """Load all info from an existing vector map into the internal structure"""
  163. # Get the data from an existing raster map
  164. kvp = vector.vector_info(self.ident)
  165. # Fill base information
  166. self.base.set_name(self.ident.split("@")[0])
  167. self.base.set_mapset(self.ident.split("@")[1])
  168. self.base.set_creator(str(getpass.getuser()))
  169. # Fill spatial extent
  170. self.set_spatial_extent(north=kvp["north"], south=kvp["south"], \
  171. east=kvp["east"], west=kvp["west"],\
  172. top=kvp["top"], bottom=kvp["bottom"])
  173. # Fill metadata .. no metadata yet
  174. ###############################################################################
  175. class space_time_raster_dataset(abstract_space_time_dataset):
  176. """Space time raster dataset class
  177. """
  178. def __init__(self, ident):
  179. abstract_space_time_dataset.__init__(self, ident)
  180. def get_type(self):
  181. return "strds"
  182. def get_new_instance(self, ident):
  183. """Return a new instance with the type of this class"""
  184. return space_time_raster_dataset(ident)
  185. def get_new_map_instance(self, ident):
  186. """Return a new instance of a map dataset which is associated with the type of this class"""
  187. return raster_dataset(ident)
  188. def get_map_register(self):
  189. """Return the name of the map register table"""
  190. return self.metadata.get_raster_register()
  191. def set_map_register(self, name):
  192. """Set the name of the map register table"""
  193. self.metadata.set_raster_register(name)
  194. def reset(self, ident):
  195. """Reset the internal structure and set the identifier"""
  196. self.ident = ident
  197. self.base = strds_base(ident=ident)
  198. if ident != None:
  199. self.base.set_name(self.ident.split("@")[0])
  200. self.base.set_mapset(self.ident.split("@")[1])
  201. self.base.set_creator(str(getpass.getuser()))
  202. self.absolute_time = strds_absolute_time(ident=ident)
  203. self.relative_time = strds_relative_time(ident=ident)
  204. self.spatial_extent = strds_spatial_extent(ident=ident)
  205. self.metadata = strds_metadata(ident=ident)
  206. ###############################################################################
  207. class space_time_raster3d_dataset(abstract_space_time_dataset):
  208. """Space time raster3d dataset class
  209. """
  210. def __init__(self, ident):
  211. abstract_space_time_dataset.__init__(self, ident)
  212. def get_type(self):
  213. return "str3ds"
  214. def get_new_instance(self, ident):
  215. """Return a new instance with the type of this class"""
  216. return space_time_raster3d_dataset(ident)
  217. def get_new_map_instance(self, ident):
  218. """Return a new instance of a map dataset which is associated with the type of this class"""
  219. return raster3d_dataset(ident)
  220. def get_map_register(self):
  221. """Return the name of the map register table"""
  222. return self.metadata.get_raster3d_register()
  223. def set_map_register(self, name):
  224. """Set the name of the map register table"""
  225. self.metadata.set_raster3d_register(name)
  226. def reset(self, ident):
  227. """Reset the internal structure and set the identifier"""
  228. self.ident = ident
  229. self.base = str3ds_base(ident=ident)
  230. if ident != None:
  231. self.base.set_name(self.ident.split("@")[0])
  232. self.base.set_mapset(self.ident.split("@")[1])
  233. self.base.set_creator(str(getpass.getuser()))
  234. self.absolute_time = str3ds_absolute_time(ident=ident)
  235. self.relative_time = str3ds_relative_time(ident=ident)
  236. self.spatial_extent = str3ds_spatial_extent(ident=ident)
  237. self.metadata = str3ds_metadata(ident=ident)
  238. ###############################################################################
  239. class space_time_vector_dataset(abstract_space_time_dataset):
  240. """Space time vector dataset class
  241. """
  242. def __init__(self, ident):
  243. abstract_space_time_dataset.__init__(self, ident)
  244. def get_type(self):
  245. return "stvds"
  246. def get_new_instance(self, ident):
  247. """Return a new instance with the type of this class"""
  248. return space_time_vector_dataset(ident)
  249. def get_new_map_instance(self, ident):
  250. """Return a new instance of a map dataset which is associated with the type of this class"""
  251. return vector_dataset(ident)
  252. def get_map_register(self):
  253. """Return the name of the map register table"""
  254. return self.metadata.get_vector_register()
  255. def set_map_register(self, name):
  256. """Set the name of the map register table"""
  257. self.metadata.set_vector_register(name)
  258. def reset(self, ident):
  259. """Reset the internal structure and set the identifier"""
  260. self.ident = ident
  261. self.base = stvds_base(ident=ident)
  262. if ident != None:
  263. self.base.set_name(self.ident.split("@")[0])
  264. self.base.set_mapset(self.ident.split("@")[1])
  265. self.base.set_creator(str(getpass.getuser()))
  266. self.absolute_time = stvds_absolute_time(ident=ident)
  267. self.relative_time = stvds_relative_time(ident=ident)
  268. self.spatial_extent = stvds_spatial_extent(ident=ident)
  269. self.metadata = stvds_metadata(ident=ident)
  270. ###############################################################################
  271. def register_maps_in_space_time_dataset(type, name, maps, start=None, increment=None):
  272. """Use this method to register maps in space time datasets. This function is generic and
  273. can handle raster, vector and raster3d maps as well as there space time datasets.
  274. Additionally a start time string and an increment string can be specified
  275. to assign a time interval automatically to the maps.
  276. It takes care of the correct update of the space time datasets from all
  277. registered maps.
  278. @type The type of the maps raster, raster3d or vector
  279. @name The name of the space time dataset
  280. @maps A comma separated list of map names
  281. @start The start date and time of the first raster map, in case the map has no date (format absolute: "yyyy-mm-dd HH:MM:SS" or "yyyy-mm-dd", format relative 5.0)
  282. @increment Time increment between maps for time stamp creation (format absolute: NNN seconds, minutes, hours, days, weeks, months, years; format relative: 1.0)
  283. """
  284. # We may need the mapset
  285. mapset = core.gisenv()["MAPSET"]
  286. # Check if the dataset name contains the mapset as well
  287. if name.find("@") < 0:
  288. id = name + "@" + mapset
  289. else:
  290. id = name
  291. if type == "raster":
  292. sp = space_time_raster_dataset(id)
  293. if type == "raster3d":
  294. sp = space_time_raster3d_dataset(id)
  295. if type == "vector":
  296. sp = space_time_vector_dataset(id)
  297. # Read content from temporal database
  298. sp.select()
  299. if sp.is_in_db() == False:
  300. core.fatal("Space time " + sp.get_new_map_instance(None).get_type() + " dataset <" + name + "> not found")
  301. if maps.find(",") == -1:
  302. maplist = (maps,)
  303. else:
  304. maplist = tuple(maps.split(","))
  305. count = 0
  306. for mapname in maplist:
  307. mapname = mapname.strip()
  308. # Check if the map name contains the mapset as well
  309. if mapname.find("@") < 0:
  310. mapid = mapname + "@" + mapset
  311. else:
  312. mapid = mapname
  313. # Get a new instance of the space time dataset map type
  314. map = sp.get_new_map_instance(mapid)
  315. # In case the map is already registered print a message and continue to the next map
  316. # Put the map into the database
  317. if map.is_in_db() == False:
  318. # Break in case no valid time is provided
  319. if start == "" or start == None:
  320. core.fatal("Unable to register " + map.get_type() + " map <" + map.get_id() + ">. The map has no valid time and the start time is not set.")
  321. # Load the data from the grass file database
  322. map.load()
  323. # Put it into the temporal database
  324. map.insert()
  325. else:
  326. map.select()
  327. if map.get_temporal_type() != sp.get_temporal_type():
  328. core.fatal("Unable to register " + map.get_type() + " map <" + map.get_id() + ">. The temporal types are different.")
  329. # Set the valid time
  330. if start:
  331. assign_valid_time_to_map(sp.get_temporal_type(), map, start, increment, count)
  332. # Finally Register map in the space time dataset
  333. sp.register_map(map)
  334. count += 1
  335. # Update the space time tables
  336. sp.update_from_registered_maps()
  337. ###############################################################################
  338. def unregister_maps_from_space_time_datasets(type, name, maps):
  339. """Unregister maps from a single space time dataset or, in case no dataset name is provided,
  340. unregister from all datasets within the maps are registered.
  341. @type The type of the maps raster, vector or raster3d
  342. @name Name of an existing space time raster dataset. If no name is provided the raster map(s) are unregistered from all space time datasets in which they are registered.
  343. @maps Name(s) of existing map(s) to unregister
  344. """
  345. mapset = core.gisenv()["MAPSET"]
  346. # In case a space time dataset is specified
  347. if name:
  348. # Check if the dataset name contains the mapset as well
  349. if name.find("@") < 0:
  350. id = name + "@" + mapset
  351. else:
  352. id = name
  353. if type == "raster":
  354. sp = space_time_raster_dataset(id)
  355. if type == "raster3d":
  356. sp = space_time_raster3d_dataset(id)
  357. if type == "vector":
  358. sp = space_time_vector_dataset(id)
  359. if sp.is_in_db() == False:
  360. core.fatal("Space time " + sp.get_new_map_instance(None).get_type() + " dataset <" + name + "> not found")
  361. # Build the list of maps
  362. if maps.find(",") == -1:
  363. maplist = (maps,)
  364. else:
  365. maplist = tuple(maps.split(","))
  366. for mapname in maplist:
  367. mapname = mapname.strip()
  368. # Check if the map name contains the mapset as well
  369. if mapname.find("@") < 0:
  370. mapid = mapname + "@" + mapset
  371. else:
  372. mapid = mapname
  373. # Create a new instance with the map type
  374. if type == "raster":
  375. map = raster_dataset(mapid)
  376. if type == "raster3d":
  377. map = raster3d_dataset(mapid)
  378. if type == "vector":
  379. map = vector_dataset(mapid)
  380. # Unregister map if in database
  381. if map.is_in_db() == True:
  382. if name:
  383. sp.select()
  384. sp.unregister_map(map)
  385. else:
  386. map.select()
  387. map.unregister()
  388. if name:
  389. sp.update_from_registered_maps()
  390. def assign_valid_time_to_map(ttype, map, start, increment=None, mult=1):
  391. """Assign the valid time to a map dataset
  392. @ttype The temporal type which should be assigned and which the time format is of
  393. @map A map dataset object derived from abstract_map_dataset
  394. @start The start date and time of the first raster map, in case the map has no date (format absolute: "yyyy-mm-dd HH:MM:SS" or "yyyy-mm-dd", format relative 5.0)
  395. @increment Time increment between maps for time stamp creation (format absolute: NNN seconds, minutes, hours, days, weeks, months, years; format relative: 1.0)
  396. @multi A multiplier for the increment
  397. """
  398. if ttype == "absolute":
  399. # Create the start time object
  400. if start.find(":") > 0:
  401. time_format = "%Y-%m-%d %H:%M:%S"
  402. else:
  403. time_format = "%Y-%m-%d"
  404. start_time = datetime.strptime(start, time_format)
  405. end_time = None
  406. # Add the increment
  407. if increment:
  408. start_time = increment_datetime_by_string(start_time, increment, mult)
  409. end_time = increment_datetime_by_string(start_time, increment, 1)
  410. core.verbose("Set absolute valid time for map <" + map.get_id() + "> to " + str(start_time) + " - " + str(end_time))
  411. map.update_absolute_time(start_time, end_time)
  412. else:
  413. if increment:
  414. interval = float(start) + mult * float(increment)
  415. else:
  416. interval = float(start)
  417. core.verbose("Set relative valid time for map <" + map.get_id() + "> to " + str(interval))
  418. map.update_relative_time(interval)