geometry.py 48 KB

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