space_time_datasets.py 47 KB

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