space_time_datasets.py 46 KB

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