space_time_datasets.py 45 KB

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