space_time_datasets.py 42 KB

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