space_time_datasets.py 35 KB

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