space_time_datasets.py 45 KB

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