geometry.py 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Wed Jul 18 10:46:25 2012
  4. @author: pietro
  5. """
  6. import ctypes
  7. import re
  8. import numpy as np
  9. import grass.lib.gis as libgis
  10. import grass.lib.vector as libvect
  11. from grass.pygrass.errors import GrassError
  12. from .basic import Ilist, Bbox, Cats
  13. from . import sql
  14. WKT = {'POINT\((.*)\)': 'point', # 'POINT\(\s*([+-]*\d+\.*\d*)+\s*\)'
  15. 'LINESTRING\((.*)\)': 'line'}
  16. def read_WKT(string):
  17. """Read the string and return a geometry object
  18. WKT:
  19. POINT(0 0)
  20. LINESTRING(0 0,1 1,1 2)
  21. POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
  22. MULTIPOINT(0 0,1 2)
  23. MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))
  24. MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)),
  25. ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))
  26. GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))
  27. EWKT:
  28. POINT(0 0 0) -- XYZ
  29. SRID=32632;POINT(0 0) -- XY with SRID
  30. POINTM(0 0 0) -- XYM
  31. POINT(0 0 0 0) -- XYZM
  32. SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- XYM with SRID
  33. MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1))
  34. POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0))
  35. MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),
  36. (1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)),
  37. ((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0)))
  38. GEOMETRYCOLLECTIONM( POINTM(2 3 9), LINESTRINGM(2 3 4, 3 4 5) )
  39. MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4) )
  40. POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),
  41. ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),
  42. ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),
  43. ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)),
  44. ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)),
  45. ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )
  46. TRIANGLE ((0 0, 0 9, 9 0, 0 0))
  47. TIN( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )
  48. """
  49. for regexp, obj in WKT.items():
  50. if re.match(regexp, string):
  51. geo = 10
  52. return obj(geo)
  53. def read_WKB(buff):
  54. """Read the binary buffer and return a geometry object"""
  55. pass
  56. def intersects(lineA, lineB, with_z=False):
  57. """Return a list of points ::
  58. >>> lineA = Line([(0, 0), (4, 0)])
  59. >>> lineB = Line([(2, 2), (2, -2)])
  60. >>> intersects(lineA, lineB)
  61. Line([Point(2.000000, 0.000000)])
  62. """
  63. line = Line()
  64. if libvect.Vect_line_get_intersections(lineA.c_points, lineB.c_points,
  65. line.c_points, int(with_z)):
  66. return line
  67. else:
  68. return []
  69. #=============================================
  70. # GEOMETRY
  71. #=============================================
  72. def get_xyz(pnt):
  73. """Return a tuple with: x, y, z. ::
  74. >>> pnt = Point(0, 0)
  75. >>> get_xyz(pnt)
  76. (0.0, 0.0, 0.0)
  77. >>> get_xyz((1, 1))
  78. (1, 1, 0.0)
  79. >>> get_xyz((1, 1, 2))
  80. (1, 1, 2)
  81. >>> get_xyz((1, 1, 2, 2)) #doctest: +ELLIPSIS
  82. Traceback (most recent call last):
  83. ...
  84. ValueError: The the format of the point is not supported: (1, 1, 2, 2)
  85. ..
  86. """
  87. if isinstance(pnt, Point):
  88. if pnt.is2D:
  89. x, y = pnt.x, pnt.y
  90. z = 0.
  91. else:
  92. x, y, z = pnt.x, pnt.y, pnt.z
  93. else:
  94. if len(pnt) == 2:
  95. x, y = pnt
  96. z = 0.
  97. elif len(pnt) == 3:
  98. x, y, z = pnt
  99. else:
  100. str_error = "The the format of the point is not supported: {0!r}"
  101. raise ValueError(str_error.format(pnt))
  102. return x, y, z
  103. class Attrs(object):
  104. def __init__(self, cat, table, writable=False):
  105. self._cat = None
  106. self.cond = ''
  107. self.table = table
  108. self.cat = cat
  109. self.writable = writable
  110. def _get_cat(self):
  111. return self._cat
  112. def _set_cat(self, value):
  113. self._cat = value
  114. if value:
  115. # update condition
  116. self.cond = "%s=%d" % (self.table.key, value)
  117. cat = property(fget=_get_cat, fset=_set_cat)
  118. def __getitem__(self, key):
  119. """Return the value stored in the attribute table. ::
  120. >>> from grass.pygrass.vector import VectorTopo
  121. >>> schools = VectorTopo('schools')
  122. >>> schools.open('r')
  123. >>> school = schools[1]
  124. >>> attrs = Attrs(school.cat, schools.table)
  125. >>> attrs['TAG']
  126. u'568'
  127. """
  128. #SELECT {cols} FROM {tname} WHERE {condition};
  129. try:
  130. cur = self.table.execute(sql.SELECT_WHERE.format(cols=key,
  131. tname=self.table.name,
  132. condition=self.cond))
  133. except:
  134. import ipdb; ipdb.set_trace()
  135. results = cur.fetchone()
  136. if results is not None:
  137. return results[0] if len(results) == 1 else results
  138. def __setitem__(self, key, value):
  139. """Set value of a given column of a table attribute. ::
  140. >>> from grass.pygrass.vector import VectorTopo
  141. >>> from grass.pygrass.functions import copy, remove
  142. >>> copy('schools', 'myschools', 'vect')
  143. >>> schools = VectorTopo('myschools')
  144. >>> schools.open('r')
  145. >>> school = schools[1]
  146. >>> attrs = Attrs(school.cat, schools.table, True)
  147. >>> attrs['TAG'] = 'New Label'
  148. >>> attrs['TAG']
  149. u'New Label'
  150. >>> attrs.table.conn.close()
  151. >>> remove('myschools','vect')
  152. """
  153. if self.writable:
  154. #UPDATE {tname} SET {new_col} = {old_col} WHERE {condition}
  155. values = '%s=%r' % (key, value)
  156. self.table.execute(sql.UPDATE_WHERE.format(tname=self.table.name,
  157. values=values,
  158. condition=self.cond))
  159. #self.table.conn.commit()
  160. else:
  161. str_err = "You can only read the attributes if the map is \
  162. in another mapset"
  163. raise GrassError(str_err)
  164. def __dict__(self):
  165. """Return a dict of the attribute table row."""
  166. dic = {}
  167. for key, val in zip(self.keys(), self.values()):
  168. dic[key] = val
  169. return dic
  170. def values(self):
  171. """Return the values of the attribute table row.
  172. >>> from grass.pygrass.vector import VectorTopo
  173. >>> schools = VectorTopo('schools')
  174. >>> schools.open('r')
  175. >>> school = schools[1]
  176. >>> attrs = Attrs(school.cat, schools.table)
  177. >>> attrs.values() # doctest: +ELLIPSIS
  178. (1,
  179. ...
  180. None)
  181. """
  182. #SELECT {cols} FROM {tname} WHERE {condition}
  183. cur = self.table.execute(sql.SELECT_WHERE.format(cols='*',
  184. tname=self.table.name,
  185. condition=self.cond))
  186. return cur.fetchone()
  187. def keys(self):
  188. """Return the column name of the attribute table.
  189. >>> from grass.pygrass.vector import VectorTopo
  190. >>> schools = VectorTopo('schools')
  191. >>> schools.open('r')
  192. >>> school = schools[1]
  193. >>> attrs = Attrs(school.cat, schools.table)
  194. >>> attrs.keys() # doctest: +ELLIPSIS
  195. (u'cat',
  196. ...
  197. u'NOTES')
  198. """
  199. return self.table.columns.names()
  200. def commit(self):
  201. """Save the changes"""
  202. self.table.conn.commit()
  203. class Geo(object):
  204. """
  205. >>> geo0 = Geo()
  206. >>> points = ctypes.pointer(libvect.line_pnts())
  207. >>> cats = ctypes.pointer(libvect.line_cats())
  208. >>> geo1 = Geo(c_points=points, c_cats=cats)
  209. """
  210. gtype = None
  211. def __init__(self, v_id=None, c_mapinfo=None, c_points=None, c_cats=None,
  212. table=None, writable=False, is2D=True):
  213. self.id = v_id # vector id
  214. self.c_mapinfo = c_mapinfo
  215. self.is2D = is2D
  216. read = False
  217. # set c_points
  218. if c_points is None:
  219. self.c_points = ctypes.pointer(libvect.line_pnts())
  220. read = True
  221. else:
  222. self.c_points = c_points
  223. # set c_cats
  224. if c_cats is None:
  225. self.c_cats = ctypes.pointer(libvect.line_cats())
  226. read = True
  227. else:
  228. self.c_cats = c_cats
  229. # set the attributes
  230. self.attrs = None
  231. if table is not None:
  232. self.attrs = Attrs(self.cat, table, writable)
  233. if self.id is not None and self.c_mapinfo is not None and read:
  234. self.read()
  235. @property
  236. def cat(self):
  237. if self.c_cats.contents.cat:
  238. return self.c_cats.contents.cat.contents.value
  239. def is_with_topology(self):
  240. if self.c_mapinfo is not None:
  241. return self.c_mapinfo.contents.level == 2
  242. else:
  243. return False
  244. def read(self):
  245. """Read and set the coordinates of the centroid from the vector map,
  246. using the centroid_id and calling the Vect_read_line C function"""
  247. ftype, c_points, c_cats = c_read_line(self.id, self.c_mapinfo,
  248. self.c_points, self.c_cats)
  249. class Point(Geo):
  250. """Instantiate a Point object that could be 2 or 3D, default
  251. parameters are 0.
  252. ::
  253. >>> pnt = Point()
  254. >>> pnt.x
  255. 0.0
  256. >>> pnt.y
  257. 0.0
  258. >>> pnt.z
  259. >>> pnt.is2D
  260. True
  261. >>> pnt
  262. Point(0.000000, 0.000000)
  263. >>> pnt.z = 0
  264. >>> pnt.is2D
  265. False
  266. >>> pnt
  267. Point(0.000000, 0.000000, 0.000000)
  268. >>> print(pnt)
  269. POINT(0.000000, 0.000000, 0.000000)
  270. ..
  271. """
  272. # geometry type
  273. gtype = libvect.GV_POINT
  274. def __init__(self, x=0, y=0, z=None, **kargs):
  275. super(Point, self).__init__(**kargs)
  276. if self.id is not None:
  277. self.read()
  278. else:
  279. self.is2D = True if z is None else False
  280. z = z if z is not None else 0
  281. libvect.Vect_append_point(self.c_points, x, y, z)
  282. def _get_x(self):
  283. return self.c_points.contents.x[0]
  284. def _set_x(self, value):
  285. self.c_points.contents.x[0] = value
  286. x = property(fget=_get_x, fset=_set_x)
  287. def _get_y(self):
  288. return self.c_points.contents.y[0]
  289. def _set_y(self, value):
  290. self.c_points.contents.y[0] = value
  291. y = property(fget=_get_y, fset=_set_y)
  292. def _get_z(self):
  293. if self.is2D:
  294. return None
  295. return self.c_points.contents.z[0]
  296. def _set_z(self, value):
  297. if value is None:
  298. self.is2D = True
  299. self.c_points.contents.z[0] = 0
  300. else:
  301. self.c_points.contents.z[0] = value
  302. self.is2D = False
  303. z = property(fget=_get_z, fset=_set_z)
  304. def __str__(self):
  305. return self.get_wkt()
  306. def __repr__(self):
  307. return "Point(%s)" % ', '.join(['%f' % coor for coor in self.coords()])
  308. def __eq__(self, pnt):
  309. if isinstance(pnt, Point):
  310. return pnt.coords() == self.coords()
  311. return Point(*pnt).coords() == self.coords()
  312. def coords(self):
  313. """Return a tuple with the point coordinates. ::
  314. >>> pnt = Point(10, 100)
  315. >>> pnt.coords()
  316. (10.0, 100.0)
  317. If the point is 2D return a x, y tuple. But if we change the ``z``
  318. the Point object become a 3D point, therefore the method return a
  319. x, y, z tuple. ::
  320. >>> pnt.z = 1000.
  321. >>> pnt.coords()
  322. (10.0, 100.0, 1000.0)
  323. ..
  324. """
  325. if self.is2D:
  326. return self.x, self.y
  327. else:
  328. return self.x, self.y, self.z
  329. def get_wkt(self):
  330. """Return a "well know text" (WKT) geometry string. ::
  331. >>> pnt = Point(10, 100)
  332. >>> pnt.get_wkt()
  333. 'POINT(10.000000 100.000000)'
  334. .. warning::
  335. Only ``POINT`` (2/3D) are supported, ``POINTM`` and ``POINT`` with:
  336. ``XYZM`` are not supported yet.
  337. """
  338. return "POINT(%s)" % ' '.join(['%f' % coord
  339. for coord in self.coords()])
  340. def get_wkb(self):
  341. """Return a "well know binary" (WKB) geometry buffer
  342. .. warning::
  343. Not implemented yet.
  344. """
  345. pass
  346. def distance(self, pnt):
  347. """Calculate distance of 2 points, using the Vect_points_distance
  348. C function, If one of the point have z == None, return the 2D distance.
  349. ::
  350. >>> pnt0 = Point(0, 0, 0)
  351. >>> pnt1 = Point(1, 0)
  352. >>> pnt0.distance(pnt1)
  353. 1.0
  354. >>> pnt1.z = 1
  355. >>> pnt1
  356. Point(1.000000, 0.000000, 1.000000)
  357. >>> pnt0.distance(pnt1)
  358. 1.4142135623730951
  359. The distance method require a :class:Point or a tuple with
  360. the coordinates.
  361. """
  362. if self.is2D or pnt.is2D:
  363. return libvect.Vect_points_distance(self.x, self.y, 0,
  364. pnt.x, pnt.y, 0, 0)
  365. else:
  366. return libvect.Vect_points_distance(self.x, self.y, self.z,
  367. pnt.x, pnt.y, pnt.z, 1)
  368. def buffer(self, dist=None, dist_x=None, dist_y=None, angle=0,
  369. round_=True, tol=0.1):
  370. """Return the buffer area around the point, using the
  371. ``Vect_point_buffer2`` C function.
  372. Parameters
  373. ----------
  374. dist : numeric
  375. The distance around the line.
  376. dist_x: numeric, optional
  377. The distance along x
  378. dist_y: numeric, optional
  379. The distance along y
  380. angle: numeric, optional
  381. The angle between 0x and major axis
  382. round_: bool, optional
  383. To make corners round
  384. tol: float, optional
  385. Fix the maximum distance between theoretical arc
  386. and output segments
  387. Returns
  388. -------
  389. buffer : Area
  390. The buffer area around the line.
  391. Example
  392. ---------
  393. ::
  394. >>> pnt = Point(0, 0)
  395. >>> area = pnt.buffer(10)
  396. >>> area.boundary #doctest: +ELLIPSIS
  397. Line([Point(10.000000, 0.000000),...Point(10.000000, 0.000000)])
  398. >>> area.centroid
  399. Point(0.000000, 0.000000)
  400. >>> area.isles
  401. []
  402. """
  403. if dist is not None:
  404. dist_x = dist
  405. dist_y = dist
  406. elif not dist_x or not dist_y:
  407. raise TypeError('TypeError: buffer expected 1 arguments, got 0')
  408. bound = Line()
  409. p_points = ctypes.pointer(bound.c_points)
  410. libvect.Vect_point_buffer2(self.x, self.y,
  411. dist_x, dist_y,
  412. angle, int(round_), tol,
  413. p_points)
  414. return Area(boundary=bound, centroid=self)
  415. class Line(Geo):
  416. """Instantiate a new Line with a list of tuple, or with a list of Point. ::
  417. >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)])
  418. >>> line #doctest: +NORMALIZE_WHITESPACE
  419. Line([Point(0.000000, 0.000000),
  420. Point(1.000000, 1.000000),
  421. Point(2.000000, 0.000000),
  422. Point(1.000000, -1.000000)])
  423. ..
  424. """
  425. # geometry type
  426. gtype = libvect.GV_LINE
  427. def __init__(self, points=None, **kargs):
  428. super(Line, self).__init__(**kargs)
  429. if points is not None:
  430. for pnt in points:
  431. self.append(pnt)
  432. def __getitem__(self, key):
  433. """Get line point of given index, slice allowed. ::
  434. >>> line = Line([(0, 0), (1, 1), (2, 2), (3, 3)])
  435. >>> line[1]
  436. Point(1.000000, 1.000000)
  437. >>> line[-1]
  438. Point(3.000000, 3.000000)
  439. >>> line[:2]
  440. [Point(0.000000, 0.000000), Point(1.000000, 1.000000)]
  441. ..
  442. """
  443. #TODO:
  444. # line[0].x = 10 is not working
  445. #pnt.c_px = ctypes.pointer(self.c_points.contents.x[indx])
  446. # pnt.c_px = ctypes.cast(id(self.c_points.contents.x[indx]),
  447. # ctypes.POINTER(ctypes.c_double))
  448. if isinstance(key, slice):
  449. #import pdb; pdb.set_trace()
  450. #Get the start, stop, and step from the slice
  451. return [Point(self.c_points.contents.x[indx],
  452. self.c_points.contents.y[indx],
  453. None if self.is2D else self.c_points.contents.z[indx])
  454. for indx in range(*key.indices(len(self)))]
  455. elif isinstance(key, int):
  456. if key < 0: # Handle negative indices
  457. key += self.c_points.contents.n_points
  458. if key >= self.c_points.contents.n_points:
  459. raise IndexError('Index out of range')
  460. return Point(self.c_points.contents.x[key],
  461. self.c_points.contents.y[key],
  462. None if self.is2D else self.c_points.contents.z[key])
  463. else:
  464. raise ValueError("Invalid argument type: %r." % key)
  465. def __setitem__(self, indx, pnt):
  466. """Change the coordinate of point. ::
  467. >>> line = Line([(0, 0), (1, 1)])
  468. >>> line[0] = (2, 2)
  469. >>> line
  470. Line([Point(2.000000, 2.000000), Point(1.000000, 1.000000)])
  471. ..
  472. """
  473. x, y, z = get_xyz(pnt)
  474. self.c_points.contents.x[indx] = x
  475. self.c_points.contents.y[indx] = y
  476. self.c_points.contents.z[indx] = z
  477. def __iter__(self):
  478. """Return a Point generator of the Line"""
  479. return (self.__getitem__(i) for i in range(self.__len__()))
  480. def __len__(self):
  481. """Return the number of points of the line."""
  482. return self.c_points.contents.n_points
  483. def __str__(self):
  484. return self.get_wkt()
  485. def __repr__(self):
  486. return "Line([%s])" % ', '.join([repr(pnt) for pnt in self.__iter__()])
  487. def get_pnt(self, distance, angle=0, slope=0):
  488. """Return a Point object on line in the specified distance, using the
  489. `Vect_point_on_line` C function.
  490. Raise a ValueError If the distance exceed the Line length. ::
  491. >>> line = Line([(0, 0), (1, 1)])
  492. >>> line.get_pnt(5) #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  493. Traceback (most recent call last):
  494. ...
  495. ValueError: The distance exceed the lenght of the line,
  496. that is: 1.414214
  497. >>> line.get_pnt(1)
  498. Point(0.707107, 0.707107)
  499. ..
  500. """
  501. # instantiate an empty Point object
  502. maxdist = self.length()
  503. if distance > maxdist:
  504. str_err = "The distance exceed the lenght of the line, that is: %f"
  505. raise ValueError(str_err % maxdist)
  506. pnt = Point(0, 0, -9999)
  507. libvect.Vect_point_on_line(self.c_points, distance,
  508. pnt.c_points.contents.x,
  509. pnt.c_points.contents.y,
  510. pnt.c_points.contents.z,
  511. angle, slope)
  512. pnt.is2D = self.is2D
  513. return pnt
  514. def append(self, pnt):
  515. """Appends one point to the end of a line, using the
  516. ``Vect_append_point`` C function. ::
  517. >>> line = Line()
  518. >>> line.append((10, 100))
  519. >>> line
  520. Line([Point(10.000000, 100.000000)])
  521. >>> line.append((20, 200))
  522. >>> line
  523. Line([Point(10.000000, 100.000000), Point(20.000000, 200.000000)])
  524. Like python list.
  525. """
  526. x, y, z = get_xyz(pnt)
  527. libvect.Vect_append_point(self.c_points, x, y, z)
  528. def bbox(self, bbox=None):
  529. """Return the bounding box of the line, using ``Vect_line_box``
  530. C function. ::
  531. >>> line = Line([(0, 0), (0, 1), (2, 1), (2, 0)])
  532. >>> bbox = line.bbox()
  533. >>> bbox
  534. Bbox(1.0, 0.0, 2.0, 0.0)
  535. ..
  536. """
  537. bbox = bbox if bbox else Bbox()
  538. libvect.Vect_line_box(self.c_points, bbox.c_bbox)
  539. return bbox
  540. def extend(self, line, forward=True):
  541. """Appends points to the end of a line.
  542. It is possible to extend a line, give a list of points, or directly
  543. with a line_pnts struct.
  544. If forward is True the line is extend forward otherwise is extend
  545. backward. The method use the `Vect_append_points` C function. ::
  546. >>> line = Line([(0, 0), (1, 1)])
  547. >>> line.extend( Line([(2, 2), (3, 3)]) )
  548. >>> line #doctest: +NORMALIZE_WHITESPACE
  549. Line([Point(0.000000, 0.000000),
  550. Point(1.000000, 1.000000),
  551. Point(2.000000, 2.000000),
  552. Point(3.000000, 3.000000)])
  553. Like python list, it is possible to extend a line, with another line
  554. or with a list of points.
  555. """
  556. # set direction
  557. if forward:
  558. direction = libvect.GV_FORWARD
  559. else:
  560. direction = libvect.GV_BACKWARD
  561. # check if is a Line object
  562. if isinstance(line, Line):
  563. c_points = line.c_points
  564. else:
  565. # instantiate a Line object
  566. lin = Line()
  567. for pnt in line:
  568. # add the points to the line
  569. lin.append(pnt)
  570. c_points = lin.c_points
  571. libvect.Vect_append_points(self.c_points, c_points, direction)
  572. def insert(self, indx, pnt):
  573. """Insert new point at index position and move all old points at
  574. that position and above up, using ``Vect_line_insert_point``
  575. C function. ::
  576. >>> line = Line([(0, 0), (1, 1)])
  577. >>> line.insert(0, Point(1.000000, -1.000000) )
  578. >>> line #doctest: +NORMALIZE_WHITESPACE
  579. Line([Point(1.000000, -1.000000),
  580. Point(0.000000, 0.000000),
  581. Point(1.000000, 1.000000)])
  582. ..
  583. """
  584. if indx < 0: # Handle negative indices
  585. indx += self.c_points.contents.n_points
  586. if indx >= self.c_points.contents.n_points:
  587. raise IndexError('Index out of range')
  588. x, y, z = get_xyz(pnt)
  589. libvect.Vect_line_insert_point(self.c_points, indx, x, y, z)
  590. def length(self):
  591. """Calculate line length, 3D-length in case of 3D vector line, using
  592. `Vect_line_length` C function. ::
  593. >>> line = Line([(0, 0), (1, 1), (0, 1)])
  594. >>> line.length()
  595. 2.414213562373095
  596. ..
  597. """
  598. return libvect.Vect_line_length(self.c_points)
  599. def length_geodesic(self):
  600. """Calculate line length, usig `Vect_line_geodesic_length` C function.
  601. ::
  602. >>> line = Line([(0, 0), (1, 1), (0, 1)])
  603. >>> line.length_geodesic()
  604. 2.414213562373095
  605. ..
  606. """
  607. return libvect.Vect_line_geodesic_length(self.c_points)
  608. def distance(self, pnt):
  609. """Return a tuple with:
  610. * the closest point on the line,
  611. * the distance between these two points,
  612. * distance of point from segment beginning
  613. * distance of point from line
  614. The distance is compute using the ``Vect_line_distance`` C function.
  615. Example
  616. ---------
  617. ::
  618. >>> line = Line([(0, 0), (0, 2)])
  619. >>> line.distance(Point(1, 1))
  620. (Point(0.000000, 1.000000), 1.0, 1.0, 1.0)
  621. ..
  622. """
  623. # instantite outputs
  624. cx = ctypes.c_double(0)
  625. cy = ctypes.c_double(0)
  626. cz = ctypes.c_double(0)
  627. dist = ctypes.c_double(0)
  628. sp_dist = ctypes.c_double(0)
  629. lp_dist = ctypes.c_double(0)
  630. libvect.Vect_line_distance(self.c_points,
  631. pnt.x, pnt.y, 0 if pnt.is2D else pnt.z,
  632. 0 if self.is2D else 1,
  633. ctypes.byref(cx), ctypes.byref(cy),
  634. ctypes.byref(cz), ctypes.byref(dist),
  635. ctypes.byref(sp_dist),
  636. ctypes.byref(lp_dist))
  637. # instantiate the Point class
  638. point = Point(cx.value, cy.value, cz.value)
  639. point.is2D = self.is2D
  640. return point, dist.value, sp_dist.value, lp_dist.value
  641. def get_first_cat(self):
  642. """Fetches FIRST category number for given vector line and field, using
  643. the ``Vect_get_line_cat`` C function.
  644. .. warning::
  645. Not implemented yet.
  646. """
  647. # TODO: add this method.
  648. libvect.Vect_get_line_cat(self.map, self.id, self.field)
  649. pass
  650. def pop(self, indx):
  651. """Return the point in the index position and remove from the Line. ::
  652. >>> line = Line([(0, 0), (1, 1), (2, 2)])
  653. >>> midle_pnt = line.pop(1)
  654. >>> midle_pnt
  655. Point(1.000000, 1.000000)
  656. >>> line
  657. Line([Point(0.000000, 0.000000), Point(2.000000, 2.000000)])
  658. ..
  659. """
  660. if indx < 0: # Handle negative indices
  661. indx += self.c_points.contents.n_points
  662. if indx >= self.c_points.contents.n_points:
  663. raise IndexError('Index out of range')
  664. pnt = self.__getitem__(indx)
  665. libvect.Vect_line_delete_point(self.c_points, indx)
  666. return pnt
  667. def delete(self, indx):
  668. """Remove the point in the index position. ::
  669. >>> line = Line([(0, 0), (1, 1), (2, 2)])
  670. >>> line.delete(-1)
  671. >>> line
  672. Line([Point(0.000000, 0.000000), Point(1.000000, 1.000000)])
  673. ..
  674. """
  675. if indx < 0: # Handle negative indices
  676. indx += self.c_points.contents.n_points
  677. if indx >= self.c_points.contents.n_points:
  678. raise IndexError('Index out of range')
  679. libvect.Vect_line_delete_point(self.c_points, indx)
  680. def prune(self):
  681. """Remove duplicate points, i.e. zero length segments, using
  682. `Vect_line_prune` C function. ::
  683. >>> line = Line([(0, 0), (1, 1), (1, 1), (2, 2)])
  684. >>> line.prune()
  685. >>> line #doctest: +NORMALIZE_WHITESPACE
  686. Line([Point(0.000000, 0.000000),
  687. Point(1.000000, 1.000000),
  688. Point(2.000000, 2.000000)])
  689. ..
  690. """
  691. libvect.Vect_line_prune(self.c_points)
  692. def prune_thresh(self, threshold):
  693. """Remove points in threshold, using the ``Vect_line_prune_thresh``
  694. C funtion. ::
  695. >>> line = Line([(0, 0), (1.0, 1.0), (1.2, 0.9), (2, 2)])
  696. >>> line.prune_thresh(0.5)
  697. >>> line #doctest: +SKIP +NORMALIZE_WHITESPACE
  698. Line([Point(0.000000, 0.000000),
  699. Point(1.000000, 1.000000),
  700. Point(2.000000, 2.000000)])
  701. .. warning ::
  702. prune_thresh is not working yet.
  703. """
  704. libvect.Vect_line_prune(self.c_points, ctypes.c_double(threshold))
  705. def remove(self, pnt):
  706. """Delete point at given index and move all points above down, using
  707. `Vect_line_delete_point` C function. ::
  708. >>> line = Line([(0, 0), (1, 1), (2, 2)])
  709. >>> line.remove((2, 2))
  710. >>> line[-1]
  711. Point(1.000000, 1.000000)
  712. ..
  713. """
  714. for indx, point in enumerate(self.__iter__()):
  715. if pnt == point:
  716. libvect.Vect_line_delete_point(self.c_points, indx)
  717. return
  718. raise ValueError('list.remove(x): x not in list')
  719. def reverse(self):
  720. """Reverse the order of vertices, using `Vect_line_reverse`
  721. C function. ::
  722. >>> line = Line([(0, 0), (1, 1), (2, 2)])
  723. >>> line.reverse()
  724. >>> line #doctest: +NORMALIZE_WHITESPACE
  725. Line([Point(2.000000, 2.000000),
  726. Point(1.000000, 1.000000),
  727. Point(0.000000, 0.000000)])
  728. ..
  729. """
  730. libvect.Vect_line_reverse(self.c_points)
  731. def segment(self, start, end):
  732. """Create line segment. using the ``Vect_line_segment`` C function."""
  733. line = Line()
  734. libvect.Vect_line_segment(self.c_points, start, end, line.c_points)
  735. return line
  736. def tolist(self):
  737. """Return a list of tuple. ::
  738. >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)])
  739. >>> line.tolist()
  740. [(0.0, 0.0), (1.0, 1.0), (2.0, 0.0), (1.0, -1.0)]
  741. ..
  742. """
  743. return [pnt.coords() for pnt in self.__iter__()]
  744. def toarray(self):
  745. """Return an array of coordinates. ::
  746. >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)])
  747. >>> line.toarray() #doctest: +NORMALIZE_WHITESPACE
  748. array([[ 0., 0.],
  749. [ 1., 1.],
  750. [ 2., 0.],
  751. [ 1., -1.]])
  752. ..
  753. """
  754. return np.array(self.tolist())
  755. def get_wkt(self):
  756. """Return a Well Known Text string of the line. ::
  757. >>> line = Line([(0, 0), (1, 1), (1, 2)])
  758. >>> line.get_wkt() #doctest: +ELLIPSIS
  759. 'LINESTRING(0.000000 0.000000, ..., 1.000000 2.000000)'
  760. ..
  761. """
  762. return "LINESTRING(%s)" % ', '.join([
  763. ' '.join(['%f' % coord for coord in pnt.coords()])
  764. for pnt in self.__iter__()])
  765. def from_wkt(self, wkt):
  766. """Read a WKT string. ::
  767. >>> line = Line()
  768. >>> line.from_wkt("LINESTRING(0 0,1 1,1 2)")
  769. >>> line #doctest: +NORMALIZE_WHITESPACE
  770. Line([Point(0.000000, 0.000000),
  771. Point(1.000000, 1.000000),
  772. Point(1.000000, 2.000000)])
  773. ..
  774. """
  775. match = re.match('LINESTRING\((.*)\)', wkt)
  776. if match:
  777. self.reset()
  778. for coord in match.groups()[0].strip().split(','):
  779. self.append(tuple([float(e) for e in coord.split(' ')]))
  780. else:
  781. return None
  782. def get_wkb(self):
  783. """Return a WKB buffer.
  784. .. warning::
  785. Not implemented yet.
  786. """
  787. pass
  788. def buffer(self, dist=None, dist_x=None, dist_y=None,
  789. angle=0, round_=True, caps=True, tol=0.1):
  790. """Return the buffer area around the line, using the
  791. ``Vect_line_buffer2`` C function.
  792. Parameters
  793. ----------
  794. dist : numeric
  795. The distance around the line.
  796. dist_x: numeric, optional
  797. The distance along x
  798. dist_y: numeric, optional
  799. The distance along y
  800. angle: numeric, optional
  801. The angle between 0x and major axis
  802. round_: bool, optional
  803. To make corners round
  804. caps: bool, optional
  805. To add caps at line ends
  806. tol: float, optional
  807. Fix the maximum distance between theoretical arc
  808. and output segments
  809. Returns
  810. -------
  811. buffer : Area
  812. The buffer area around the line.
  813. Example
  814. ---------
  815. ::
  816. >>> line = Line([(0, 0), (0, 2)])
  817. >>> area = line.buffer(10)
  818. >>> area.boundary #doctest: +ELLIPSIS
  819. Line([Point(-10.000000, 0.000000),...Point(-10.000000, 0.000000)])
  820. >>> area.centroid
  821. Point(0.000000, 0.000000)
  822. >>> area.isles
  823. []
  824. ..
  825. """
  826. if dist is not None:
  827. dist_x = dist
  828. dist_y = dist
  829. elif not dist_x or not dist_y:
  830. raise TypeError('TypeError: buffer expected 1 arguments, got 0')
  831. p_bound = ctypes.pointer(ctypes.pointer(libvect.line_pnts()))
  832. pp_isle = ctypes.pointer(ctypes.pointer(
  833. ctypes.pointer(libvect.line_pnts())))
  834. n_isles = ctypes.pointer(ctypes.c_int())
  835. libvect.Vect_line_buffer2(self.c_points,
  836. dist_x, dist_y, angle,
  837. int(round_), int(caps), tol,
  838. p_bound, pp_isle, n_isles)
  839. return Area(boundary=Line(c_points=p_bound.contents),
  840. centroid=self[0],
  841. isles=[Line(c_points=pp_isle[i].contents)
  842. for i in range(n_isles.contents.value)])
  843. def reset(self):
  844. """Reset line, using `Vect_reset_line` C function. ::
  845. >>> line = Line([(0, 0), (1, 1), (2, 0), (1, -1)])
  846. >>> len(line)
  847. 4
  848. >>> line.reset()
  849. >>> len(line)
  850. 0
  851. >>> line
  852. Line([])
  853. ..
  854. """
  855. libvect.Vect_reset_line(self.c_points)
  856. class Node(object):
  857. pass
  858. class Boundary(Line):
  859. """
  860. """
  861. # geometry type
  862. gtype = libvect.GV_BOUNDARY
  863. def __init__(self, lines=None, left=None, right=None,
  864. **kargs):
  865. v_id = kargs.get('v_id', 0)
  866. self.dir = libvect.GV_FORWARD if v_id > 0 else libvect.GV_BACKWARD
  867. super(Boundary, self).__init__(**kargs)
  868. self.c_left = ctypes.pointer(ctypes.c_int())
  869. self.c_right = ctypes.pointer(ctypes.c_int())
  870. #self.get_left_right()
  871. @property
  872. def left_id(self):
  873. return self.c_left.contents.value
  874. @property
  875. def right_id(self):
  876. return self.c_right.contents.value
  877. def __repr__(self):
  878. return "Boundary(v_id=%r)" % self.id
  879. def _get_centroid(self, side, idonly=False):
  880. if side > 0:
  881. v_id = libvect.Vect_get_area_centroid(self.c_mapinfo, side)
  882. v_id = v_id if v_id else None
  883. if idonly:
  884. return v_id
  885. else:
  886. cntr = Centroid(v_id=v_id, c_mapinfo=self.c_mapinfo)
  887. return cntr
  888. def get_left_centroid(self, idonly=False):
  889. return self._get_centroid(self.left_id, idonly)
  890. def get_right_centroid(self, idonly=False):
  891. return self._get_centroid(self.left_id, idonly)
  892. def get_left_right(self):
  893. """Return left and right value"""
  894. libvect.Vect_get_line_areas(self.c_mapinfo, self.id,
  895. self.c_left, self.c_right)
  896. return self.c_left.contents.value, self.c_right.contents.value
  897. def area(self):
  898. """Return the area of the polygon.
  899. ::
  900. >>> bound = Boundary(points=[(0, 0), (0, 2), (2, 2), (2, 0),
  901. ... (0, 0)])
  902. >>> bound.area()
  903. 4.0
  904. .."""
  905. libgis.G_begin_polygon_area_calculations()
  906. return libgis.G_area_of_polygon(self.c_points.contents.x,
  907. self.c_points.contents.y,
  908. self.c_points.contents.n_points)
  909. class Centroid(Point):
  910. """The Centroid class inherit from the Point class.
  911. Centroid contains an attribute with the C Map_info struct, and attributes
  912. with the id of the Area. ::
  913. >>> centroid = Centroid(x=0, y=10)
  914. >>> centroid
  915. Centoid(0.000000, 10.000000)
  916. >>> from grass.pygrass.vector import VectorTopo
  917. >>> geo = VectorTopo('geology')
  918. >>> geo.open()
  919. >>> centroid = Centroid(v_id=1, c_mapinfo=geo.c_mapinfo)
  920. >>> centroid
  921. Centoid(893202.874416, 297339.312795)
  922. ..
  923. """
  924. # geometry type
  925. gtype = libvect.GV_CENTROID
  926. def __init__(self, area_id=None, **kargs):
  927. super(Centroid, self).__init__(**kargs)
  928. self.area_id = area_id
  929. if self.id and self.c_mapinfo and self.area_id is None:
  930. self.area_id = self.get_area_id()
  931. elif self.c_mapinfo and self.area_id and self.id is None:
  932. self.id = self.get_centroid_id()
  933. if self.area_id is not None:
  934. self.read()
  935. #self.c_pline = ctypes.pointer(libvect.P_line()) if topology else None
  936. def __repr__(self):
  937. return "Centoid(%s)" % ', '.join(['%f' % co for co in self.coords()])
  938. def get_centroid_id(self):
  939. """Return the centroid_id, using the c_mapinfo and an area_id
  940. attributes of the class, and calling the Vect_get_area_centroid
  941. C function, if no centroid_id were found return None"""
  942. centroid_id = libvect.Vect_get_area_centroid(self.c_mapinfo,
  943. self.area_id)
  944. return centroid_id if centroid_id != 0 else None
  945. def get_area_id(self):
  946. """Return the area_id, using the c_mapinfo and an centroid_id
  947. attributes of the class, and calling the Vect_get_centroid_area
  948. C function, if no area_id were found return None"""
  949. area_id = libvect.Vect_get_centroid_area(self.c_mapinfo,
  950. self.id)
  951. return area_id if area_id != 0 else None
  952. class Isle(Geo):
  953. """An Isle is an area contained by another area.
  954. """
  955. def __init__(self, **kargs):
  956. super(Isle, self).__init__(**kargs)
  957. #self.area_id = area_id
  958. def __repr__(self):
  959. return "Isle(%d)" % (self.id)
  960. def boundaries(self):
  961. ilist = Ilist()
  962. libvect.Vect_get_isle_boundaries(self.c_mapinfo, self.id,
  963. ilist.c_ilist)
  964. return ilist
  965. def bbox(self, bbox=None):
  966. bbox = bbox if bbox else Bbox()
  967. libvect.Vect_get_isle_box(self.c_mapinfo, self.id, bbox.c_bbox)
  968. return bbox
  969. def points(self):
  970. """Return a Line object with the outer ring points"""
  971. line = Line()
  972. libvect.Vect_get_isle_points(self.c_mapinfo, self.id, line.c_points)
  973. return line
  974. def points_geos(self):
  975. """Return a Line object with the outer ring points
  976. """
  977. return libvect.Vect_get_isle_points_geos(self.c_mapinfo, self.id)
  978. def area_id(self):
  979. """Returns area id for isle."""
  980. return libvect.Vect_get_isle_area(self.c_mapinfo, self.id)
  981. def alive(self):
  982. """Check if isle is alive or dead (topology required)"""
  983. return bool(libvect.Vect_isle_alive(self.c_mapinfo, self.id))
  984. def contain_pnt(self, pnt):
  985. """Check if point is in area."""
  986. bbox = self.bbox()
  987. return bool(libvect.Vect_point_in_island(pnt.x, pnt.y,
  988. self.c_mapinfo, self.id,
  989. bbox.c_bbox.contents))
  990. def area(self):
  991. """Return the area value of an Isle"""
  992. border = self.points()
  993. return libgis.G_area_of_polygon(border.c_points.contents.x,
  994. border.c_points.contents.y,
  995. border.c_points.contents.n_points)
  996. def perimeter(self):
  997. """Return the perimeter value of an Isle.
  998. ::
  999. double Vect_area_perimeter()
  1000. """
  1001. border = self.points()
  1002. return libvect.Vect_area_perimeter(border.c_points)
  1003. class Isles(object):
  1004. def __init__(self, c_mapinfo, area_id=None):
  1005. self.c_mapinfo = c_mapinfo
  1006. self.area_id = area_id
  1007. self._isles_id = None
  1008. self._isles = None
  1009. if area_id:
  1010. self._isles_id = self.get_isles_id()
  1011. self._isles = self.get_isles()
  1012. def __len__(self):
  1013. return libvect.Vect_get_area_num_isles(self.c_mapinfo, self.area_id)
  1014. def __repr__(self):
  1015. return "Isles(%r)" % self.area_id
  1016. def __getitem__(self, key):
  1017. if self._isles is None:
  1018. self.get_isles()
  1019. return self._isles[key]
  1020. def get_isles_id(self):
  1021. return [libvect.Vect_get_area_isle(self.c_mapinfo, self.area_id, i)
  1022. for i in range(self.__len__())]
  1023. def get_isles(self):
  1024. return [Isle(v_id=isle_id, c_mapinfo=self.c_mapinfo)
  1025. for isle_id in self._isles_id]
  1026. def select_by_bbox(self, bbox):
  1027. """Vect_select_isles_by_box"""
  1028. pass
  1029. class Area(Geo):
  1030. """
  1031. 'Vect_build_line_area',
  1032. 'Vect_find_area',
  1033. 'Vect_get_area_box',
  1034. 'Vect_get_area_points_geos',
  1035. 'Vect_get_centroid_area',
  1036. 'Vect_get_isle_area',
  1037. 'Vect_get_line_areas',
  1038. 'Vect_get_num_areas',
  1039. 'Vect_get_point_in_area',
  1040. 'Vect_isle_find_area',
  1041. 'Vect_point_in_area',
  1042. 'Vect_point_in_area_outer_ring',
  1043. 'Vect_read_area_geos',
  1044. 'Vect_remove_small_areas',
  1045. 'Vect_select_areas_by_box',
  1046. 'Vect_select_areas_by_polygon']
  1047. """
  1048. # geometry type
  1049. gtype = libvect.GV_AREA
  1050. def __init__(self, boundary=None, centroid=None, isles=None, **kargs):
  1051. super(Area, self).__init__(**kargs)
  1052. self.boundary = None
  1053. self.centroid = None
  1054. self.isles = None
  1055. if boundary and centroid:
  1056. self.boundary = boundary
  1057. self.centroid = centroid
  1058. self.isles = isles if isles else []
  1059. # set the attributes
  1060. if self.attrs and self.cat:
  1061. self.attrs.cat = self.cat
  1062. def __repr__(self):
  1063. return "Area(%d)" % self.id if self.id else "Area( )"
  1064. def init_from_id(self, area_id=None):
  1065. """Return an Area object"""
  1066. if area_id is None and self.id is None:
  1067. raise ValueError("You need to give or set the area_id")
  1068. self.id = area_id if area_id is not None else self.id
  1069. # get boundary
  1070. self.get_points()
  1071. # get isles
  1072. self.get_isles()
  1073. def get_points(self, line=None):
  1074. """Return a Line object with the outer ring"""
  1075. line = Line() if line is None else line
  1076. libvect.Vect_get_area_points(self.c_mapinfo, self.id, line.c_points)
  1077. return line
  1078. def get_centroid(self, centroid=None):
  1079. centroid_id = libvect.Vect_get_area_centroid(self.c_mapinfo, self.id)
  1080. if centroid:
  1081. centroid.id = centroid_id
  1082. centroid.read()
  1083. return centroid
  1084. return Centroid(v_id=centroid_id, c_mapinfo=self.c_mapinfo,
  1085. area_id=self.id)
  1086. def num_isles(self):
  1087. return libvect.Vect_get_area_num_isles(self.c_mapinfo, self.id)
  1088. def get_isles(self, isles=None):
  1089. """Instantiate the boundary attribute reading area_id"""
  1090. if isles is not None:
  1091. isles.area_id = self.id
  1092. return isles
  1093. return Isles(self.c_mapinfo, self.id)
  1094. def area(self):
  1095. """Returns area of area without areas of isles.
  1096. double Vect_get_area_area (const struct Map_info *Map, int area)
  1097. """
  1098. return libvect.Vect_get_area_area(self.c_mapinfo, self.id)
  1099. def alive(self):
  1100. """Check if area is alive or dead (topology required)
  1101. """
  1102. return bool(libvect.Vect_area_alive(self.c_mapinfo, self.id))
  1103. def bbox(self, bbox=None):
  1104. """
  1105. Vect_get_area_box
  1106. """
  1107. bbox = bbox if bbox else Bbox()
  1108. libvect.Vect_get_area_box(self.c_mapinfo, self.id, bbox.c_bbox)
  1109. return bbox
  1110. def buffer(self, dist=None, dist_x=None, dist_y=None,
  1111. angle=0, round_=True, caps=True, tol=0.1):
  1112. """Return the buffer area around the area, using the
  1113. ``Vect_area_buffer2`` C function.
  1114. Parameters
  1115. ----------
  1116. dist : numeric
  1117. The distance around the line.
  1118. dist_x: numeric, optional
  1119. The distance along x
  1120. dist_y: numeric, optional
  1121. The distance along y
  1122. angle: numeric, optional
  1123. The angle between 0x and major axis
  1124. round_: bool, optional
  1125. To make corners round
  1126. caps: bool, optional
  1127. To add caps at line ends
  1128. tol: float, optional
  1129. Fix the maximum distance between theoretical arc
  1130. and output segments
  1131. Returns
  1132. -------
  1133. buffer : Area
  1134. The buffer area around the line.
  1135. """
  1136. if dist is not None:
  1137. dist_x = dist
  1138. dist_y = dist
  1139. elif not dist_x or not dist_y:
  1140. raise TypeError('TypeError: buffer expected 1 arguments, got 0')
  1141. p_bound = ctypes.pointer(ctypes.pointer(libvect.line_pnts()))
  1142. pp_isle = ctypes.pointer(ctypes.pointer(
  1143. ctypes.pointer(libvect.line_pnts())))
  1144. n_isles = ctypes.pointer(ctypes.c_int())
  1145. libvect.Vect_area_buffer2(self.c_mapinfo, self.id,
  1146. dist_x, dist_y, angle,
  1147. int(round_), int(caps), tol,
  1148. p_bound, pp_isle, n_isles)
  1149. return Area(boundary=Line(c_points=p_bound.contents),
  1150. centroid=self.centroid,
  1151. isles=[Line(c_points=pp_isle[i].contents)
  1152. for i in range(n_isles.contents.value)])
  1153. def boundaries(self, ilist=False):
  1154. """Creates list of boundaries for given area.
  1155. int Vect_get_area_boundaries(const struct Map_info *Map,
  1156. int area, struct ilist *List)
  1157. """
  1158. ilst = Ilist()
  1159. libvect.Vect_get_area_boundaries(self.c_mapinfo, self.id,
  1160. ilst.c_ilist)
  1161. if ilist:
  1162. return ilist
  1163. return [Boundary(v_id, c_mapinfo=self.c_mapinfo) for v_id in ilst]
  1164. def cats(self, cats=None):
  1165. """Get area categories.
  1166. int Vect_get_area_cats (const struct Map_info *Map,
  1167. int area, struct line_cats *Cats)
  1168. """
  1169. cats = cats if cats else Cats()
  1170. libvect.Vect_get_area_cats(self.c_mapinfo, self.id, cats.c_cats)
  1171. return cats
  1172. def get_first_cat(self):
  1173. """Find FIRST category of given field and area.
  1174. int Vect_get_area_cat(const struct Map_info *Map, int area, int field)
  1175. """
  1176. pass
  1177. def contain_pnt(self, pnt, bbox=None):
  1178. """Check if point is in area.
  1179. int Vect_point_in_area(double x, double y,
  1180. const struct Map_info *Map,
  1181. int area, struct bound_box box)
  1182. """
  1183. bbox = bbox if bbox else self.bbox()
  1184. return bool(libvect.Vect_point_in_area(pnt.x, pnt.y,
  1185. self.c_mapinfo, self.id,
  1186. bbox.c_bbox))
  1187. def perimeter(self):
  1188. """Calculate area perimeter.
  1189. double Vect_area_perimeter (const struct line_pnts *Points)
  1190. """
  1191. border = self.get_points()
  1192. return libvect.Vect_area_perimeter(border.c_points)
  1193. def read(self, line=None, centroid=None, isles=None):
  1194. self.boundary = self.get_points(line)
  1195. self.centroid = self.get_centroid(centroid)
  1196. #self.isles = self.get_isles(isles)
  1197. libvect.Vect_read_line(self.c_mapinfo, None, self.c_cats,
  1198. self.centroid.id)
  1199. #
  1200. # Define a dictionary to convert the feature type to name and or object
  1201. #
  1202. GV_TYPE = {libvect.GV_POINT: {'label': 'point', 'obj': Point},
  1203. libvect.GV_LINE: {'label': 'line', 'obj': Line},
  1204. libvect.GV_BOUNDARY: {'label': 'boundary', 'obj': Boundary},
  1205. libvect.GV_CENTROID: {'label': 'centroid', 'obj': Centroid},
  1206. libvect.GV_FACE: {'label': 'face', 'obj': None},
  1207. libvect.GV_KERNEL: {'label': 'kernel', 'obj': None},
  1208. libvect.GV_AREA: {'label': 'area', 'obj': Area},
  1209. libvect.GV_VOLUME: {'label': 'volume', 'obj': None}, }
  1210. GEOOBJ = {"areas": Area,
  1211. "dblinks": None,
  1212. "faces": None,
  1213. "holes": None,
  1214. "islands": Isle,
  1215. "kernels": None,
  1216. "line_points": None,
  1217. "points": Point,
  1218. "lines": Line,
  1219. "nodes": Node,
  1220. "volumes": None}
  1221. def c_read_next_line(c_mapinfo, c_points, c_cats):
  1222. v_id = c_mapinfo.contents.next_line
  1223. v_id = v_id if v_id != 0 else None
  1224. ftype = libvect.Vect_read_next_line(c_mapinfo, c_points, c_cats)
  1225. if ftype == -2:
  1226. raise StopIteration()
  1227. if ftype == -1:
  1228. raise
  1229. return ftype, v_id, c_points, c_cats
  1230. def read_next_line(c_mapinfo, table=None, writable=False,
  1231. c_points=None, c_cats=None):
  1232. """Return the next geometry feature of a vector map."""
  1233. c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts())
  1234. c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats())
  1235. ftype, v_id, c_points, c_cats = c_read_next_line(c_mapinfo, c_points,
  1236. c_cats)
  1237. return GV_TYPE[ftype]['obj'](v_id=v_id, c_mapinfo=c_mapinfo,
  1238. c_points=c_points, c_cats=c_cats,
  1239. table=table, writable=writable)
  1240. def c_read_line(feature_id, c_mapinfo, c_points, c_cats):
  1241. nmax = libvect.Vect_get_num_lines(c_mapinfo)
  1242. if feature_id < 0: # Handle negative indices
  1243. feature_id += nmax + 1
  1244. if feature_id > nmax:
  1245. raise IndexError('Index out of range')
  1246. if feature_id > 0:
  1247. ftype = libvect.Vect_read_line(c_mapinfo, c_points, c_cats, feature_id)
  1248. return ftype, c_points, c_cats
  1249. else:
  1250. raise ValueError('The index must be >0, %r given.' % feature_id)
  1251. def read_line(feature_id, c_mapinfo, table=None, writable=False,
  1252. c_points=None, c_cats=None):
  1253. """Return a geometry object given the feature id and the c_mapinfo.
  1254. """
  1255. c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts())
  1256. c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats())
  1257. ftype, c_points, c_cats = c_read_line(feature_id, c_mapinfo,
  1258. c_points, c_cats)
  1259. if GV_TYPE[ftype]['obj'] is not None:
  1260. return GV_TYPE[ftype]['obj'](v_id=feature_id, c_mapinfo=c_mapinfo,
  1261. c_points=c_points, c_cats=c_cats,
  1262. table=table, writable=writable)