space_time_datasets.py 37 KB

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