wxvdriver.py 33 KB

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