space_time_datasets.py 36 KB

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