wxvdriver.py 30 KB

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