wxvdriver.py 33 KB

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