abstract.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. """
  2. Created on Fri Aug 17 17:24:03 2012
  3. @author: pietro
  4. """
  5. import ctypes
  6. import datetime
  7. import grass.lib.vector as libvect
  8. from grass.pygrass.vector.vector_type import MAPTYPE
  9. from grass.pygrass import utils
  10. from grass.pygrass.errors import must_be_open
  11. from grass.pygrass.vector.table import DBlinks, Link
  12. from grass.pygrass.vector.find import PointFinder, BboxFinder, PolygonFinder
  13. from grass.exceptions import GrassError, OpenError
  14. test_vector_name = "abstract_doctest_map"
  15. def is_open(c_mapinfo):
  16. """Return if the Vector is open"""
  17. return (
  18. c_mapinfo.contents.open != 0
  19. and c_mapinfo.contents.open != libvect.VECT_CLOSED_CODE
  20. )
  21. # =============================================
  22. # VECTOR ABSTRACT CLASS
  23. # =============================================
  24. class Info(object):
  25. """Basic vector info.
  26. To get access to the vector info the map must be opened. ::
  27. >>> test_vect = Info(test_vector_name)
  28. >>> test_vect.open(mode='r')
  29. Then it is possible to read and write the following map attributes: ::
  30. >>> test_vect.organization
  31. 'Thuenen Institut'
  32. >>> test_vect.person
  33. 'Soeren Gebbert'
  34. >>> test_vect.title
  35. 'Test dataset'
  36. >>> test_vect.scale
  37. 1
  38. >>> test_vect.comment
  39. 'This is a comment'
  40. >>> test_vect.comment = "One useful comment!"
  41. >>> test_vect.comment
  42. 'One useful comment!'
  43. There are some read only attributes: ::
  44. >>> test_vect.maptype
  45. 'native'
  46. And some basic methods: ::
  47. >>> test_vect.is_3D()
  48. False
  49. >>> test_vect.exist()
  50. True
  51. >>> test_vect.is_open()
  52. True
  53. >>> test_vect.close()
  54. """
  55. def __init__(self, name, mapset="", *aopen, **kwopen):
  56. self._name = ""
  57. self._mapset = ""
  58. # Set map name and mapset
  59. self.name = name
  60. self.mapset = mapset
  61. self._aopen = aopen
  62. self._kwopen = kwopen
  63. self.c_mapinfo = ctypes.pointer(libvect.Map_info())
  64. self._topo_level = 1
  65. self._class_name = "Vector"
  66. self._mode = "r"
  67. self.overwrite = False
  68. self.date_fmt = "%a %b %d %H:%M:%S %Y"
  69. def __enter__(self):
  70. self.open(*self._aopen, **self._kwopen)
  71. return self
  72. def __exit__(self, exc_type, exc_value, traceback):
  73. self.close()
  74. def _get_mode(self):
  75. return self._mode
  76. def _set_mode(self, mode):
  77. if mode.upper() not in "RW":
  78. str_err = _("Mode type: {0} not supported ('r', 'w')")
  79. raise ValueError(str_err.format(mode))
  80. self._mode = mode
  81. mode = property(fget=_get_mode, fset=_set_mode)
  82. def _get_name(self):
  83. """Private method to obtain the Vector name"""
  84. return self._name
  85. def _set_name(self, newname):
  86. """Private method to change the Vector name"""
  87. if not utils.is_clean_name(newname):
  88. str_err = _("Map name {0} not valid")
  89. raise ValueError(str_err.format(newname))
  90. self._name = newname
  91. name = property(fget=_get_name, fset=_set_name, doc="Set or obtain the Vector name")
  92. def _get_mapset(self):
  93. """Private method to obtain the Vector mapset"""
  94. return self._mapset
  95. def _set_mapset(self, mapset):
  96. """Private method to change the Vector mapset"""
  97. if mapset:
  98. self._mapset = mapset
  99. mapset = property(
  100. fget=_get_mapset, fset=_set_mapset, doc="Set or obtain the Vector mapset"
  101. )
  102. def _get_organization(self):
  103. """Private method to obtain the Vector organization"""
  104. return utils.decode(libvect.Vect_get_organization(self.c_mapinfo))
  105. def _set_organization(self, org):
  106. """Private method to change the Vector organization"""
  107. libvect.Vect_set_organization(self.c_mapinfo, org)
  108. organization = property(
  109. fget=_get_organization,
  110. fset=_set_organization,
  111. doc="Set or obtain the Vector organization",
  112. )
  113. def _get_date(self):
  114. """Private method to obtain the Vector date"""
  115. return utils.decode(libvect.Vect_get_date(self.c_mapinfo))
  116. def _set_date(self, date):
  117. """Private method to change the Vector date"""
  118. return libvect.Vect_set_date(self.c_mapinfo, date)
  119. date = property(fget=_get_date, fset=_set_date, doc="Set or obtain the Vector date")
  120. def _get_person(self):
  121. """Private method to obtain the Vector person"""
  122. return utils.decode(libvect.Vect_get_person(self.c_mapinfo))
  123. def _set_person(self, person):
  124. """Private method to change the Vector person"""
  125. libvect.Vect_set_person(self.c_mapinfo, person)
  126. person = property(
  127. fget=_get_person, fset=_set_person, doc="Set or obtain the Vector author"
  128. )
  129. def _get_title(self):
  130. """Private method to obtain the Vector title"""
  131. return utils.decode(libvect.Vect_get_map_name(self.c_mapinfo))
  132. def _set_title(self, title):
  133. """Private method to change the Vector title"""
  134. libvect.Vect_set_map_name(self.c_mapinfo, title)
  135. title = property(
  136. fget=_get_title, fset=_set_title, doc="Set or obtain the Vector title"
  137. )
  138. def _get_map_date(self):
  139. """Private method to obtain the Vector map date"""
  140. date_str = utils.decode(libvect.Vect_get_map_date(self.c_mapinfo))
  141. try:
  142. return datetime.datetime.strptime(date_str, self.date_fmt)
  143. except ValueError:
  144. return date_str
  145. def _set_map_date(self, datetimeobj):
  146. """Private method to change the Vector map date"""
  147. date_str = datetimeobj.strftime(self.date_fmt)
  148. libvect.Vect_set_map_date(self.c_mapinfo, date_str)
  149. map_date = property(
  150. fget=_get_map_date, fset=_set_map_date, doc="Set or obtain the Vector map date"
  151. )
  152. def _get_scale(self):
  153. """Private method to obtain the Vector scale"""
  154. return libvect.Vect_get_scale(self.c_mapinfo)
  155. def _set_scale(self, scale):
  156. """Private method to set the Vector scale"""
  157. return libvect.Vect_set_scale(self.c_mapinfo, ctypes.c_int(scale))
  158. scale = property(
  159. fget=_get_scale, fset=_set_scale, doc="Set or obtain the Vector scale"
  160. )
  161. def _get_comment(self):
  162. """Private method to obtain the Vector comment"""
  163. return utils.decode(libvect.Vect_get_comment(self.c_mapinfo))
  164. def _set_comment(self, comm):
  165. """Private method to set the Vector comment"""
  166. return libvect.Vect_set_comment(self.c_mapinfo, comm)
  167. comment = property(
  168. fget=_get_comment, fset=_set_comment, doc="Set or obtain the Vector comment"
  169. )
  170. def _get_zone(self):
  171. """Private method to obtain the Vector projection zone"""
  172. return libvect.Vect_get_zone(self.c_mapinfo)
  173. def _set_zone(self, zone):
  174. """Private method to set the Vector projection zone"""
  175. return libvect.Vect_set_zone(self.c_mapinfo, ctypes.c_int(zone))
  176. zone = property(
  177. fget=_get_zone, fset=_set_zone, doc="Set or obtain the Vector projection zone"
  178. )
  179. def _get_proj(self):
  180. """Private method to obtain the Vector projection code"""
  181. return libvect.Vect_get_proj(self.c_mapinfo)
  182. def _set_proj(self, proj):
  183. """Private method to set the Vector projection code"""
  184. libvect.Vect_set_proj(self.c_mapinfo, ctypes.c_int(proj))
  185. proj = property(
  186. fget=_get_proj, fset=_set_proj, doc="Set or obtain the Vector projection code"
  187. )
  188. def _get_thresh(self):
  189. """Private method to obtain the Vector threshold"""
  190. return libvect.Vect_get_thresh(self.c_mapinfo)
  191. def _set_thresh(self, thresh):
  192. """Private method to set the Vector threshold"""
  193. return libvect.Vect_set_thresh(self.c_mapinfo, ctypes.c_double(thresh))
  194. thresh = property(
  195. fget=_get_thresh, fset=_set_thresh, doc="Set or obtain the Vector threshold"
  196. )
  197. @property
  198. @must_be_open
  199. def full_name(self):
  200. """Return the full name of Vector"""
  201. return libvect.Vect_get_full_name(self.c_mapinfo)
  202. @property
  203. @must_be_open
  204. def maptype(self):
  205. """Return the map type of Vector"""
  206. return MAPTYPE[libvect.Vect_maptype(self.c_mapinfo)]
  207. @property
  208. @must_be_open
  209. def proj_name(self):
  210. """Return the project name of Vector"""
  211. return libvect.Vect_get_proj_name(self.c_mapinfo)
  212. def write_header(self):
  213. """Save the change in the C struct permanently to disk."""
  214. libvect.Vect_write_header(self.c_mapinfo)
  215. def rename(self, newname):
  216. """Method to rename the Vector map
  217. :param newname: the new name for the Vector map
  218. :type newname: str
  219. """
  220. if self.exist():
  221. if not self.is_open():
  222. utils.rename(self.name, newname, "vect")
  223. else:
  224. raise GrassError("The map is open, not able to renamed it.")
  225. self._name = newname
  226. def is_3D(self):
  227. """Return if the Vector is 3D"""
  228. return bool(libvect.Vect_is_3d(self.c_mapinfo))
  229. def exist(self):
  230. """Return if the Vector exists or not"""
  231. if self.name:
  232. if self.mapset == "":
  233. mapset = utils.get_mapset_vector(self.name, self.mapset)
  234. self.mapset = mapset if mapset else ""
  235. return True if mapset else False
  236. return bool(utils.get_mapset_vector(self.name, self.mapset))
  237. else:
  238. return False
  239. def is_open(self):
  240. """Return if the Vector is open"""
  241. return is_open(self.c_mapinfo)
  242. def open(
  243. self,
  244. mode=None,
  245. layer=1,
  246. overwrite=None,
  247. with_z=None,
  248. # parameters valid only if mode == 'w'
  249. tab_name="",
  250. tab_cols=None,
  251. link_name=None,
  252. link_key="cat",
  253. link_db="$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db",
  254. link_driver="sqlite",
  255. ):
  256. """Open a Vector map.
  257. :param mode: open a vector map in ``r`` in reading, ``w`` in writing
  258. and in ``rw`` read and write mode
  259. :type mode: str
  260. :param layer: specify the layer that you want to use
  261. :type layer: int
  262. :param overwrite: valid only for ``w`` mode
  263. :type overwrite: bool
  264. :param with_z: specify if vector map must be open with third dimension
  265. enabled or not. Valid only for ``w`` mode,
  266. default: False
  267. :type with_z: bool
  268. :param tab_name: define the name of the table that will be generate
  269. :type tab_name: str
  270. :param tab_cols: define the name and type of the columns of the
  271. attribute table of the vecto map
  272. :type tab_cols: list of pairs
  273. :param link_name: define the name of the link connecttion with the
  274. database
  275. :type link_name: str
  276. :param link_key: define the nema of the column that will be use as
  277. vector category
  278. :type link_key: str
  279. :param link_db: define the database connection parameters
  280. :type link_db: str
  281. :param link_driver: define witch database driver will be used
  282. :param link_driver: str
  283. Some of the parameters are valid only with mode ``w`` or ``rw``
  284. See more examples in the documentation of the ``read`` and ``write``
  285. methods
  286. """
  287. self.mode = mode if mode else self.mode
  288. with_z = libvect.WITH_Z if with_z else libvect.WITHOUT_Z
  289. # check if map exists or not
  290. if not self.exist() and self.mode != "w":
  291. raise OpenError("Map <%s> not found." % self._name)
  292. if libvect.Vect_set_open_level(self._topo_level) != 0:
  293. raise OpenError("Invalid access level.")
  294. # update the overwrite attribute
  295. self.overwrite = overwrite if overwrite is not None else self.overwrite
  296. # check if the mode is valid
  297. if self.mode not in ("r", "rw", "w"):
  298. raise ValueError("Mode not supported. Use one of: 'r', 'rw', 'w'.")
  299. # check if the map exist
  300. if self.exist() and self.mode in ("r", "rw"):
  301. # open in READ mode
  302. if self.mode == "r":
  303. openvect = libvect.Vect_open_old2(
  304. self.c_mapinfo, self.name, self.mapset, str(layer)
  305. )
  306. # open in READ and WRITE mode
  307. elif self.mode == "rw":
  308. openvect = libvect.Vect_open_update2(
  309. self.c_mapinfo, self.name, self.mapset, str(layer)
  310. )
  311. # instantiate class attributes
  312. self.dblinks = DBlinks(self.c_mapinfo)
  313. # If it is opened in write mode
  314. if self.mode == "w":
  315. openvect = libvect.Vect_open_new(self.c_mapinfo, self.name, with_z)
  316. self.dblinks = DBlinks(self.c_mapinfo)
  317. if self.mode in ("w", "rw") and tab_cols:
  318. # create a link
  319. link = Link(
  320. layer,
  321. link_name if link_name else self.name,
  322. tab_name if tab_name else self.name,
  323. link_key,
  324. link_db,
  325. link_driver,
  326. )
  327. # add the new link
  328. self.dblinks.add(link)
  329. # create the table
  330. table = link.table()
  331. table.create(tab_cols, overwrite=overwrite)
  332. table.conn.commit()
  333. # check the C function result.
  334. if openvect == -1:
  335. str_err = "Not able to open the map, C function return %d."
  336. raise OpenError(str_err % openvect)
  337. # Load attribute table for selected layer.
  338. if len(self.dblinks) == 0:
  339. self.layer = layer
  340. self.table = None
  341. self.n_lines = 0
  342. else:
  343. layer_db_link = self.dblinks.by_layer(layer)
  344. if not layer_db_link:
  345. raise LookupError(
  346. "There appears to be no database link for layer %d of <%s>."
  347. % (layer, self.name)
  348. )
  349. if layer_db_link.layer != layer:
  350. raise RuntimeError(
  351. "The databse link for layer %d of <%s> references layer %d."
  352. % (layer, self.name, layer_db_link.layer)
  353. )
  354. self.layer = layer
  355. try:
  356. self.table = layer_db_link.table()
  357. except Exception as error:
  358. raise RuntimeError(
  359. "Loading the attribute table for layer %d of <%s> failed."
  360. % (layer, self.name)
  361. ) from error
  362. self.n_lines = self.table.n_rows()
  363. self.writeable = self.mapset == utils.getenv("MAPSET")
  364. # Initialize the finder
  365. self.find = {
  366. "by_point": PointFinder(self.c_mapinfo, self.table, self.writeable),
  367. "by_bbox": BboxFinder(self.c_mapinfo, self.table, self.writeable),
  368. "by_polygon": PolygonFinder(self.c_mapinfo, self.table, self.writeable),
  369. }
  370. self.find_by_point = self.find["by_point"]
  371. self.find_by_bbox = self.find["by_bbox"]
  372. self.find_by_polygon = self.find["by_polygon"]
  373. def close(self, build=False):
  374. """Method to close the Vector
  375. :param build: True if the vector map should be build before close it
  376. :type build: bool
  377. """
  378. if hasattr(self, "table") and self.table is not None:
  379. self.table.conn.close()
  380. if self.is_open():
  381. if libvect.Vect_close(self.c_mapinfo) != 0:
  382. str_err = "Error when trying to close the map with Vect_close"
  383. raise GrassError(str_err)
  384. if (
  385. self.c_mapinfo.contents.mode == libvect.GV_MODE_RW
  386. or self.c_mapinfo.contents.mode == libvect.GV_MODE_WRITE
  387. ) and build:
  388. self.build()
  389. def remove(self):
  390. """Remove vector map"""
  391. if self.is_open():
  392. self.close()
  393. utils.remove(self.name, "vect")
  394. def build(self):
  395. """Close the vector map and build vector Topology"""
  396. self.close()
  397. libvect.Vect_set_open_level(1)
  398. if libvect.Vect_open_old2(self.c_mapinfo, self.name, self.mapset, "0") != 1:
  399. str_err = "Error when trying to open the vector map."
  400. raise GrassError(str_err)
  401. # Vect_build returns 1 on success and 0 on error (bool approach)
  402. if libvect.Vect_build(self.c_mapinfo) != 1:
  403. str_err = "Error when trying build topology with Vect_build"
  404. raise GrassError(str_err)
  405. libvect.Vect_close(self.c_mapinfo)
  406. if __name__ == "__main__":
  407. import doctest
  408. utils.create_test_vector_map(test_vector_name)
  409. doctest.testmod()