space_time_datasets.py 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157
  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. (C) 2008-2011 by the GRASS Development Team
  5. This program is free software under the GNU General Public
  6. License (>=v2). Read the file COPYING that comes with GRASS
  7. for details.
  8. @author Soeren Gebbert
  9. """
  10. import getpass
  11. import logging
  12. from abstract_map_dataset import *
  13. from abstract_space_time_dataset import *
  14. ###############################################################################
  15. class RasterDataset(AbstractMapDataset):
  16. """!Raster dataset class
  17. This class provides functions to select, update, insert or delete raster
  18. map information and valid time stamps into the SQL temporal database.
  19. Usage:
  20. @code
  21. >>> import grass.script as grass
  22. >>> init()
  23. >>> grass.use_temp_region()
  24. >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0,
  25. ... t=1.0, b=0.0, res=10.0)
  26. 0
  27. >>> grass.run_command("r.mapcalc", overwrite=True, quiet=True,
  28. ... expression="strds_map_test_case = 1")
  29. 0
  30. >>> grass.run_command("r.timestamp", map="strds_map_test_case",
  31. ... date="15 jan 1999", quiet=True)
  32. 0
  33. >>> mapset = get_current_mapset()
  34. >>> name = "strds_map_test_case"
  35. >>> identifier = "%s@%s" % (name, mapset)
  36. >>> rmap = RasterDataset(identifier)
  37. >>> rmap.map_exists()
  38. True
  39. >>> rmap.read_timestamp_from_grass()
  40. True
  41. >>> rmap.get_temporal_extent_as_tuple()
  42. (datetime.datetime(1999, 1, 15, 0, 0), None)
  43. >>> rmap.load()
  44. >>> rmap.spatial_extent.print_info()
  45. +-------------------- Spatial extent ----------------------------------------+
  46. | North:...................... 80.0
  47. | South:...................... 0.0
  48. | East:.. .................... 120.0
  49. | West:....................... 0.0
  50. | Top:........................ 0.0
  51. | Bottom:..................... 0.0
  52. >>> rmap.absolute_time.print_info()
  53. +-------------------- Absolute time -----------------------------------------+
  54. | Start time:................. 1999-01-15 00:00:00
  55. | End time:................... None
  56. >>> rmap.metadata.print_info()
  57. +-------------------- Metadata information ----------------------------------+
  58. | Datatype:................... CELL
  59. | Number of columns:.......... 8
  60. | Number of rows:............. 12
  61. | Number of cells:............ 96
  62. | North-South resolution:..... 10.0
  63. | East-west resolution:....... 10.0
  64. | Minimum value:.............. 1.0
  65. | Maximum value:.............. 1.0
  66. | STRDS register table ....... None
  67. >>> newmap = rmap.get_new_instance("new@PERMANENT")
  68. >>> isinstance(newmap, RasterDataset)
  69. True
  70. >>> newstrds = rmap.get_new_stds_instance("new@PERMANENT")
  71. >>> isinstance(newstrds, SpaceTimeRasterDataset)
  72. True
  73. >>> rmap.get_type()
  74. 'raster'
  75. >>> rmap.get_stds_register()
  76. >>> rmap.set_absolute_time(start_time=datetime(2001,1,1),
  77. ... end_time=datetime(2012,1,1))
  78. True
  79. >>> rmap.get_absolute_time()
  80. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0), None)
  81. >>> rmap.get_temporal_extent_as_tuple()
  82. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
  83. >>> rmap.get_name()
  84. 'strds_map_test_case'
  85. >>> rmap.get_mapset() == mapset
  86. True
  87. >>> rmap.get_temporal_type()
  88. 'absolute'
  89. >>> rmap.get_spatial_extent_as_tuple()
  90. (80.0, 0.0, 120.0, 0.0, 0.0, 0.0)
  91. >>> rmap.is_time_absolute()
  92. True
  93. >>> rmap.is_time_relative()
  94. False
  95. >>> grass.run_command("g.remove", rast=name, quiet=True)
  96. 0
  97. >>> grass.del_temp_region()
  98. @endcode
  99. """
  100. def __init__(self, ident):
  101. AbstractMapDataset.__init__(self)
  102. self.reset(ident)
  103. def get_type(self):
  104. return 'raster'
  105. def get_new_instance(self, ident):
  106. """!Return a new instance with the type of this class"""
  107. return RasterDataset(ident)
  108. def get_new_stds_instance(self, ident):
  109. """!Return a new space time dataset instance in which maps
  110. are stored with the type of this class"""
  111. return SpaceTimeRasterDataset(ident)
  112. def get_stds_register(self):
  113. """!Return the space time dataset register table name in which stds
  114. are listed in which this map is registered"""
  115. return self.metadata.get_strds_register()
  116. def set_stds_register(self, name):
  117. """!Set the space time dataset register table name in which stds
  118. are listed in which this map is registered"""
  119. self.metadata.set_strds_register(name)
  120. def spatial_overlapping(self, dataset):
  121. """!Return True if the spatial extents 2d overlap"""
  122. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  123. def spatial_relation(self, dataset):
  124. """!Return the two dimensional spatial relation"""
  125. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  126. def spatial_intersection(self, dataset):
  127. """!Return the two dimensional intersection as spatial_extent
  128. object or None in case no intersection was found.
  129. @param dataset The abstract dataset to intersect with
  130. @return The intersection spatial extent or None
  131. """
  132. return self.spatial_extent.intersect_2d(dataset.spatial_extent)
  133. def spatial_union(self, dataset):
  134. """!Return the two dimensional union as spatial_extent
  135. object or None in case the extents does not overlap or meet.
  136. @param dataset The abstract dataset to create a union with
  137. @return The union spatial extent or None
  138. """
  139. return self.spatial_extent.union_2d(dataset.spatial_extent)
  140. def spatial_disjoint_union(self, dataset):
  141. """!Return the two dimensional union as spatial_extent object.
  142. @param dataset The abstract dataset to create a union with
  143. @return The union spatial extent
  144. """
  145. return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent)
  146. def get_np_array(self):
  147. """!Return this raster map as memmap numpy style array to access the raster
  148. values in numpy style without loading the whole map in the RAM.
  149. In case this raster map does exists in the grass spatial database,
  150. the map will be exported using r.out.bin to a temporary location
  151. and assigned to the memmap object that is returned by this function.
  152. In case the raster map does not exists, an empty temporary
  153. binary file will be created and assigned to the memap object.
  154. You need to call the write function to write the memmap
  155. array back into grass.
  156. """
  157. a = garray.array()
  158. if self.map_exists():
  159. a.read(self.get_map_id())
  160. return a
  161. def reset(self, ident):
  162. """!Reset the internal structure and set the identifier"""
  163. self.base = RasterBase(ident=ident)
  164. self.absolute_time = RasterAbsoluteTime(ident=ident)
  165. self.relative_time = RasterRelativeTime(ident=ident)
  166. self.spatial_extent = RasterSpatialExtent(ident=ident)
  167. self.metadata = RasterMetadata(ident=ident)
  168. def has_grass_timestamp(self):
  169. """!Check if a grass file bsased time stamp exists for this map.
  170. @return True if success, False on error
  171. """
  172. return self.ciface.has_raster_timestamp(self.get_name(),
  173. self.get_mapset())
  174. def read_timestamp_from_grass(self):
  175. """!Read the timestamp of this map from the map metadata
  176. in the grass file system based spatial database and
  177. set the internal time stamp that should be insert/updated
  178. in the temporal database.
  179. @return True if success, False on error
  180. """
  181. if not self.has_grass_timestamp():
  182. return False
  183. check, dates = self.ciface.read_raster_timestamp(self.get_name(),
  184. self.get_mapset(),)
  185. if check < 1:
  186. self.msgr.error(_("Unable to read timestamp file "
  187. "for raster map <%s>" % (self.get_map_id())))
  188. return False
  189. if len(dates) == 2:
  190. self.set_absolute_time(dates[0], dates[1], None)
  191. else:
  192. self.set_relative_time(dates[0], dates[1], dates[2])
  193. return True
  194. def write_timestamp_to_grass(self):
  195. """!Write the timestamp of this map into the map metadata in
  196. the grass file system based spatial database.
  197. Internally the libgis API functions are used for writing
  198. @return True if success, False on error
  199. """
  200. check = self.ciface.write_raster_timestamp(self.get_name(),
  201. self.get_mapset(),
  202. self._convert_timestamp())
  203. if check == -1:
  204. self.msgr.error(_("Unable to create timestamp file "
  205. "for raster map <%s>" % (self.get_map_id())))
  206. return False
  207. if check == -2:
  208. self.msgr.error(_("Invalid datetime in timestamp for raster map <%s>" %
  209. (self.get_map_id())))
  210. return False
  211. if check == -3:
  212. self.msgr.error(_("Internal error"))
  213. return False
  214. return True
  215. def remove_timestamp_from_grass(self):
  216. """!Remove the timestamp from the grass file system based
  217. spatial database
  218. Internally the libgis API functions are used for removal
  219. @return True if success, False on error
  220. """
  221. check = self.ciface.remove_raster_timestamp(self.get_name(),
  222. self.get_mapset())
  223. if check == -1:
  224. self.msgr.error(_("Unable to remove timestamp for raster map <%s>" %
  225. (self.get_name())))
  226. return False
  227. return True
  228. def map_exists(self):
  229. """!Return True in case the map exists in the grass spatial database
  230. @return True if map exists, False otherwise
  231. """
  232. return self.ciface.raster_map_exists(self.get_name(),
  233. self.get_mapset())
  234. def load(self):
  235. """!Load all info from an existing raster map into the internal s
  236. tructure"""
  237. # Fill base information
  238. self.base.set_creator(str(getpass.getuser()))
  239. kvp = self.ciface.read_raster_info(self.get_name(),
  240. self.get_mapset())
  241. # Fill spatial extent
  242. self.set_spatial_extent_from_values(north=kvp["north"],
  243. south=kvp["south"],
  244. east=kvp["east"],
  245. west=kvp["west"])
  246. # Fill metadata
  247. self.metadata.set_nsres(kvp["nsres"])
  248. self.metadata.set_ewres(kvp["ewres"])
  249. self.metadata.set_datatype(kvp["datatype"])
  250. self.metadata.set_min(kvp["min"])
  251. self.metadata.set_max(kvp["max"])
  252. rows = int(kvp["rows"])
  253. cols = int(kvp["cols"])
  254. ncells = cols * rows
  255. self.metadata.set_cols(cols)
  256. self.metadata.set_rows(rows)
  257. self.metadata.set_number_of_cells(ncells)
  258. ###############################################################################
  259. class Raster3DDataset(AbstractMapDataset):
  260. """!Raster3d dataset class
  261. This class provides functions to select, update, insert or delete raster3d
  262. map information and valid time stamps into the SQL temporal database.
  263. Usage:
  264. @code
  265. >>> import grass.script as grass
  266. >>> init()
  267. >>> grass.use_temp_region()
  268. >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0,
  269. ... t=100.0, b=0.0, res=10.0, res3=10.0)
  270. 0
  271. >>> grass.run_command("r3.mapcalc", overwrite=True, quiet=True,
  272. ... expression="str3ds_map_test_case = 1")
  273. 0
  274. >>> grass.run_command("r3.timestamp", map="str3ds_map_test_case",
  275. ... date="15 jan 1999", quiet=True)
  276. 0
  277. >>> mapset = get_current_mapset()
  278. >>> name = "str3ds_map_test_case"
  279. >>> identifier = "%s@%s" % (name, mapset)
  280. >>> r3map = Raster3DDataset(identifier)
  281. >>> r3map.map_exists()
  282. True
  283. >>> r3map.read_timestamp_from_grass()
  284. True
  285. >>> r3map.get_temporal_extent_as_tuple()
  286. (datetime.datetime(1999, 1, 15, 0, 0), None)
  287. >>> r3map.load()
  288. >>> r3map.spatial_extent.print_info()
  289. +-------------------- Spatial extent ----------------------------------------+
  290. | North:...................... 80.0
  291. | South:...................... 0.0
  292. | East:.. .................... 120.0
  293. | West:....................... 0.0
  294. | Top:........................ 100.0
  295. | Bottom:..................... 0.0
  296. >>> r3map.absolute_time.print_info()
  297. +-------------------- Absolute time -----------------------------------------+
  298. | Start time:................. 1999-01-15 00:00:00
  299. | End time:................... None
  300. >>> r3map.metadata.print_info()
  301. +-------------------- Metadata information ----------------------------------+
  302. | Datatype:................... DCELL
  303. | Number of columns:.......... 8
  304. | Number of rows:............. 12
  305. | Number of cells:............ 960
  306. | North-South resolution:..... 10.0
  307. | East-west resolution:....... 10.0
  308. | Minimum value:.............. 1.0
  309. | Maximum value:.............. 1.0
  310. | Number of depths:........... 10
  311. | Top-Bottom resolution:...... 10.0
  312. | STR3DS register table ...... None
  313. >>> newmap = r3map.get_new_instance("new@PERMANENT")
  314. >>> isinstance(newmap, Raster3DDataset)
  315. True
  316. >>> newstr3ds = r3map.get_new_stds_instance("new@PERMANENT")
  317. >>> isinstance(newstr3ds, SpaceTimeRaster3DDataset)
  318. True
  319. >>> r3map.get_type()
  320. 'raster3d'
  321. >>> r3map.get_stds_register()
  322. >>> r3map.set_absolute_time(start_time=datetime(2001,1,1),
  323. ... end_time=datetime(2012,1,1))
  324. True
  325. >>> r3map.get_absolute_time()
  326. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0), None)
  327. >>> r3map.get_temporal_extent_as_tuple()
  328. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
  329. >>> r3map.get_name()
  330. 'str3ds_map_test_case'
  331. >>> r3map.get_mapset() == mapset
  332. True
  333. >>> r3map.get_temporal_type()
  334. 'absolute'
  335. >>> r3map.get_spatial_extent_as_tuple()
  336. (80.0, 0.0, 120.0, 0.0, 100.0, 0.0)
  337. >>> r3map.is_time_absolute()
  338. True
  339. >>> r3map.is_time_relative()
  340. False
  341. >>> grass.run_command("g.remove", rast3d=name, quiet=True)
  342. 0
  343. >>> grass.del_temp_region()
  344. """
  345. def __init__(self, ident):
  346. AbstractMapDataset.__init__(self)
  347. self.reset(ident)
  348. def get_type(self):
  349. return "raster3d"
  350. def get_new_instance(self, ident):
  351. """!Return a new instance with the type of this class"""
  352. return Raster3DDataset(ident)
  353. def get_new_stds_instance(self, ident):
  354. """!Return a new space time dataset instance in which maps
  355. are stored with the type of this class"""
  356. return SpaceTimeRaster3DDataset(ident)
  357. def get_stds_register(self):
  358. """!Return the space time dataset register table name in
  359. which stds are listed in which this map is registered"""
  360. return self.metadata.get_str3ds_register()
  361. def set_stds_register(self, name):
  362. """!Set the space time dataset register table name in
  363. which stds are listed in which this map is registered"""
  364. self.metadata.set_str3ds_register(name)
  365. def spatial_overlapping(self, dataset):
  366. """!Return True if the spatial extents overlap"""
  367. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  368. return self.spatial_extent.overlapping(dataset.spatial_extent)
  369. else:
  370. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  371. def spatial_relation(self, dataset):
  372. """!Return the two or three dimensional spatial relation"""
  373. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  374. return self.spatial_extent.spatial_relation(dataset.spatial_extent)
  375. else:
  376. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  377. def spatial_intersection(self, dataset):
  378. """!Return the three or two dimensional intersection as spatial_extent
  379. object or None in case no intersection was found.
  380. @param dataset The abstract dataset to intersect with
  381. @return The intersection spatial extent or None
  382. """
  383. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  384. return self.spatial_extent.intersect(dataset.spatial_extent)
  385. else:
  386. return self.spatial_extent.intersect_2d(dataset.spatial_extent)
  387. def spatial_union(self, dataset):
  388. """!Return the three or two dimensional union as spatial_extent
  389. object or None in case the extents does not overlap or meet.
  390. @param dataset The abstract dataset to create a union with
  391. @return The union spatial extent or None
  392. """
  393. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  394. return self.spatial_extent.union(dataset.spatial_extent)
  395. else:
  396. return self.spatial_extent.union_2d(dataset.spatial_extent)
  397. def spatial_disjoint_union(self, dataset):
  398. """!Return the three or two dimensional union as spatial_extent object.
  399. @param dataset The abstract dataset to create a union with
  400. @return The union spatial extent
  401. """
  402. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  403. return self.spatial_extent.disjoint_union(dataset.spatial_extent)
  404. else:
  405. return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent)
  406. def get_np_array(self):
  407. """!Return this 3D raster map as memmap numpy style array to access the 3D raster
  408. values in numpy style without loading the whole map in the RAM.
  409. In case this 3D raster map does exists in the grass spatial database,
  410. the map will be exported using r3.out.bin to a temporary location
  411. and assigned to the memmap object that is returned by this function.
  412. In case the 3D raster map does not exists, an empty temporary
  413. binary file will be created and assigned to the memap object.
  414. You need to call the write function to write the memmap
  415. array back into grass.
  416. """
  417. a = garray.array3d()
  418. if self.map_exists():
  419. a.read(self.get_map_id())
  420. return a
  421. def reset(self, ident):
  422. """!Reset the internal structure and set the identifier"""
  423. self.base = Raster3DBase(ident=ident)
  424. self.absolute_time = Raster3DAbsoluteTime(ident=ident)
  425. self.relative_time = Raster3DRelativeTime(ident=ident)
  426. self.spatial_extent = Raster3DSpatialExtent(ident=ident)
  427. self.metadata = Raster3DMetadata(ident=ident)
  428. def has_grass_timestamp(self):
  429. """!Check if a grass file bsased time stamp exists for this map.
  430. @return True if success, False on error
  431. """
  432. return self.ciface.has_raster3d_timestamp(self.get_name(),
  433. self.get_mapset())
  434. def read_timestamp_from_grass(self):
  435. """!Read the timestamp of this map from the map metadata
  436. in the grass file system based spatial database and
  437. set the internal time stamp that should be insert/updated
  438. in the temporal database.
  439. @return True if success, False on error
  440. """
  441. if not self.has_grass_timestamp():
  442. return False
  443. check, dates = self.ciface.read_raster3d_timestamp(self.get_name(),
  444. self.get_mapset(),)
  445. if check < 1:
  446. self.msgr.error(_("Unable to read timestamp file "
  447. "for 3D raster map <%s>" % (self.get_map_id())))
  448. return False
  449. if len(dates) == 2:
  450. self.set_absolute_time(dates[0], dates[1], None)
  451. else:
  452. self.set_relative_time(dates[0], dates[1], dates[2])
  453. return True
  454. def write_timestamp_to_grass(self):
  455. """!Write the timestamp of this map into the map metadata
  456. in the grass file system based spatial database.
  457. Internally the libgis API functions are used for writing
  458. @return True if success, False on error
  459. """
  460. check = self.ciface.write_raster3d_timestamp(self.get_name(),
  461. self.get_mapset(),
  462. self._convert_timestamp())
  463. if check == -1:
  464. self.msgr.error(_("Unable to create timestamp file "
  465. "for 3D raster map <%s>" % (self.get_map_id())))
  466. return False
  467. if check == -2:
  468. self.msgr.error(_("Invalid datetime in timestamp for 3D raster map <%s>" %
  469. (self.get_map_id())))
  470. return False
  471. if check == -3:
  472. self.msgr.error(_("Internal error"))
  473. return False
  474. return True
  475. def remove_timestamp_from_grass(self):
  476. """!Remove the timestamp from the grass file system based spatial database
  477. @return True if success, False on error
  478. """
  479. check = self.ciface.remove_raster3d_timestamp(self.get_name(),
  480. self.get_mapset())
  481. if check == -1:
  482. self.msgr.error(_("Unable to remove timestamp for raster map <%s>" %
  483. (self.get_name())))
  484. return False
  485. return True
  486. def map_exists(self):
  487. """!Return True in case the map exists in the grass spatial database
  488. @return True if map exists, False otherwise
  489. """
  490. return self.ciface.raster3d_map_exists(self.get_name(),
  491. self.get_mapset())
  492. def load(self):
  493. """!Load all info from an existing raster3d map into the internal structure"""
  494. # Fill base information
  495. self.base.set_creator(str(getpass.getuser()))
  496. # Fill spatial extent
  497. kvp = self.ciface.read_raster3d_info(self.get_name(),
  498. self.get_mapset())
  499. self.set_spatial_extent_from_values(north=kvp["north"], south=kvp["south"],
  500. east=kvp["east"], west=kvp["west"],
  501. top=kvp["top"], bottom=kvp["bottom"])
  502. # Fill metadata
  503. self.metadata.set_nsres(kvp["nsres"])
  504. self.metadata.set_ewres(kvp["ewres"])
  505. self.metadata.set_tbres(kvp["tbres"])
  506. self.metadata.set_datatype(kvp["datatype"])
  507. self.metadata.set_min(kvp["min"])
  508. self.metadata.set_max(kvp["max"])
  509. rows = int(kvp["rows"])
  510. cols = int(kvp["cols"])
  511. depths = int(kvp["depths"])
  512. ncells = cols * rows * depths
  513. self.metadata.set_cols(cols)
  514. self.metadata.set_rows(rows)
  515. self.metadata.set_depths(depths)
  516. self.metadata.set_number_of_cells(ncells)
  517. ###############################################################################
  518. class VectorDataset(AbstractMapDataset):
  519. """!Vector dataset class
  520. This class provides functions to select, update, insert or delete vector
  521. map information and valid time stamps into the SQL temporal database.
  522. Usage:
  523. @code
  524. >>> import grass.script as grass
  525. >>> init()
  526. >>> grass.use_temp_region()
  527. >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0,
  528. ... t=1.0, b=0.0, res=10.0)
  529. 0
  530. >>> grass.run_command("v.random", overwrite=True, output="stvds_map_test_case",
  531. ... n=100, zmin=0, zmax=100, flags="z", column="elevation", quiet=True)
  532. 0
  533. >>> grass.run_command("v.timestamp", map="stvds_map_test_case",
  534. ... date="15 jan 1999", quiet=True)
  535. 0
  536. >>> mapset = get_current_mapset()
  537. >>> name = "stvds_map_test_case"
  538. >>> identifier = "%s@%s" % (name, mapset)
  539. >>> vmap = VectorDataset(identifier)
  540. >>> vmap.map_exists()
  541. True
  542. >>> vmap.read_timestamp_from_grass()
  543. True
  544. >>> vmap.get_temporal_extent_as_tuple()
  545. (datetime.datetime(1999, 1, 15, 0, 0), None)
  546. >>> vmap.load()
  547. >>> vmap.absolute_time.print_info()
  548. +-------------------- Absolute time -----------------------------------------+
  549. | Start time:................. 1999-01-15 00:00:00
  550. | End time:................... None
  551. >>> vmap.metadata.print_info()
  552. +-------------------- Metadata information ----------------------------------+
  553. | STVDS register table ....... None
  554. | Is map 3d .................. True
  555. | Number of points ........... 100
  556. | Number of lines ............ 0
  557. | Number of boundaries ....... 0
  558. | Number of centroids ........ 0
  559. | Number of faces ............ 0
  560. | Number of kernels .......... 0
  561. | Number of primitives ....... 100
  562. | Number of nodes ............ 0
  563. | Number of areas ............ 0
  564. | Number of islands .......... 0
  565. | Number of holes ............ 0
  566. | Number of volumes .......... 0
  567. >>> newmap = vmap.get_new_instance("new@PERMANENT")
  568. >>> isinstance(newmap, VectorDataset)
  569. True
  570. >>> newstvds = vmap.get_new_stds_instance("new@PERMANENT")
  571. >>> isinstance(newstvds, SpaceTimeVectorDataset)
  572. True
  573. >>> vmap.get_type()
  574. 'vector'
  575. >>> vmap.get_stds_register()
  576. >>> vmap.set_absolute_time(start_time=datetime(2001,1,1),
  577. ... end_time=datetime(2012,1,1))
  578. True
  579. >>> vmap.get_absolute_time()
  580. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0), None)
  581. >>> vmap.get_temporal_extent_as_tuple()
  582. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
  583. >>> vmap.get_name()
  584. 'stvds_map_test_case'
  585. >>> vmap.get_mapset() == mapset
  586. True
  587. >>> vmap.get_temporal_type()
  588. 'absolute'
  589. >>> vmap.is_time_absolute()
  590. True
  591. >>> vmap.is_time_relative()
  592. False
  593. >>> grass.run_command("g.remove", vect=name, quiet=True)
  594. 0
  595. >>> grass.del_temp_region()
  596. @endcode
  597. """
  598. def __init__(self, ident):
  599. AbstractMapDataset.__init__(self)
  600. self.reset(ident)
  601. def get_type(self):
  602. return "vector"
  603. def get_new_instance(self, ident):
  604. """!Return a new instance with the type of this class"""
  605. return VectorDataset(ident)
  606. def get_new_stds_instance(self, ident):
  607. """!Return a new space time dataset instance in which maps
  608. are stored with the type of this class"""
  609. return SpaceTimeVectorDataset(ident)
  610. def get_stds_register(self):
  611. """!Return the space time dataset register table name in
  612. which stds are listed in which this map is registered"""
  613. return self.metadata.get_stvds_register()
  614. def set_stds_register(self, name):
  615. """!Set the space time dataset register table name in
  616. which stds are listed in which this map is registered"""
  617. self.metadata.set_stvds_register(name)
  618. def get_layer(self):
  619. """!Return the layer"""
  620. return self.base.get_layer()
  621. def spatial_overlapping(self, dataset):
  622. """!Return True if the spatial extents 2d overlap"""
  623. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  624. def spatial_relation(self, dataset):
  625. """!Return the two dimensional spatial relation"""
  626. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  627. def spatial_intersection(self, dataset):
  628. """!Return the two dimensional intersection as spatial_extent
  629. object or None in case no intersection was found.
  630. @param dataset The abstract dataset to intersect with
  631. @return The intersection spatial extent or None
  632. """
  633. return self.spatial_extent.intersect_2d(dataset.spatial_extent)
  634. def spatial_union(self, dataset):
  635. """!Return the two dimensional union as spatial_extent
  636. object or None in case the extents does not overlap or meet.
  637. @param dataset The abstract dataset to create a union with
  638. @return The union spatial extent or None
  639. """
  640. return self.spatial_extent.union_2d(dataset.spatial_extent)
  641. def spatial_disjoint_union(self, dataset):
  642. """!Return the two dimensional union as spatial_extent object.
  643. @param dataset The abstract dataset to create a union with
  644. @return The union spatial extent
  645. """
  646. return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent)
  647. def reset(self, ident):
  648. """!Reset the internal structure and set the identifier"""
  649. self.base = VectorBase(ident=ident)
  650. self.absolute_time = VectorAbsoluteTime(ident=ident)
  651. self.relative_time = VectorRelativeTime(ident=ident)
  652. self.spatial_extent = VectorSpatialExtent(ident=ident)
  653. self.metadata = VectorMetadata(ident=ident)
  654. def has_grass_timestamp(self):
  655. """!Check if a grass file bsased time stamp exists for this map.
  656. """
  657. return self.ciface.has_vector_timestamp(self.get_name(),
  658. self.get_mapset(),
  659. self.get_layer())
  660. def read_timestamp_from_grass(self):
  661. """!Read the timestamp of this map from the map metadata
  662. in the grass file system based spatial database and
  663. set the internal time stamp that should be insert/updated
  664. in the temporal database.
  665. """
  666. if not self.has_grass_timestamp():
  667. return False
  668. check, dates = self.ciface.read_vector_timestamp(self.get_name(),
  669. self.get_mapset(),)
  670. if check < 1:
  671. self.msgr.error(_("Unable to read timestamp file "
  672. "for vector map <%s>" % (self.get_map_id())))
  673. return False
  674. if len(dates) == 2:
  675. self.set_absolute_time(dates[0], dates[1], None)
  676. else:
  677. self.set_relative_time(dates[0], dates[1], dates[2])
  678. return True
  679. def write_timestamp_to_grass(self):
  680. """!Write the timestamp of this map into the map metadata in
  681. the grass file system based spatial database.
  682. Internally the libgis API functions are used for writing
  683. """
  684. check = self.ciface.write_vector_timestamp(self.get_name(),
  685. self.get_mapset(),
  686. self._convert_timestamp(),
  687. self.get_layer())
  688. if check == -1:
  689. self.msgr.error(_("Unable to create timestamp file "
  690. "for vector map <%s>" % (self.get_map_id())))
  691. return False
  692. if check == -2:
  693. self.msgr.error(_("Invalid datetime in timestamp for vector map <%s>" %
  694. (self.get_map_id())))
  695. return False
  696. return True
  697. def remove_timestamp_from_grass(self):
  698. """!Remove the timestamp from the grass file system based spatial
  699. database
  700. Internally the libgis API functions are used for removal
  701. """
  702. check = self.ciface.remove_vector_timestamp(self.get_name(),
  703. self.get_mapset())
  704. if check == -1:
  705. self.msgr.error(_("Unable to remove timestamp for vector map <%s>" %
  706. (self.get_name())))
  707. return False
  708. return True
  709. def map_exists(self):
  710. """!Return True in case the map exists in the grass spatial database
  711. @return True if map exists, False otherwise
  712. """
  713. return self.ciface.vector_map_exists(self.get_name(),
  714. self.get_mapset())
  715. def load(self):
  716. """!Load all info from an existing vector map into the internal
  717. structure"""
  718. # Fill base information
  719. self.base.set_creator(str(getpass.getuser()))
  720. # Get the data from an existing vector map
  721. kvp = self.ciface.read_vector_info(self.get_name(),
  722. self.get_mapset())
  723. # Fill spatial extent
  724. self.set_spatial_extent_from_values(north=kvp["north"], south=kvp["south"],
  725. east=kvp["east"], west=kvp["west"],
  726. top=kvp["top"], bottom=kvp["bottom"])
  727. # Fill metadata
  728. self.metadata.set_3d_info(kvp["map3d"])
  729. self.metadata.set_number_of_points(kvp["points"])
  730. self.metadata.set_number_of_lines(kvp["lines"])
  731. self.metadata.set_number_of_boundaries(kvp["boundaries"])
  732. self.metadata.set_number_of_centroids(kvp["centroids"])
  733. self.metadata.set_number_of_faces(kvp["faces"])
  734. self.metadata.set_number_of_kernels(kvp["kernels"])
  735. self.metadata.set_number_of_primitives(kvp["primitives"])
  736. self.metadata.set_number_of_nodes(kvp["nodes"])
  737. self.metadata.set_number_of_areas(kvp["areas"])
  738. self.metadata.set_number_of_islands(kvp["islands"])
  739. self.metadata.set_number_of_holes(kvp["holes"])
  740. self.metadata.set_number_of_volumes(kvp["volumes"])
  741. ###############################################################################
  742. class SpaceTimeRasterDataset(AbstractSpaceTimeDataset):
  743. """!Space time raster dataset class
  744. """
  745. def __init__(self, ident):
  746. AbstractSpaceTimeDataset.__init__(self, ident)
  747. def get_type(self):
  748. return "strds"
  749. def get_new_instance(self, ident):
  750. """!Return a new instance with the type of this class"""
  751. return SpaceTimeRasterDataset(ident)
  752. def get_new_map_instance(self, ident):
  753. """!Return a new instance of a map dataset which is associated "
  754. "with the type of this class"""
  755. return RasterDataset(ident)
  756. def get_map_register(self):
  757. """!Return the name of the map register table"""
  758. return self.metadata.get_raster_register()
  759. def set_map_register(self, name):
  760. """!Set the name of the map register table"""
  761. self.metadata.set_raster_register(name)
  762. def spatial_overlapping(self, dataset):
  763. """!Return True if the spatial extents 2d overlap"""
  764. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  765. def spatial_relation(self, dataset):
  766. """!Return the two dimensional spatial relation"""
  767. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  768. def spatial_intersection(self, dataset):
  769. """!Return the two dimensional intersection as spatial_extent
  770. object or None in case no intersection was found.
  771. @param dataset The abstract dataset to intersect with
  772. @return The intersection spatial extent or None
  773. """
  774. return self.spatial_extent.intersect_2d(dataset.spatial_extent)
  775. def spatial_union(self, dataset):
  776. """!Return the two dimensional union as spatial_extent
  777. object or None in case the extents does not overlap or meet.
  778. @param dataset The abstract dataset to create a union with
  779. @return The union spatial extent or None
  780. """
  781. return self.spatial_extent.union_2d(dataset.spatial_extent)
  782. def spatial_disjoint_union(self, dataset):
  783. """!Return the two dimensional union as spatial_extent object.
  784. @param dataset The abstract dataset to create a union with
  785. @return The union spatial extent
  786. """
  787. return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent)
  788. def reset(self, ident):
  789. """!Reset the internal structure and set the identifier"""
  790. self.base = STRDSBase(ident=ident)
  791. self.base.set_creator(str(getpass.getuser()))
  792. self.absolute_time = STRDSAbsoluteTime(ident=ident)
  793. self.relative_time = STRDSRelativeTime(ident=ident)
  794. self.spatial_extent = STRDSSpatialExtent(ident=ident)
  795. self.metadata = STRDSMetadata(ident=ident)
  796. ###############################################################################
  797. class SpaceTimeRaster3DDataset(AbstractSpaceTimeDataset):
  798. """!Space time raster3d dataset class
  799. """
  800. def __init__(self, ident):
  801. AbstractSpaceTimeDataset.__init__(self, ident)
  802. def get_type(self):
  803. return "str3ds"
  804. def get_new_instance(self, ident):
  805. """!Return a new instance with the type of this class"""
  806. return SpaceTimeRaster3DDataset(ident)
  807. def get_new_map_instance(self, ident):
  808. """!Return a new instance of a map dataset which is associated
  809. with the type of this class"""
  810. return Raster3DDataset(ident)
  811. def get_map_register(self):
  812. """!Return the name of the map register table"""
  813. return self.metadata.get_raster3d_register()
  814. def set_map_register(self, name):
  815. """!Set the name of the map register table"""
  816. self.metadata.set_raster3d_register(name)
  817. def spatial_overlapping(self, dataset):
  818. """!Return True if the spatial extents overlap"""
  819. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  820. return self.spatial_extent.overlapping(dataset.spatial_extent)
  821. else:
  822. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  823. def spatial_relation(self, dataset):
  824. """!Return the two or three dimensional spatial relation"""
  825. if self.get_type() == dataset.get_type() or \
  826. dataset.get_type() == "str3ds":
  827. return self.spatial_extent.spatial_relation(dataset.spatial_extent)
  828. else:
  829. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  830. def spatial_intersection(self, dataset):
  831. """!Return the three or two dimensional intersection as spatial_extent
  832. object or None in case no intersection was found.
  833. @param dataset The abstract dataset to intersect with
  834. @return The intersection spatial extent or None
  835. """
  836. if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d":
  837. return self.spatial_extent.intersect(dataset.spatial_extent)
  838. else:
  839. return self.spatial_extent.intersect_2d(dataset.spatial_extent)
  840. def spatial_union(self, dataset):
  841. """!Return the three or two dimensional union as spatial_extent
  842. object or None in case the extents does not overlap or meet.
  843. @param dataset The abstract dataset to create a union with
  844. @return The union spatial extent or None
  845. """
  846. if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d":
  847. return self.spatial_extent.union(dataset.spatial_extent)
  848. else:
  849. return self.spatial_extent.union_2d(dataset.spatial_extent)
  850. def spatial_disjoint_union(self, dataset):
  851. """!Return the three or two dimensional union as spatial_extent object.
  852. @param dataset The abstract dataset to create a union with
  853. @return The union spatial extent
  854. """
  855. if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d":
  856. return self.spatial_extent.disjoint_union(dataset.spatial_extent)
  857. else:
  858. return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent)
  859. def reset(self, ident):
  860. """!Reset the internal structure and set the identifier"""
  861. self.base = STR3DSBase(ident=ident)
  862. self.base.set_creator(str(getpass.getuser()))
  863. self.absolute_time = STR3DSAbsoluteTime(ident=ident)
  864. self.relative_time = STR3DSRelativeTime(ident=ident)
  865. self.spatial_extent = STR3DSSpatialExtent(ident=ident)
  866. self.metadata = STR3DSMetadata(ident=ident)
  867. ###############################################################################
  868. class SpaceTimeVectorDataset(AbstractSpaceTimeDataset):
  869. """!Space time vector dataset class
  870. """
  871. def __init__(self, ident):
  872. AbstractSpaceTimeDataset.__init__(self, ident)
  873. def get_type(self):
  874. return "stvds"
  875. def get_new_instance(self, ident):
  876. """!Return a new instance with the type of this class"""
  877. return SpaceTimeVectorDataset(ident)
  878. def get_new_map_instance(self, ident):
  879. """!Return a new instance of a map dataset which is associated
  880. with the type of this class"""
  881. return VectorDataset(ident)
  882. def get_map_register(self):
  883. """!Return the name of the map register table"""
  884. return self.metadata.get_vector_register()
  885. def set_map_register(self, name):
  886. """!Set the name of the map register table"""
  887. self.metadata.set_vector_register(name)
  888. def spatial_overlapping(self, dataset):
  889. """!Return True if the spatial extents 2d overlap"""
  890. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  891. def spatial_relation(self, dataset):
  892. """!Return the two dimensional spatial relation"""
  893. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  894. def spatial_intersection(self, dataset):
  895. """!Return the two dimensional intersection as spatial_extent
  896. object or None in case no intersection was found.
  897. @param dataset The abstract dataset to intersect with
  898. @return The intersection spatial extent or None
  899. """
  900. return self.spatial_extent.intersect_2d(dataset.spatial_extent)
  901. def spatial_union(self, dataset):
  902. """!Return the two dimensional union as spatial_extent
  903. object or None in case the extents does not overlap or meet.
  904. @param dataset The abstract dataset to create a union with
  905. @return The union spatial extent or None
  906. """
  907. return self.spatial_extent.union_2d(dataset.spatial_extent)
  908. def spatial_disjoint_union(self, dataset):
  909. """!Return the two dimensional union as spatial_extent object.
  910. @param dataset The abstract dataset to create a union with
  911. @return The union spatial extent
  912. """
  913. return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent)
  914. def reset(self, ident):
  915. """!Reset the internal structure and set the identifier"""
  916. self.base = STVDSBase(ident=ident)
  917. self.base.set_creator(str(getpass.getuser()))
  918. self.absolute_time = STVDSAbsoluteTime(ident=ident)
  919. self.relative_time = STVDSRelativeTime(ident=ident)
  920. self.spatial_extent = STVDSSpatialExtent(ident=ident)
  921. self.metadata = STVDSMetadata(ident=ident)
  922. ###############################################################################
  923. if __name__ == "__main__":
  924. import doctest
  925. doctest.testmod()