space_time_datasets.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. """!@package grass.temporal
  2. @brief GRASS Python scripting module (temporal GIS functions)
  3. Temporal GIS related functions to be used in Python scripts.
  4. (C) 2008-2011 by the GRASS Development Team
  5. This program is free software under the GNU General Public
  6. License (>=v2). Read the file COPYING that comes with GRASS
  7. for details.
  8. >>> import grass.script as grass
  9. >>> grass.run_command("r3.mapcalc", overwrite=True, expression="str3ds_map_test_case = 1")
  10. 0
  11. >>> grass.run_command("v.random", overwrite=True, output="stvds_map_test_case",
  12. ... n=100, zmin=0, zmax=100, flags="z", column="elevation")
  13. 0
  14. @author Soeren Gebbert
  15. """
  16. import getpass
  17. from ctypes import *
  18. import grass.lib.gis as libgis
  19. import grass.lib.raster as libraster
  20. import grass.lib.vector as libvector
  21. import grass.lib.raster3d as libraster3d
  22. import grass.script as grass
  23. from abstract_space_time_dataset import *
  24. ###############################################################################
  25. class RasterDataset(AbstractMapDataset):
  26. """!Raster dataset class
  27. This class provides functions to select, update, insert or delete raster
  28. map information and valid time stamps into the SQL temporal database.
  29. Usage:
  30. @code
  31. >>> import grass.script as grass
  32. >>> init()
  33. >>> grass.use_temp_region()
  34. >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0,
  35. ... t=1.0, b=0.0, res=10.0)
  36. 0
  37. >>> grass.run_command("r.mapcalc", overwrite=True,
  38. ... expression="strds_map_test_case = 1")
  39. 0
  40. >>> mapset = grass.gisenv()["MAPSET"]
  41. >>> name = "strds_map_test_case"
  42. >>> identifier = "%s@%s" % (name, mapset)
  43. >>> rmap = RasterDataset(identifier)
  44. >>> rmap.set_absolute_time(start_time=datetime(2001,1,1),
  45. ... end_time=datetime(2012,1,1))
  46. >>> rmap.map_exists()
  47. True
  48. >>> rmap.load()
  49. >>> rmap.spatial_extent.print_info()
  50. +-------------------- Spatial extent ----------------------------------------+
  51. | North:...................... 80.0
  52. | South:...................... 0.0
  53. | East:.. .................... 120.0
  54. | West:....................... 0.0
  55. | Top:........................ 0.0
  56. | Bottom:..................... 0.0
  57. >>> rmap.absolute_time.print_info()
  58. +-------------------- Absolute time -----------------------------------------+
  59. | Start time:................. 2001-01-01 00:00:00
  60. | End time:................... 2012-01-01 00:00:00
  61. >>> rmap.metadata.print_info()
  62. +-------------------- Metadata information ----------------------------------+
  63. | Datatype:................... CELL
  64. | Number of columns:.......... 8
  65. | Number of rows:............. 12
  66. | Number of cells:............ 96
  67. | North-South resolution:..... 10.0
  68. | East-west resolution:....... 10.0
  69. | Minimum value:.............. 1.0
  70. | Maximum value:.............. 1.0
  71. | STRDS register table ....... None
  72. >>> newmap = rmap.get_new_instance("new@PERMANENT")
  73. >>> isinstance(newmap, RasterDataset)
  74. True
  75. >>> newstrds = rmap.get_new_stds_instance("new@PERMANENT")
  76. >>> isinstance(newstrds, SpaceTimeRasterDataset)
  77. True
  78. >>> rmap.get_type()
  79. 'raster'
  80. >>> rmap.get_stds_register()
  81. >>> rmap.get_absolute_time()
  82. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0), None)
  83. >>> rmap.get_valid_time()
  84. (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
  85. >>> rmap.get_name()
  86. 'strds_map_test_case'
  87. >>> rmap.get_mapset() == mapset
  88. True
  89. >>> rmap.get_temporal_type()
  90. 'absolute'
  91. >>> rmap.get_spatial_extent()
  92. (80.0, 0.0, 120.0, 0.0, 0.0, 0.0)
  93. >>> rmap.is_time_absolute()
  94. True
  95. >>> rmap.is_time_relative()
  96. False
  97. >>> grass.run_command("g.remove", rast=name)
  98. 0
  99. >>> grass.del_temp_region()
  100. @endcode
  101. """
  102. def __init__(self, ident):
  103. AbstractMapDataset.__init__(self)
  104. self.reset(ident)
  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 get_stds_register(self):
  115. """!Return the space time dataset register table name in which stds
  116. are listed in which this map is registered"""
  117. return self.metadata.get_strds_register()
  118. def set_stds_register(self, name):
  119. """!Set the space time dataset register table name in which stds
  120. are listed in which this map is registered"""
  121. self.metadata.set_strds_register(name)
  122. def spatial_overlapping(self, dataset):
  123. """!Return True if the spatial extents 2d overlap"""
  124. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  125. def spatial_relation(self, dataset):
  126. """!Return the two dimensional spatial relation"""
  127. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  128. def get_np_array(self):
  129. """!Return this raster map as memmap numpy style array to access the raster
  130. values in numpy style without loading the whole map in the RAM.
  131. In case this raster map does exists in the grass spatial database,
  132. the map will be exported using r.out.bin to a temporary location
  133. and assigned to the memmap object that is returned by this function.
  134. In case the raster map does not exists, an empty temporary
  135. binary file will be created and assigned to the memap object.
  136. You need to call the write function to write the memmap
  137. array back into grass.
  138. """
  139. a = garray.array()
  140. if self.map_exists():
  141. a.read(self.get_map_id())
  142. return a
  143. def reset(self, ident):
  144. """!Reset the internal structure and set the identifier"""
  145. self.base = RasterBase(ident=ident)
  146. self.absolute_time = RasterAbsoluteTime(ident=ident)
  147. self.relative_time = RasterRelativeTime(ident=ident)
  148. self.spatial_extent = RasterSpatialExtent(ident=ident)
  149. self.metadata = RasterMetadata(ident=ident)
  150. def has_grass_timestamp(self):
  151. """!Check if a grass file bsased time stamp exists for this map.
  152. """
  153. if G_has_raster_timestamp(self.get_name(), self.get_mapset()):
  154. return True
  155. else:
  156. return False
  157. def write_timestamp_to_grass(self):
  158. """!Write the timestamp of this map into the map metadata in
  159. the grass file system based spatial database.
  160. Internally the libgis API functions are used for writing
  161. """
  162. ts = libgis.TimeStamp()
  163. libgis.G_scan_timestamp(byref(ts), self._convert_timestamp())
  164. check = libgis.G_write_raster_timestamp(self.get_name(), byref(ts))
  165. if check == -1:
  166. core.error(_("Unable to create timestamp file "
  167. "for raster map <%s>" % (self.get_map_id())))
  168. if check == -2:
  169. core.error(_("Invalid datetime in timestamp for raster map <%s>" %
  170. (self.get_map_id())))
  171. def remove_timestamp_from_grass(self):
  172. """!Remove the timestamp from the grass file system based
  173. spatial database
  174. Internally the libgis API functions are used for removal
  175. """
  176. check = libgis.G_remove_raster_timestamp(self.get_name())
  177. if check == -1:
  178. core.error(_("Unable to remove timestamp for raster map <%s>" %
  179. (self.get_name())))
  180. def map_exists(self):
  181. """!Return True in case the map exists in the grass spatial database
  182. @return True if map exists, False otherwise
  183. """
  184. mapset = libgis.G_find_raster(self.get_name(), self.get_mapset())
  185. if not mapset:
  186. return False
  187. return True
  188. def read_info(self):
  189. """!Read the raster map info from the file system and store the content
  190. into a dictionary
  191. This method uses the ctypes interface to the gis and raster libraries
  192. to read the map metadata information
  193. """
  194. kvp = {}
  195. name = self.get_name()
  196. mapset = self.get_mapset()
  197. if not self.map_exists():
  198. core.fatal(_("Raster map <%s> not found" % name))
  199. # Read the region information
  200. region = libgis.Cell_head()
  201. libraster.Rast_get_cellhd(name, mapset, byref(region))
  202. kvp["north"] = region.north
  203. kvp["south"] = region.south
  204. kvp["east"] = region.east
  205. kvp["west"] = region.west
  206. kvp["nsres"] = region.ns_res
  207. kvp["ewres"] = region.ew_res
  208. kvp["rows"] = region.cols
  209. kvp["cols"] = region.rows
  210. maptype = libraster.Rast_map_type(name, mapset)
  211. if maptype == libraster.DCELL_TYPE:
  212. kvp["datatype"] = "DCELL"
  213. elif maptype == libraster.FCELL_TYPE:
  214. kvp["datatype"] = "FCELL"
  215. elif maptype == libraster.CELL_TYPE:
  216. kvp["datatype"] = "CELL"
  217. # Read range
  218. if libraster.Rast_map_is_fp(name, mapset):
  219. range = libraster.FPRange()
  220. libraster.Rast_init_fp_range(byref(range))
  221. ret = libraster.Rast_read_fp_range(name, mapset, byref(range))
  222. if ret < 0:
  223. core.fatal(_("Unable to read range file"))
  224. if ret == 2:
  225. kvp["min"] = None
  226. kvp["max"] = None
  227. else:
  228. min = libgis.DCELL()
  229. max = libgis.DCELL()
  230. libraster.Rast_get_fp_range_min_max(
  231. byref(range), byref(min), byref(max))
  232. kvp["min"] = min.value
  233. kvp["max"] = max.value
  234. else:
  235. range = libraster.Range()
  236. libraster.Rast_init_range(byref(range))
  237. ret = libraster.Rast_read_range(name, mapset, byref(range))
  238. if ret < 0:
  239. core.fatal(_("Unable to read range file"))
  240. if ret == 2:
  241. kvp["min"] = None
  242. kvp["max"] = None
  243. else:
  244. min = libgis.CELL()
  245. max = libgis.CELL()
  246. libraster.Rast_get_range_min_max(
  247. byref(range), byref(min), byref(max))
  248. kvp["min"] = min.value
  249. kvp["max"] = max.value
  250. return kvp
  251. def load(self):
  252. """!Load all info from an existing raster map into the internal s
  253. tructure"""
  254. # Fill base information
  255. self.base.set_creator(str(getpass.getuser()))
  256. # Get the data from an existing raster map
  257. if get_use_ctypes_map_access() == True:
  258. kvp = self.read_info()
  259. else:
  260. kvp = grass.raster_info(self.get_id())
  261. # Fill spatial extent
  262. self.set_spatial_extent(north=kvp["north"], south=kvp["south"],
  263. east=kvp["east"], west=kvp["west"])
  264. # Fill metadata
  265. self.metadata.set_nsres(kvp["nsres"])
  266. self.metadata.set_ewres(kvp["ewres"])
  267. self.metadata.set_datatype(kvp["datatype"])
  268. self.metadata.set_min(kvp["min"])
  269. self.metadata.set_max(kvp["max"])
  270. rows = int(kvp["rows"])
  271. cols = int(kvp["cols"])
  272. ncells = cols * rows
  273. self.metadata.set_cols(cols)
  274. self.metadata.set_rows(rows)
  275. self.metadata.set_number_of_cells(ncells)
  276. ###############################################################################
  277. class Raster3DDataset(AbstractMapDataset):
  278. """!Raster3d dataset class
  279. This class provides functions to select, update, insert or delete raster3d
  280. map information and valid time stamps into the SQL temporal database.
  281. """
  282. def __init__(self, ident):
  283. AbstractMapDataset.__init__(self)
  284. self.reset(ident)
  285. def get_type(self):
  286. return "raster3d"
  287. def get_new_instance(self, ident):
  288. """!Return a new instance with the type of this class"""
  289. return Raster3DDataset(ident)
  290. def get_new_stds_instance(self, ident):
  291. """!Return a new space time dataset instance in which maps
  292. are stored with the type of this class"""
  293. return SpaceTimeRaster3DDataset(ident)
  294. def get_stds_register(self):
  295. """!Return the space time dataset register table name in
  296. which stds are listed in which this map is registered"""
  297. return self.metadata.get_str3ds_register()
  298. def set_stds_register(self, name):
  299. """!Set the space time dataset register table name in
  300. which stds are listed in which this map is registered"""
  301. self.metadata.set_str3ds_register(name)
  302. def spatial_overlapping(self, dataset):
  303. """!Return True if the spatial extents overlap"""
  304. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  305. return self.spatial_extent.overlapping(dataset.spatial_extent)
  306. else:
  307. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  308. def spatial_relation(self, dataset):
  309. """!Return the two or three dimensional spatial relation"""
  310. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  311. return self.spatial_extent.spatial_relation(dataset.spatial_extent)
  312. else:
  313. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  314. def get_np_array(self):
  315. """!Return this 3D raster map as memmap numpy style array to access the 3D raster
  316. values in numpy style without loading the whole map in the RAM.
  317. In case this 3D raster map does exists in the grass spatial database,
  318. the map will be exported using r3.out.bin to a temporary location
  319. and assigned to the memmap object that is returned by this function.
  320. In case the 3D raster map does not exists, an empty temporary
  321. binary file will be created and assigned to the memap object.
  322. You need to call the write function to write the memmap
  323. array back into grass.
  324. """
  325. a = garray.array3d()
  326. if self.map_exists():
  327. a.read(self.get_map_id())
  328. return a
  329. def reset(self, ident):
  330. """!Reset the internal structure and set the identifier"""
  331. self.base = Raster3DBase(ident=ident)
  332. self.absolute_time = Raster3DAbsoluteTime(ident=ident)
  333. self.relative_time = Raster3DRelativeTime(ident=ident)
  334. self.spatial_extent = Raster3DSpatialExtent(ident=ident)
  335. self.metadata = Raster3DMetadata(ident=ident)
  336. def has_grass_timestamp(self):
  337. """!Check if a grass file bsased time stamp exists for this map.
  338. """
  339. if G_has_raster3d_timestamp(self.get_name(), self.get_mapset()):
  340. return True
  341. else:
  342. return False
  343. def write_timestamp_to_grass(self):
  344. """!Write the timestamp of this map into the map metadata
  345. in the grass file system based spatial database.
  346. Internally the libgis API functions are used for writing
  347. """
  348. ts = libgis.TimeStamp()
  349. libgis.G_scan_timestamp(byref(ts), self._convert_timestamp())
  350. check = libgis.G_write_raster3d_timestamp(self.get_name(), byref(ts))
  351. if check == -1:
  352. core.error(_("Unable to create timestamp file "
  353. "for raster3d map <%s>" % (self.get_map_id())))
  354. if check == -2:
  355. core.error(_("Invalid datetime in timestamp "
  356. "for raster3d map <%s>" % (self.get_map_id())))
  357. def remove_timestamp_from_grass(self):
  358. """!Remove the timestamp from the grass file system based spatial database
  359. Internally the libgis API functions are used for removal
  360. """
  361. check = libgis.G_remove_raster3d_timestamp(self.get_name())
  362. if check == -1:
  363. core.error(_("Unable to remove timestamp for raster3d map <%s>" %
  364. (self.get_name())))
  365. def map_exists(self):
  366. """!Return True in case the map exists in the grass spatial database
  367. @return True if map exists, False otherwise
  368. """
  369. mapset = libgis.G_find_raster3d(self.get_name(), self.get_mapset())
  370. if not mapset:
  371. return False
  372. return True
  373. def read_info(self):
  374. """!Read the raster3d map info from the file system and store the content
  375. into a dictionary
  376. This method uses the ctypes interface to the gis and raster3d libraries
  377. to read the map metadata information
  378. """
  379. kvp = {}
  380. name = self.get_name()
  381. mapset = self.get_mapset()
  382. if not self.map_exists():
  383. core.fatal(_("Raster3d map <%s> not found" % name))
  384. # Read the region information
  385. region = libraster3d.RASTER3D_Region()
  386. libraster3d.Rast3d_read_region_map(name, mapset, byref(region))
  387. kvp["north"] = region.north
  388. kvp["south"] = region.south
  389. kvp["east"] = region.east
  390. kvp["west"] = region.west
  391. kvp["nsres"] = region.ns_res
  392. kvp["ewres"] = region.ew_res
  393. kvp["tbres"] = region.tb_res
  394. kvp["rows"] = region.cols
  395. kvp["cols"] = region.rows
  396. kvp["depths"] = region.depths
  397. kvp["top"] = region.top
  398. kvp["bottom"] = region.bottom
  399. # We need to open the map, this function returns a void pointer
  400. # but we may need the correct type which is RASTER3D_Map, hence
  401. # the casting
  402. g3map = cast(libraster3d.Rast3d_open_cell_old(name, mapset,
  403. libraster3d.RASTER3D_DEFAULT_WINDOW,
  404. libraster3d.RASTER3D_TILE_SAME_AS_FILE,
  405. libraster3d.RASTER3D_NO_CACHE),
  406. POINTER(libraster3d.RASTER3D_Map))
  407. if not g3map:
  408. core.fatal(_("Unable to open 3D raster map <%s>" % (name)))
  409. maptype = libraster3d.Rast3d_file_type_map(g3map)
  410. if maptype == libraster.DCELL_TYPE:
  411. kvp["datatype"] = "DCELL"
  412. elif maptype == libraster.FCELL_TYPE:
  413. kvp["datatype"] = "FCELL"
  414. # Read range
  415. min = libgis.DCELL()
  416. max = libgis.DCELL()
  417. ret = libraster3d.Rast3d_range_load(g3map)
  418. if not ret:
  419. core.fatal(_("Unable to load range of 3D raster map <%s>" %
  420. (name)))
  421. libraster3d.Rast3d_range_min_max(g3map, byref(min), byref(max))
  422. if min.value != min.value:
  423. kvp["min"] = None
  424. else:
  425. kvp["min"] = float(min.value)
  426. if max.value != max.value:
  427. kvp["max"] = None
  428. else:
  429. kvp["max"] = float(max.value)
  430. if not libraster3d.Rast3d_close(g3map):
  431. G_fatal_error(_("Unable to close 3D raster map <%s>" % (name)))
  432. return kvp
  433. def load(self):
  434. """!Load all info from an existing raster3d map into the internal structure"""
  435. # Fill base information
  436. self.base.set_creator(str(getpass.getuser()))
  437. # Fill spatial extent
  438. # Get the data from an existing 3D raster map
  439. if get_use_ctypes_map_access() == True:
  440. kvp = self.read_info()
  441. else:
  442. kvp = grass.raster3d_info(self.get_id())
  443. self.set_spatial_extent(north=kvp["north"], south=kvp["south"],
  444. east=kvp["east"], west=kvp["west"],
  445. top=kvp["top"], bottom=kvp["bottom"])
  446. # Fill metadata
  447. self.metadata.set_nsres(kvp["nsres"])
  448. self.metadata.set_ewres(kvp["ewres"])
  449. self.metadata.set_tbres(kvp["tbres"])
  450. self.metadata.set_datatype(kvp["datatype"])
  451. self.metadata.set_min(kvp["min"])
  452. self.metadata.set_max(kvp["max"])
  453. rows = int(kvp["rows"])
  454. cols = int(kvp["cols"])
  455. depths = int(kvp["depths"])
  456. ncells = cols * rows
  457. ncells = cols * rows * depths
  458. self.metadata.set_cols(cols)
  459. self.metadata.set_rows(rows)
  460. self.metadata.set_depths(depths)
  461. self.metadata.set_number_of_cells(ncells)
  462. ###############################################################################
  463. class VectorDataset(AbstractMapDataset):
  464. """!Vector dataset class
  465. This class provides functions to select, update, insert or delete vector
  466. map information and valid time stamps into the SQL temporal database.
  467. """
  468. def __init__(self, ident):
  469. AbstractMapDataset.__init__(self)
  470. self.reset(ident)
  471. def get_type(self):
  472. return "vector"
  473. def get_new_instance(self, ident):
  474. """!Return a new instance with the type of this class"""
  475. return VectorDataset(ident)
  476. def get_new_stds_instance(self, ident):
  477. """!Return a new space time dataset instance in which maps
  478. are stored with the type of this class"""
  479. return SpaceTimeVectorDataset(ident)
  480. def get_stds_register(self):
  481. """!Return the space time dataset register table name in
  482. which stds are listed in which this map is registered"""
  483. return self.metadata.get_stvds_register()
  484. def set_stds_register(self, name):
  485. """!Set the space time dataset register table name in
  486. which stds are listed in which this map is registered"""
  487. self.metadata.set_stvds_register(name)
  488. def get_layer(self):
  489. """!Return the layer"""
  490. return self.base.get_layer()
  491. def spatial_overlapping(self, dataset):
  492. """!Return True if the spatial extents 2d overlap"""
  493. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  494. def spatial_relation(self, dataset):
  495. """!Return the two dimensional spatial relation"""
  496. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  497. def reset(self, ident):
  498. """!Reset the internal structure and set the identifier"""
  499. self.base = VectorBase(ident=ident)
  500. self.absolute_time = VectorAbsoluteTime(ident=ident)
  501. self.relative_time = VectorRelativeTime(ident=ident)
  502. self.spatial_extent = VectorSpatialExtent(ident=ident)
  503. self.metadata = VectorMetadata(ident=ident)
  504. def has_grass_timestamp(self):
  505. """!Check if a grass file bsased time stamp exists for this map.
  506. """
  507. if G_has_vector_timestamp(self.get_name(), self.get_layer(),
  508. self.get_mapset()):
  509. return True
  510. else:
  511. return False
  512. def write_timestamp_to_grass(self):
  513. """!Write the timestamp of this map into the map metadata in
  514. the grass file system based spatial database.
  515. Internally the libgis API functions are used for writing
  516. """
  517. ts = libgis.TimeStamp()
  518. libgis.G_scan_timestamp(byref(ts), self._convert_timestamp())
  519. check = libgis.G_write_vector_timestamp(
  520. self.get_name(), self.get_layer(), byref(ts))
  521. if check == -1:
  522. core.error(_("Unable to create timestamp file "
  523. "for vector map <%s>" % (self.get_map_id())))
  524. if check == -2:
  525. core.error(_("Invalid datetime in timestamp for vector map <%s>" %
  526. (self.get_map_id())))
  527. def remove_timestamp_from_grass(self):
  528. """!Remove the timestamp from the grass file system based spatial
  529. database
  530. Internally the libgis API functions are used for removal
  531. """
  532. check = libgis.G_remove_vector_timestamp(
  533. self.get_name(), self.get_layer())
  534. if check == -1:
  535. core.error(_("Unable to remove timestamp for vector map <%s>" %
  536. (self.get_name())))
  537. def map_exists(self):
  538. """!Return True in case the map exists in the grass spatial database
  539. @return True if map exists, False otherwise
  540. """
  541. mapset = libgis.G_find_vector(self.get_name(), self.get_mapset())
  542. if not mapset:
  543. return False
  544. return True
  545. def read_info(self):
  546. """!Read the vector map info from the file system and store the content
  547. into a dictionary
  548. This method uses the ctypes interface to the vector libraries
  549. to read the map metadata information
  550. """
  551. kvp = {}
  552. name = self.get_name()
  553. mapset = self.get_mapset()
  554. if not self.map_exists():
  555. core.fatal(_("Vector map <%s> not found" % name))
  556. # The vector map structure
  557. Map = libvector.Map_info()
  558. # We open the maps always in topology mode first
  559. libvector.Vect_set_open_level(2)
  560. with_topo = True
  561. # Code lend from v.info main.c
  562. if libvector.Vect_open_old_head2(byref(Map), name, mapset, "1") < 2:
  563. # force level 1, open fully
  564. # NOTE: number of points, lines, boundaries, centroids,
  565. # faces, kernels is still available
  566. libvector.Vect_set_open_level(1) # no topology
  567. with_topo = False
  568. core.message(_("Open map without topology support"))
  569. if libvector.Vect_open_old2(byref(Map), name, mapset, "1") < 1:
  570. core.fatal(_("Unable to open vector map <%s>" %
  571. (libvector.Vect_get_full_name(byref(Map)))))
  572. # Release the vector spatial index memory when closed
  573. libvector.Vect_set_release_support(byref(Map))
  574. # Read the extent information
  575. bbox = libvector.bound_box()
  576. libvector.Vect_get_map_box(byref(Map), byref(bbox))
  577. kvp["north"] = bbox.N
  578. kvp["south"] = bbox.S
  579. kvp["east"] = bbox.E
  580. kvp["west"] = bbox.W
  581. kvp["top"] = bbox.T
  582. kvp["bottom"] = bbox.B
  583. kvp["map3d"] = bool(libvector.Vect_is_3d(byref(Map)))
  584. # Read number of features
  585. if with_topo:
  586. kvp["points"] = libvector.Vect_get_num_primitives(
  587. byref(Map), libvector.GV_POINT)
  588. kvp["lines"] = libvector.Vect_get_num_primitives(
  589. byref(Map), libvector.GV_LINE)
  590. kvp["boundaries"] = libvector.Vect_get_num_primitives(
  591. byref(Map), libvector.GV_BOUNDARY)
  592. kvp["centroids"] = libvector.Vect_get_num_primitives(
  593. byref(Map), libvector.GV_CENTROID)
  594. kvp["faces"] = libvector.Vect_get_num_primitives(
  595. byref(Map), libvector.GV_FACE)
  596. kvp["kernels"] = libvector.Vect_get_num_primitives(
  597. byref(Map), libvector.GV_KERNEL)
  598. # Summarize the primitives
  599. kvp["primitives"] = kvp["points"] + kvp["lines"] + \
  600. kvp["boundaries"] + kvp["centroids"]
  601. if kvp["map3d"]:
  602. kvp["primitives"] += kvp["faces"] + kvp["kernels"]
  603. # Read topology information
  604. kvp["nodes"] = libvector.Vect_get_num_nodes(byref(Map))
  605. kvp["areas"] = libvector.Vect_get_num_areas(byref(Map))
  606. kvp["islands"] = libvector.Vect_get_num_islands(byref(Map))
  607. kvp["holes"] = libvector.Vect_get_num_holes(byref(Map))
  608. kvp["volumes"] = libvector.Vect_get_num_primitives(
  609. byref(Map), libvector.GV_VOLUME)
  610. else:
  611. kvp["points"] = None
  612. kvp["lines"] = None
  613. kvp["boundaries"] = None
  614. kvp["centroids"] = None
  615. kvp["faces"] = None
  616. kvp["kernels"] = None
  617. kvp["primitives"] = None
  618. kvp["nodes"] = None
  619. kvp["areas"] = None
  620. kvp["islands"] = None
  621. kvp["holes"] = None
  622. kvp["volumes"] = None
  623. libvector.Vect_close(byref(Map))
  624. return kvp
  625. def load(self):
  626. """!Load all info from an existing vector map into the internal
  627. structure"""
  628. # Fill base information
  629. self.base.set_creator(str(getpass.getuser()))
  630. # Get the data from an existing vector map
  631. if get_use_ctypes_map_access() == True:
  632. kvp = self.read_info()
  633. else:
  634. kvp = grass.vector_info(self.get_map_id())
  635. # Fill spatial extent
  636. self.set_spatial_extent(north=kvp["north"], south=kvp["south"],
  637. east=kvp["east"], west=kvp["west"],
  638. top=kvp["top"], bottom=kvp["bottom"])
  639. # Fill metadata
  640. self.metadata.set_3d_info(kvp["map3d"])
  641. self.metadata.set_number_of_points(kvp["points"])
  642. self.metadata.set_number_of_lines(kvp["lines"])
  643. self.metadata.set_number_of_boundaries(kvp["boundaries"])
  644. self.metadata.set_number_of_centroids(kvp["centroids"])
  645. self.metadata.set_number_of_faces(kvp["faces"])
  646. self.metadata.set_number_of_kernels(kvp["kernels"])
  647. self.metadata.set_number_of_primitives(kvp["primitives"])
  648. self.metadata.set_number_of_nodes(kvp["nodes"])
  649. self.metadata.set_number_of_areas(kvp["areas"])
  650. self.metadata.set_number_of_islands(kvp["islands"])
  651. self.metadata.set_number_of_holes(kvp["holes"])
  652. self.metadata.set_number_of_volumes(kvp["volumes"])
  653. ###############################################################################
  654. class SpaceTimeRasterDataset(AbstractSpaceTimeDataset):
  655. """!Space time raster dataset class
  656. """
  657. def __init__(self, ident):
  658. AbstractSpaceTimeDataset.__init__(self, ident)
  659. def get_type(self):
  660. return "strds"
  661. def get_new_instance(self, ident):
  662. """!Return a new instance with the type of this class"""
  663. return SpaceTimeRasterDataset(ident)
  664. def get_new_map_instance(self, ident):
  665. """!Return a new instance of a map dataset which is associated "
  666. "with the type of this class"""
  667. return RasterDataset(ident)
  668. def get_map_register(self):
  669. """!Return the name of the map register table"""
  670. return self.metadata.get_raster_register()
  671. def set_map_register(self, name):
  672. """!Set the name of the map register table"""
  673. self.metadata.set_raster_register(name)
  674. def spatial_overlapping(self, dataset):
  675. """!Return True if the spatial extents 2d overlap"""
  676. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  677. def spatial_relation(self, dataset):
  678. """!Return the two dimensional spatial relation"""
  679. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  680. def reset(self, ident):
  681. """!Reset the internal structure and set the identifier"""
  682. self.base = STRDSBase(ident=ident)
  683. self.base.set_creator(str(getpass.getuser()))
  684. self.absolute_time = STRDSAbsoluteTime(ident=ident)
  685. self.relative_time = STRDSRelativeTime(ident=ident)
  686. self.spatial_extent = STRDSSpatialExtent(ident=ident)
  687. self.metadata = STRDSMetadata(ident=ident)
  688. ###############################################################################
  689. class SpaceTimeRaster3DDataset(AbstractSpaceTimeDataset):
  690. """!Space time raster3d dataset class
  691. """
  692. def __init__(self, ident):
  693. AbstractSpaceTimeDataset.__init__(self, ident)
  694. def get_type(self):
  695. return "str3ds"
  696. def get_new_instance(self, ident):
  697. """!Return a new instance with the type of this class"""
  698. return SpaceTimeRaster3DDataset(ident)
  699. def get_new_map_instance(self, ident):
  700. """!Return a new instance of a map dataset which is associated
  701. with the type of this class"""
  702. return Raster3DDataset(ident)
  703. def get_map_register(self):
  704. """!Return the name of the map register table"""
  705. return self.metadata.get_raster3d_register()
  706. def set_map_register(self, name):
  707. """!Set the name of the map register table"""
  708. self.metadata.set_raster3d_register(name)
  709. def spatial_overlapping(self, dataset):
  710. """!Return True if the spatial extents overlap"""
  711. if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
  712. return self.spatial_extent.overlapping(dataset.spatial_extent)
  713. else:
  714. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  715. def spatial_relation(self, dataset):
  716. """!Return the two or three dimensional spatial relation"""
  717. if self.get_type() == dataset.get_type() or \
  718. dataset.get_type() == "str3ds":
  719. return self.spatial_extent.spatial_relation(dataset.spatial_extent)
  720. else:
  721. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  722. def reset(self, ident):
  723. """!Reset the internal structure and set the identifier"""
  724. self.base = STR3DSBase(ident=ident)
  725. self.base.set_creator(str(getpass.getuser()))
  726. self.absolute_time = STR3DSAbsoluteTime(ident=ident)
  727. self.relative_time = STR3DSRelativeTime(ident=ident)
  728. self.spatial_extent = STR3DSSpatialExtent(ident=ident)
  729. self.metadata = STR3DSMetadata(ident=ident)
  730. ###############################################################################
  731. class SpaceTimeVectorDataset(AbstractSpaceTimeDataset):
  732. """!Space time vector dataset class
  733. """
  734. def __init__(self, ident):
  735. AbstractSpaceTimeDataset.__init__(self, ident)
  736. def get_type(self):
  737. return "stvds"
  738. def get_new_instance(self, ident):
  739. """!Return a new instance with the type of this class"""
  740. return SpaceTimeVectorDataset(ident)
  741. def get_new_map_instance(self, ident):
  742. """!Return a new instance of a map dataset which is associated
  743. with the type of this class"""
  744. return VectorDataset(ident)
  745. def get_map_register(self):
  746. """!Return the name of the map register table"""
  747. return self.metadata.get_vector_register()
  748. def set_map_register(self, name):
  749. """!Set the name of the map register table"""
  750. self.metadata.set_vector_register(name)
  751. def spatial_overlapping(self, dataset):
  752. """!Return True if the spatial extents 2d overlap"""
  753. return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
  754. def spatial_relation(self, dataset):
  755. """!Return the two dimensional spatial relation"""
  756. return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent)
  757. def reset(self, ident):
  758. """!Reset the internal structure and set the identifier"""
  759. self.base = STVDSBase(ident=ident)
  760. self.base.set_creator(str(getpass.getuser()))
  761. self.absolute_time = STVDSAbsoluteTime(ident=ident)
  762. self.relative_time = STVDSRelativeTime(ident=ident)
  763. self.spatial_extent = STVDSSpatialExtent(ident=ident)
  764. self.metadata = STVDSMetadata(ident=ident)
  765. ###############################################################################
  766. if __name__ == "__main__":
  767. import doctest
  768. doctest.testmod()