space_time_datasets.py 46 KB

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