space_time_datasets.py 48 KB

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