wxvdriver.py 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. """!
  2. @package wxvdriver.py
  3. @brief wxGUI vector digitizer (display driver)
  4. Code based on wxVdigit C++ component from GRASS 6.4.0
  5. (gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
  6. List of classes:
  7. - DisplayDriver
  8. (C) 2007-2011 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. @author Martin Landa <landa.martin gmail.com>
  12. """
  13. import math
  14. import wx
  15. from debug import Debug
  16. from preferences import globalSettings as UserSettings
  17. from grass.lib.grass import *
  18. from grass.lib.vector import *
  19. from grass.lib.vedit import *
  20. log = None
  21. progress = None
  22. def print_error(msg, type):
  23. """!Redirect stderr"""
  24. global log
  25. if log:
  26. log.write(msg)
  27. else:
  28. print msg
  29. return 0
  30. def print_progress(value):
  31. """!Redirect progress info"""
  32. global progress
  33. if progress:
  34. progress.SetValue(value)
  35. else:
  36. print value
  37. return 0
  38. errtype = CFUNCTYPE(UNCHECKED(c_int), String, c_int)
  39. errfunc = errtype(print_error)
  40. pertype = CFUNCTYPE(UNCHECKED(c_int), c_int)
  41. perfunc = pertype(print_progress)
  42. class DisplayDriver:
  43. def __init__(self, device, deviceTmp, mapObj, window, glog, gprogress):
  44. """!Display driver used by vector digitizer
  45. @param device wx.PseudoDC device where to draw vector objects
  46. @param deviceTmp wx.PseudoDC device where to draw temporary vector objects
  47. @param mapOng Map Object (render.Map)
  48. @param windiow parent window for dialogs
  49. @param glog logging device (None to discard messages)
  50. @param gprogress progress bar device (None to discard message)
  51. """
  52. global errfunc, perfunc, log, progress
  53. log = glog
  54. progress = gprogress
  55. G_gisinit('') # initialize GRASS libs
  56. G_set_error_routine(errfunc)
  57. G_set_percent_routine(perfunc)
  58. self.mapInfo = None # open vector map (Map_Info structure)
  59. self.poMapInfo = None # pointer to self.mapInfo
  60. self.is3D = False # is open vector map 3D
  61. self.dc = device # PseudoDC devices
  62. self.dcTmp = deviceTmp
  63. self.mapObj = mapObj
  64. self.region = mapObj.GetCurrentRegion()
  65. self.window = window
  66. self.log = log # log device
  67. self.firstNode = True # track PseudoDC Id of selected features
  68. self.lastNodeId = -1
  69. # GRASS lib
  70. self.poPoints = Vect_new_line_struct()
  71. self.poCats = Vect_new_cats_struct()
  72. # selected objects
  73. self.selected = {
  74. 'field' : -1, # field number
  75. 'cats' : list(), # list of cats
  76. 'ids' : list(), # list of ids
  77. 'idsDupl' : list(), # list of duplicated features
  78. }
  79. # digitizer settings
  80. self.settings = {
  81. 'highlight' : None,
  82. 'highlightDupl' : { 'enabled' : False,
  83. 'color' : None },
  84. 'point' : { 'enabled' : False,
  85. 'color' : None },
  86. 'line' : { 'enabled' : False,
  87. 'color' : None },
  88. 'boundaryNo' : { 'enabled' : False,
  89. 'color' : None },
  90. 'boundaryOne' : { 'enabled' : False,
  91. 'color' : None },
  92. 'boundaryTwo' : { 'enabled' : False,
  93. 'color' : None },
  94. 'centroidIn' : { 'enabled' : False,
  95. 'color' : None },
  96. 'centroidOut' : { 'enabled' : False,
  97. 'color' : None },
  98. 'centroidDup' : { 'enabled' : False,
  99. 'color' : None },
  100. 'nodeOne' : { 'enabled' : False,
  101. 'color' : None },
  102. 'nodeTwo' : { 'enabled' : False,
  103. 'color' : None },
  104. 'vertex' : { 'enabled' : False,
  105. 'color' : None },
  106. 'area' : { 'enabled' : False,
  107. 'color' : None },
  108. 'direction' : { 'enabled' : False,
  109. 'color' : None },
  110. 'lineWidth' : -1, # screen units
  111. }
  112. # topology
  113. self._resetTopology()
  114. self._drawSelected = False
  115. self._drawSegments = False
  116. self.UpdateSettings()
  117. def __del__(self):
  118. """!Close currently open vector map"""
  119. G_unset_error_routine()
  120. G_unset_percent_routine()
  121. if self.poMapInfo:
  122. self.CloseMap()
  123. Vect_destroy_line_struct(self.poPoints)
  124. Vect_destroy_cats_struct(self.poCats)
  125. def _resetTopology(self):
  126. """!Reset topology dict
  127. """
  128. self.topology = {
  129. 'highlight' : 0,
  130. 'point' : 0,
  131. 'line' : 0,
  132. 'boundaryNo' : 0,
  133. 'boundaryOne' : 0,
  134. 'boundaryTwo' : 0,
  135. 'centroidIn' : 0,
  136. 'centroidOut' : 0,
  137. 'centroidDup' : 0,
  138. 'nodeOne' : 0,
  139. 'nodeTwo' : 0,
  140. 'vertex' : 0,
  141. }
  142. def _cell2Pixel(self, east, north, elev):
  143. """!Conversion from geographic coordinates (east, north)
  144. to screen (x, y)
  145. @todo 3D stuff...
  146. @param east, north, elev geographical coordinates
  147. @return x, y screen coordinates (integer)
  148. """
  149. map_res = max(self.region['ewres'], self.region['nsres'])
  150. w = self.region['center_easting'] - (self.mapObj.width / 2) * map_res
  151. n = self.region['center_northing'] + (self.mapObj.height / 2) * map_res
  152. return int((east - w) / map_res), int((n - north) / map_res)
  153. def _drawCross(self, pdc, point, size = 5):
  154. """!Draw cross symbol of given size to device content
  155. Used for points, nodes, vertices
  156. @param[in,out] PseudoDC where to draw
  157. @param point coordinates of center
  158. @param size size of the cross symbol
  159. @return 0 on success
  160. @return -1 on failure
  161. """
  162. if not pdc or not point:
  163. return -1
  164. pdc.DrawLine(point.x - size, point.y, point.x + size, point.y)
  165. pdc.DrawLine(point.x, point.y - size, point.x, point.y + size)
  166. return 0
  167. def _drawObject(self, robj):
  168. """!Draw given object to the device
  169. The object is defined as robject() from vedit.h.
  170. @param robj object to be rendered
  171. @return 1 on success
  172. @return -1 on failure (vector feature marked as dead, etc.)
  173. """
  174. if not self.dc or not self.dcTmp:
  175. return -1
  176. Debug.msg(3, "_drawObject(): type=%d npoints=%d", robj.type, robj.npoints)
  177. brush = None
  178. if self._isSelected(robj.fid):
  179. pdc = self.dcTmp
  180. if self.settings['highlightDupl']['enabled'] and self._isDuplicated(robj.fid):
  181. pen = wx.Pen(self.settings['highlightDupl']['color'], self.settings['lineWidth'], wx.SOLID)
  182. else:
  183. pen = wx.Pen(self.settings['highlight'], self.settings['lineWidth'], wx.SOLID)
  184. dcId = 1
  185. self.topology['highlight'] += 1
  186. if not self._drawSelected:
  187. return
  188. else:
  189. pdc = self.dc
  190. pen, brush = self._definePen(robj.type)
  191. dcId = 0
  192. pdc.SetPen(pen)
  193. if brush:
  194. pdc.SetBrush(brush)
  195. if robj.type & (TYPE_POINT | TYPE_CENTROIDIN | TYPE_CENTROIDOUT | TYPE_CENTROIDDUP |
  196. TYPE_NODEONE | TYPE_NODETWO | TYPE_VERTEX): # -> point
  197. if dcId > 0:
  198. if robj.type == TYPE_VERTEX:
  199. dcId = 3 # first vertex
  200. elif robj.type & (TYPE_NODEONE | TYPE_NODETWO):
  201. if self.firstNode:
  202. dcId = 1
  203. self.firstNode = False
  204. else:
  205. dcId = self.lastNodeId
  206. for i in range(robj.npoints):
  207. p = robj.point[i]
  208. if dcId > 0:
  209. pdc.SetId(dcId)
  210. dcId += 2
  211. self._drawCross(pdc, p)
  212. else:
  213. if dcId > 0 and self._drawSegments:
  214. self.fisrtNode = True
  215. self.lastNodeId = robj.npoints * 2 - 1
  216. dcId = 2 # first segment
  217. i = 0
  218. while i < robj.npoints - 1:
  219. point_beg = wx.Point(robj.point[i].x, robj.point[i].y)
  220. point_end = wx.Point(robj.point[i+1].x, robj.point[i+1].y)
  221. pdc.SetId(dcId) # set unique id & set bbox for each segment
  222. pdc.SetPen(pen)
  223. pdc.SetIdBounds(dcId - 1, wx.Rect(point_beg.x, point_beg.y, 0, 0))
  224. pdc.SetIdBounds(dcId, wx.RectPP(point_beg, point_end))
  225. pdc.DrawLine(point_beg.x, point_beg.y,
  226. point_end.x, point_end.y)
  227. i += 1
  228. dcId += 2
  229. pdc.SetIdBounds(dcId - 1, wx.Rect(robj.point[robj.npoints - 1].x,
  230. robj.point[robj.npoints - 1].y,
  231. 0, 0))
  232. else:
  233. points = list()
  234. for i in range(robj.npoints):
  235. p = robj.point[i]
  236. points.append(wx.Point(p.x, p.y))
  237. if robj.type == TYPE_AREA:
  238. pdc.DrawPolygon(points)
  239. else:
  240. pdc.DrawLines(points)
  241. def _definePen(self, rtype):
  242. """!Define pen/brush based on rendered object)
  243. Updates also self.topology dict
  244. @return pen, brush
  245. """
  246. if rtype == TYPE_POINT:
  247. key = 'point'
  248. elif rtype == TYPE_LINE:
  249. key = 'line'
  250. elif rtype == TYPE_BOUNDARYNO:
  251. key = 'boundaryNo'
  252. elif rtype == TYPE_BOUNDARYTWO:
  253. key = 'boundaryTwo'
  254. elif rtype == TYPE_BOUNDARYONE:
  255. key = 'boundaryOne'
  256. elif rtype == TYPE_CENTROIDIN:
  257. key = 'centroidIn'
  258. elif rtype == TYPE_CENTROIDOUT:
  259. key = 'centroidOut'
  260. elif rtype == TYPE_CENTROIDDUP:
  261. key = 'centroidDup'
  262. elif rtype == TYPE_NODEONE:
  263. key = 'nodeOne'
  264. elif rtype == TYPE_NODETWO:
  265. key = 'nodeTwo'
  266. elif rtype == TYPE_VERTEX:
  267. key = 'vertex'
  268. elif rtype == TYPE_AREA:
  269. key = 'area'
  270. elif rtype == TYPE_ISLE:
  271. key = 'isle'
  272. elif rtype == TYPE_DIRECTION:
  273. key = 'direction'
  274. if key not in ('direction', 'area', 'isle'):
  275. self.topology[key] += 1
  276. if key in ('area', 'isle'):
  277. pen = wx.TRANSPARENT_PEN
  278. if key == 'area':
  279. brush = wx.Brush(self.settings[key]['color'], wx.SOLID)
  280. else:
  281. brush = wx.TRANSPARENT_BRUSH
  282. else:
  283. pen = wx.Pen(self.settings[key]['color'], self.settings['lineWidth'], wx.SOLID)
  284. brush = None
  285. return pen, brush
  286. def _getDrawFlag(self):
  287. """!Get draw flag from the settings
  288. See vedit.h for list of draw flags.
  289. @return draw flag (int)
  290. """
  291. ret = 0
  292. if self.settings['point']['enabled']:
  293. ret |= DRAW_POINT
  294. if self.settings['line']['enabled']:
  295. ret |= DRAW_LINE
  296. if self.settings['boundaryNo']['enabled']:
  297. ret |= DRAW_BOUNDARYNO
  298. if self.settings['boundaryTwo']['enabled']:
  299. ret |= DRAW_BOUNDARYTWO
  300. if self.settings['boundaryOne']['enabled']:
  301. ret |= DRAW_BOUNDARYONE
  302. if self.settings['centroidIn']['enabled']:
  303. ret |= DRAW_CENTROIDIN
  304. if self.settings['centroidOut']['enabled']:
  305. ret |= DRAW_CENTROIDOUT
  306. if self.settings['centroidDup']['enabled']:
  307. ret |= DRAW_CENTROIDDUP
  308. if self.settings['nodeOne']['enabled']:
  309. ret |= DRAW_NODEONE
  310. if self.settings['nodeTwo']['enabled']:
  311. ret |= DRAW_NODETWO
  312. if self.settings['vertex']['enabled']:
  313. ret |= DRAW_VERTEX
  314. if self.settings['area']['enabled']:
  315. ret |= DRAW_AREA
  316. if self.settings['direction']['enabled']:
  317. ret |= DRAW_DIRECTION
  318. return ret
  319. def _isSelected(self, line, force = False):
  320. """!Check if vector object selected?
  321. @param line feature id
  322. @return True if vector object is selected
  323. @return False if vector object is not selected
  324. """
  325. if len(self.selected['cats']) < 1 or force:
  326. # select by id
  327. if line in self.selected['ids']:
  328. return True
  329. else:
  330. # select by cat
  331. cats = self.poCats.contents
  332. for i in range(cats.n_cats):
  333. if cats.field[i] == self.selected['field'] and \
  334. cats.cat[i] in self.selected['cats']:
  335. # remember id
  336. # -> after drawing all features selected.cats is reseted */
  337. self.selected['ids'].append(line)
  338. return True
  339. return False
  340. def _isDuplicated(self, line):
  341. """!Check for already marked duplicates
  342. @param line feature id
  343. @return True line already marked as duplicated
  344. @return False not duplicated
  345. """
  346. return line in self.selected['idsDupl']
  347. def _getRegionBox(self):
  348. """!Get bound_box() from current region
  349. @return bound_box
  350. """
  351. box = bound_box()
  352. box.N = self.region['n']
  353. box.S = self.region['s']
  354. box.E = self.region['e']
  355. box.W = self.region['w']
  356. box.T = PORT_DOUBLE_MAX
  357. box.B = -PORT_DOUBLE_MAX
  358. return box
  359. def DrawMap(self, force = False):
  360. """!Draw content of the vector map to the device
  361. @param force force drawing
  362. @return number of drawn features
  363. @return -1 on error
  364. """
  365. Debug.msg(1, "DisplayDriver.DrawMap(): force=%d", force)
  366. if not self.poMapInfo or not self.dc or not self.dcTmp:
  367. return -1
  368. rlist = Vedit_render_map(self.poMapInfo, byref(self._getRegionBox()), self._getDrawFlag(),
  369. self.region['center_easting'], self.region['center_northing'],
  370. self.mapObj.width, self.mapObj.height,
  371. max(self.region['nsres'], self.region['ewres'])).contents
  372. self._resetTopology()
  373. self.dc.BeginDrawing()
  374. self.dcTmp.BeginDrawing()
  375. # draw objects
  376. for i in range(rlist.nitems):
  377. robj = rlist.item[i].contents
  378. self._drawObject(robj)
  379. self.dc.EndDrawing()
  380. self.dcTmp.EndDrawing()
  381. # reset list of selected features by cat
  382. # list of ids - see IsSelected()
  383. self.selected['field'] = -1
  384. self.selected['cats'] = list()
  385. def _getSelectType(self):
  386. """!Get type(s) to be selected
  387. Used by SelectLinesByBox() and SelectLineByPoint()
  388. """
  389. ftype = 0
  390. for feature in (('point', GV_POINT),
  391. ('line', GV_LINE),
  392. ('centroid', GV_CENTROID),
  393. ('boundary', GV_BOUNDARY)):
  394. if UserSettings.Get(group = 'vdigit', key = 'selectType',
  395. subkey = [feature[0], 'enabled']):
  396. ftype |= feature[1]
  397. return ftype
  398. def _validLine(self, line):
  399. """!Check if feature id is valid
  400. @param line feature id
  401. @return True valid feature id
  402. @return False invalid
  403. """
  404. if line > 0 and line <= Vect_get_num_lines(self.poMapInfo):
  405. return True
  406. return False
  407. def SelectLinesByBox(self, bbox, drawSeg = False, poMapInfo = None):
  408. """!Select vector objects by given bounding box
  409. If line id is already in the list of selected lines, then it will
  410. be excluded from this list.
  411. @param bbox bounding box definition
  412. @param drawSeg True to draw segments of line
  413. @param poMapInfo use external Map_info, None for self.poMapInfo
  414. @return number of selected features
  415. @return None on error
  416. """
  417. thisMapInfo = poMapInfo is None
  418. if not poMapInfo:
  419. poMapInfo = self.poMapInfo
  420. if not poMapInfo:
  421. return None
  422. if thisMapInfo:
  423. self._drawSegments = drawSeg
  424. self._drawSelected = True
  425. # select by ids
  426. self.selected['cats'] = list()
  427. if thisMapInfo:
  428. selected = self.selected['ids']
  429. else:
  430. selected = list()
  431. poList = Vect_new_list()
  432. x1, y1 = bbox[0]
  433. x2, y2 = bbox[1]
  434. poBbox = Vect_new_line_struct()
  435. Vect_append_point(poBbox, x1, y1, 0.0)
  436. Vect_append_point(poBbox, x2, y1, 0.0)
  437. Vect_append_point(poBbox, x2, y2, 0.0)
  438. Vect_append_point(poBbox, x1, y2, 0.0)
  439. Vect_append_point(poBbox, x1, y1, 0.0)
  440. Vect_select_lines_by_polygon(poMapInfo, poBbox,
  441. 0, None, # isles
  442. self._getSelectType(), poList)
  443. flist = poList.contents
  444. nlines = flist.n_values
  445. Debug.msg(1, "DisplayDriver.SelectLinesByBox() num = %d", nlines)
  446. for i in range(nlines):
  447. line = flist.value[i]
  448. if UserSettings.Get(group = 'vdigit', key = 'selectInside',
  449. subkey = 'enabled'):
  450. inside = True
  451. if not self._validLine(line):
  452. return None
  453. Vect_read_line(poMapInfo, self.poPoints, None, line)
  454. points = self.poPoints.contents
  455. for p in range(points.n_points):
  456. if not Vect_point_in_poly(points.x[p], points.y[p],
  457. poBbox):
  458. inside = False
  459. break
  460. if not inside:
  461. continue # skip lines just overlapping bbox
  462. if not self._isSelected(line):
  463. selected.append(line)
  464. else:
  465. selected.remove(line)
  466. Vect_destroy_line_struct(poBbox)
  467. Vect_destroy_list(poList)
  468. return nlines
  469. def SelectLineByPoint(self, point, poMapInfo = None):
  470. """!Select vector feature by given point in given
  471. threshold
  472. Only one vector object can be selected. Bounding boxes of
  473. all segments are stores.
  474. @param point points coordinates (x, y)
  475. @param poMapInfo use external Map_info, None for self.poMapInfo
  476. @return dict {'line' : feature id, 'point' : point on line}
  477. """
  478. thisMapInfo = poMapInfo is None
  479. if not poMapInfo:
  480. poMapInfo = self.poMapInfo
  481. if not poMapInfo:
  482. return { 'line' : -1, 'point': None }
  483. if thisMapInfo:
  484. self._drawSelected = True
  485. # select by ids
  486. self.selected['cats'] = list()
  487. if thisMapInfo:
  488. selected = self.selected['ids']
  489. else:
  490. selected = list()
  491. poFound = Vect_new_list()
  492. lineNearest = Vect_find_line_list(poMapInfo, point[0], point[1], 0,
  493. self._getSelectType(), self.GetThreshold(), self.is3D,
  494. None, poFound)
  495. Debug.msg(1, "DisplayDriver.SelectLineByPoint() found = %d", lineNearest)
  496. if lineNearest > 0:
  497. if not self._isSelected(lineNearest):
  498. selected.append(lineNearest)
  499. else:
  500. selected.remove(lineNearest)
  501. px = c_double()
  502. py = c_double()
  503. pz = c_double()
  504. if not self._validLine(lineNearest):
  505. return { 'line' : -1, 'point': None }
  506. ftype = Vect_read_line(poMapInfo, self.poPoints, self.poCats, lineNearest)
  507. Vect_line_distance (self.poPoints, point[0], point[1], 0.0, self.is3D,
  508. byref(px), byref(py), byref(pz),
  509. None, None, None)
  510. # check for duplicates
  511. if self.settings['highlightDupl']['enabled']:
  512. found = poFound.contents
  513. for i in range(found.n_values):
  514. line = found.value[i]
  515. if line != lineNearest:
  516. selected.append(line)
  517. self.GetDuplicates()
  518. for i in range(found.n_values):
  519. line = found.value[i]
  520. if line != lineNearest and not self._isDuplicated(line):
  521. selected.remove(line)
  522. Vect_destroy_list(poFound)
  523. if thisMapInfo:
  524. # drawing segments can be very expensive
  525. # only one features selected
  526. self._drawSegments = True
  527. return { 'line' : lineNearest,
  528. 'point' : (px.value, py.value, pz.value) }
  529. def _listToIList(self, plist):
  530. """!Generate from list struct_ilist
  531. """
  532. ilist = Vect_new_list()
  533. for val in plist:
  534. Vect_list_append(ilist, val)
  535. return ilist
  536. def GetSelectedIList(self, ilist = None):
  537. """!Get list of selected objects as struct_ilist
  538. Returned IList must be freed by Vect_destroy_list().
  539. @return struct_ilist
  540. """
  541. if ilist:
  542. return self._listToIList(ilist)
  543. return self._listToIList(self.selected['ids'])
  544. def GetSelected(self, grassId = True):
  545. """!Get ids of selected objects
  546. @param grassId True for feature id, False for PseudoDC id
  547. @return list of ids of selected vector objects
  548. """
  549. if grassId:
  550. return self.selected['ids']
  551. dc_ids = list()
  552. if not self._drawSegments:
  553. dc_ids.append(1)
  554. elif len(self.selected['ids']) > 0:
  555. # only first selected feature
  556. Vect_read_line(self.poMapInfo, self.poPoints, None,
  557. self.selected['ids'][0])
  558. points = self.poPoints.contents
  559. # node - segment - vertex - segment - node
  560. for i in range(1, 2 * points.n_points):
  561. dc_ids.append(i)
  562. return dc_ids
  563. def SetSelected(self, ids, layer = -1):
  564. """!Set selected vector objects
  565. @param list of ids (None to unselect features)
  566. @param layer layer number for features selected based on category number
  567. """
  568. if ids:
  569. self._drawSelected = True
  570. else:
  571. self._drawSelected = False
  572. if layer > 0:
  573. selected.field = layer
  574. self.selected['cats'] = ids
  575. else:
  576. field = -1
  577. self.selected['ids'] = ids
  578. def GetSelectedVertex(self, pos):
  579. """!Get PseudoDC vertex id of selected line
  580. Set bounding box for vertices of line.
  581. @param pos position
  582. @return id of center, left and right vertex
  583. @return 0 no line found
  584. @return -1 on error
  585. """
  586. returnId = list()
  587. # only one object can be selected
  588. if len(self.selected['ids']) != 1 or not self._drawSegments:
  589. return returnId
  590. startId = 1
  591. line = self.selected['ids'][0]
  592. if not self._validLine(line):
  593. return -1
  594. ftype = Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line)
  595. minDist = 0.0
  596. Gid = -1
  597. # find the closest vertex (x, y)
  598. DCid = 1
  599. points = self.poPoints.contents
  600. for idx in range(points.n_points):
  601. dist = Vect_points_distance(pos[0], pos[1], 0.0,
  602. points.x[idx], points.y[idx], points.z[idx], 0)
  603. if idx == 0:
  604. minDist = dist
  605. Gid = idx
  606. else:
  607. if minDist > dist:
  608. minDist = dist
  609. Gid = idx
  610. vx, vy = self._cell2Pixel(points.x[idx], points.y[idx], points.z[idx])
  611. rect = wx.Rect(vx, vy, 0, 0)
  612. self.dc.SetIdBounds(DCid, rect)
  613. DCid += 2
  614. if minDist > self.GetThreshold():
  615. return returnId
  616. # translate id
  617. DCid = Gid * 2 + 1
  618. # add selected vertex
  619. returnId.append(DCid)
  620. # left vertex
  621. if DCid == startId:
  622. returnId.append(-1)
  623. else:
  624. returnId.append(DCid - 2)
  625. # right vertex
  626. if DCid == (points.n_points - 1) * 2 + startId:
  627. returnId.append(-1)
  628. else:
  629. returnId.append(DCid + 2)
  630. return returnId
  631. def DrawSelected(self, flag):
  632. """!Draw selected features
  633. @param flag True to draw selected features
  634. """
  635. self._drawSelected = bool(flag)
  636. def CloseMap(self):
  637. """!Close vector map
  638. @return 0 on success
  639. @return non-zero on error
  640. """
  641. ret = 0
  642. if self.poMapInfo:
  643. # rebuild topology
  644. Vect_build_partial(self.poMapInfo, GV_BUILD_NONE)
  645. Vect_build(self.poMapInfo)
  646. # close map and store topo/cidx
  647. ret = Vect_close(self.poMapInfo)
  648. del self.mapInfo
  649. self.poMapInfo = self.mapInfo = None
  650. return ret
  651. def OpenMap(self, name, mapset, update = True):
  652. """!Open vector map by the driver
  653. @param name name of vector map to be open
  654. @param mapset name of mapset where the vector map lives
  655. @return map_info
  656. @return None on error
  657. """
  658. Debug.msg("DisplayDriver.OpenMap(): name=%s mapset=%s updated=%d",
  659. name, mapset, update)
  660. if not self.mapInfo:
  661. self.mapInfo = Map_info()
  662. self.poMapInfo = pointer(self.mapInfo)
  663. # open existing map
  664. if update:
  665. ret = Vect_open_update(self.poMapInfo, name, mapset)
  666. else:
  667. ret = Vect_open_old(self.poMapInfo, name, mapset)
  668. self.is3D = Vect_is_3d(self.poMapInfo)
  669. if ret == -1: # error
  670. del self.mapInfo
  671. self.poMapInfo = self.mapInfo = None
  672. elif ret < 2:
  673. dlg = wx.MessageDialog(parent = self.window,
  674. message = _("Topology for vector map <%s> is not available. "
  675. "Topology is required by digitizer. Do you want to "
  676. "rebuild topology (takes some time) and open the vector map "
  677. "for editing?") % name,
  678. caption=_("Topology missing"),
  679. style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  680. ret = dlg.ShowModal()
  681. if ret != wx.ID_YES:
  682. del self.mapInfo
  683. self.poMapInfo = self.mapInfo = None
  684. else:
  685. Vect_build(self.poMapInfo)
  686. return self.poMapInfo
  687. def GetMapBoundingBox(self):
  688. """!Get bounding box of (opened) vector map layer
  689. @return (w,s,b,e,n,t)
  690. """
  691. if not self.poMapInfo:
  692. return None
  693. bbox = bound_box()
  694. Vect_get_map_box(self.poMapInfo, byref(bbox))
  695. return bbox.W, bbox.S, bbox.B, \
  696. bbox.E, bbox.N, bbox.T
  697. def UpdateSettings(self, alpha = 255):
  698. """!Update display driver settings
  699. @todo map units
  700. @alpha color value for aplha channel
  701. """
  702. color = dict()
  703. for key in self.settings.keys():
  704. if key == 'lineWidth':
  705. self.settings[key] = int(UserSettings.Get(group = 'vdigit', key = 'lineWidth',
  706. subkey = 'value'))
  707. continue
  708. color = wx.Color(UserSettings.Get(group = 'vdigit', key = 'symbol',
  709. subkey = [key, 'color'])[0],
  710. UserSettings.Get(group = 'vdigit', key = 'symbol',
  711. subkey = [key, 'color'])[1],
  712. UserSettings.Get(group = 'vdigit', key = 'symbol',
  713. subkey = [key, 'color'])[2])
  714. if key == 'highlight':
  715. self.settings[key] = color
  716. continue
  717. if key == 'highlightDupl':
  718. self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'checkForDupl',
  719. subkey = 'enabled'))
  720. else:
  721. self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'symbol',
  722. subkey = [key, 'enabled']))
  723. self.settings[key]['color'] = color
  724. def UpdateRegion(self):
  725. """!Update geographical region used by display driver
  726. """
  727. self.region = self.mapObj.GetCurrentRegion()
  728. def GetThreshold(self, type = 'snapping', value = None, units = None):
  729. """!Return threshold value in map units
  730. @param type snapping mode (node, vertex)
  731. @param value threshold to be set up
  732. @param units units (map, screen)
  733. @return threshold value
  734. """
  735. if value is None:
  736. value = UserSettings.Get(group = 'vdigit', key = type, subkey = 'value')
  737. if units is None:
  738. units = UserSettings.Get(group = 'vdigit', key = type, subkey = 'units')
  739. if value < 0:
  740. value = (self.region['nsres'] + self.region['ewres']) / 2.0
  741. if units == "screen pixels":
  742. # pixel -> cell
  743. res = max(self.region['nsres'], self.region['ewres'])
  744. return value * res
  745. return value
  746. def GetDuplicates(self):
  747. """!Return ids of (selected) duplicated vector features
  748. """
  749. if not self.poMapInfo:
  750. return
  751. ids = dict()
  752. APoints = Vect_new_line_struct()
  753. BPoints = Vect_new_line_struct()
  754. self.selected['idsDupl'] = list()
  755. for i in range(len(self.selected['ids'])):
  756. line1 = self.selected['ids'][i]
  757. if self._isDuplicated(line1):
  758. continue
  759. Vect_read_line(self.poMapInfo, APoints, None, line1)
  760. for line2 in self.selected['ids']:
  761. if line1 == line2 or self._isDuplicated(line2):
  762. continue
  763. Vect_read_line(self.poMapInfo, BPoints, None, line2)
  764. if Vect_line_check_duplicate(APoints, BPoints, WITHOUT_Z):
  765. if i not in ids:
  766. ids[i] = list()
  767. ids[i].append((line1, self._getCatString(line1)))
  768. self.selected['idsDupl'].append(line1)
  769. ids[i].append((line2, self._getCatString(line2)))
  770. self.selected['idsDupl'].append(line2)
  771. Vect_destroy_line_struct(APoints)
  772. Vect_destroy_line_struct(BPoints)
  773. return ids
  774. def _getCatString(self, line):
  775. Vect_read_line(self.poMapInfo, None, self.poCats, line)
  776. cats = self.poCats.contents
  777. catsDict = dict()
  778. for i in range(cats.n_cats):
  779. layer = cats.field[i]
  780. if layer not in catsDict:
  781. catsDict[layer] = list()
  782. catsDict[layer].append(cats.cat[i])
  783. catsStr = ''
  784. for l, c in catsDict.iteritems():
  785. catsStr = '%d: (%s)' % (l, ','.join(map(str, c)))
  786. return catsStr
  787. def UnSelect(self, lines):
  788. """!Unselect vector features
  789. @param lines list of feature id(s)
  790. """
  791. checkForDupl = False
  792. for line in lines:
  793. if self._isSelected(line):
  794. self.selected['ids'].remove(line)
  795. if self.settings['highlightDupl']['enabled'] and self._isDuplicated(line):
  796. checkForDupl = True
  797. if checkForDupl:
  798. self.GetDuplicates()
  799. return len(self.selected['ids'])