geometry.py 48 KB

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