wxvdriver.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. """!
  2. @package wxvdriver.py
  3. @brief wxGUI vector digitizer (display driver)
  4. Code based on wxVdigit C++ component from GRASS 6.4.0. Converted to
  5. 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 as 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. class DisplayDriver:
  21. def __init__(self, device, deviceTmp, mapObj, log = None):
  22. """Display driver used by vector digitizer
  23. @param device wx.PseudoDC device where to draw vector objects
  24. @param deviceTmp wx.PseudoDC device where to draw temporary vector objects
  25. """
  26. G_gisinit("") # initialize GRASS libs
  27. self.mapInfo = None # open vector map (Map_Info structure)
  28. self.dc = device # PseudoDC devices
  29. self.dcTmp = deviceTmp
  30. self.mapObj = mapObj
  31. self.region = mapObj.GetCurrentRegion()
  32. self.log = log # log device
  33. # objects used by GRASS vector library
  34. self.points = line_pnts()
  35. self.pointsScreen = list()
  36. self.cats = line_cats()
  37. # selected objects
  38. self.selected = {
  39. 'field' : -1, # field number
  40. 'cats' : list(), # list of cats
  41. 'ids' : list(), # list of ids
  42. 'idsDupl' : list(), # list of duplicated features
  43. }
  44. # digitizer settings
  45. self.settings = {
  46. 'highlight' : None,
  47. 'highlightDupl' : { 'enabled' : False,
  48. 'color' : None },
  49. 'point' : { 'enabled' : False,
  50. 'color' : None },
  51. 'line' : { 'enabled' : False,
  52. 'color' : None },
  53. 'boundaryNo' : { 'enabled' : False,
  54. 'color' : None },
  55. 'boundaryOne' : { 'enabled' : False,
  56. 'color' : None },
  57. 'boundaryTwo' : { 'enabled' : False,
  58. 'color' : None },
  59. 'centroidIn' : { 'enabled' : False,
  60. 'color' : None },
  61. 'centroidOut' : { 'enabled' : False,
  62. 'color' : None },
  63. 'centroidDup' : { 'enabled' : False,
  64. 'color' : None },
  65. 'nodeOne' : { 'enabled' : False,
  66. 'color' : None },
  67. 'nodeTwo' : { 'enabled' : False,
  68. 'color' : None },
  69. 'vertex' : { 'enabled' : False,
  70. 'color' : None },
  71. 'area' : { 'enabled' : False,
  72. 'color' : None },
  73. 'direction' : { 'enabled' : False,
  74. 'color' : None },
  75. 'lineWidth' : -1, # screen units
  76. }
  77. # topology
  78. self.topology = {
  79. 'highlight' : 0,
  80. 'point' : 0,
  81. 'line' : 0,
  82. 'boundaryNo' : 0,
  83. 'boundaryOne' : 0,
  84. 'boundaryTwo' : 0,
  85. 'centroidIn' : 0,
  86. 'centroidOut' : 0,
  87. 'centroidDup' : 0,
  88. 'nodeOne' : 0,
  89. 'nodeTwo' : 0,
  90. 'vertex' : 0,
  91. }
  92. self.drawSelected = None
  93. self.drawSegments = False
  94. self.UpdateSettings()
  95. # def __del__(self):
  96. # """!Close currently open vector map"""
  97. # if self.mapInfo:
  98. # self.CloseMap()
  99. def _cell2Pixel(self, east, north, elev):
  100. """!Conversion from geographic coordinates (east, north)
  101. to screen (x, y)
  102. @todo 3D stuff...
  103. @param east, north, elev geographical coordinates
  104. @return x, y screen coordinates (integer)
  105. """
  106. map_res = max(self.region['ewres'], self.region['nsres'])
  107. w = self.region['center_easting'] - (self.mapObj.width / 2) * map_res
  108. n = self.region['center_northing'] + (self.mapObj.height / 2) * map_res
  109. return int((east - w) / map_res), int((n - north) / map_res)
  110. def _drawCross(self, pdc, point, size = 5):
  111. """!Draw cross symbol of given size to device content
  112. Used for points, nodes, vertices
  113. @param[in,out] PseudoDC where to draw
  114. @param point coordinates of center
  115. @param size size of the cross symbol
  116. @return 0 on success
  117. @return -1 on failure
  118. """
  119. if not pdc or not point:
  120. return -1;
  121. pdc.DrawLine(point.x - size, point.y, point.x + size, point.y)
  122. pdc.DrawLine(point.x, point.y - size, point.x, point.y + size)
  123. return 0
  124. def _drawObject(self, robj):
  125. """!Draw given object to the device
  126. The object is defined as robject() from vedit.h.
  127. @param robj object to be rendered
  128. @return 1 on success
  129. @return -1 on failure (vector feature marked as dead, etc.)
  130. """
  131. if not self.dc or not self.dcTmp:
  132. return -1
  133. dcId = 0
  134. pdc = self.dc
  135. # draw object to the device
  136. pdc.SetId(dcId) # 0 | 1 (selected)
  137. Debug.msg(3, "_drawObject(): type=%d npoints=%d", robj.type, robj.npoints)
  138. points = list()
  139. self._setPen(robj.type, pdc)
  140. for i in range(robj.npoints):
  141. p = robj.point[i]
  142. if robj.type & (TYPE_POINT | TYPE_CENTROIDIN | TYPE_CENTROIDOUT | TYPE_CENTROIDDUP |
  143. TYPE_NODEONE | TYPE_NODETWO | TYPE_VERTEX): # -> point
  144. self._drawCross(pdc, p)
  145. else: # -> line
  146. # if dcId > 0 and self.drawSegments:
  147. # dcId = 2 # first segment
  148. # i = 0
  149. # while (i < len(self.pointsScreen) - 2):
  150. # point_beg = wx.Point(self.pointsScreen[i])
  151. # point_end = wx.Point(self.pointsScreen[i + 1])
  152. # pdc.SetId(dcId) # set unique id & set bbox for each segment
  153. # pdc.SetPen(pen)
  154. # rect = wx.Rect(point_beg, point_end)
  155. # pdc.SetIdBounds(dcId, rect)
  156. # pdc.DrawLine(point_beg.x, point_beg.y,
  157. # point_end.x, point_end.y)
  158. # i += 2
  159. # dcId += 2
  160. # else:
  161. points.append(wx.Point(p.x, p.y))
  162. if points:
  163. if robj.type == TYPE_AREA:
  164. pdc.DrawPolygon(points)
  165. else:
  166. pdc.DrawLines(points)
  167. def _setPen(self, rtype, pdc):
  168. """!Set pen/brush based on rendered object)
  169. Updates also self.topology dict
  170. """
  171. if rtype == TYPE_POINT:
  172. key = 'point'
  173. elif rtype == TYPE_LINE:
  174. key = 'line'
  175. elif rtype == TYPE_BOUNDARYNO:
  176. key = 'boundaryNo'
  177. elif rtype == TYPE_BOUNDARYTWO:
  178. key = 'boundaryTwo'
  179. elif rtype == TYPE_BOUNDARYONE:
  180. key = 'boundaryOne'
  181. elif rtype == TYPE_CENTROIDIN:
  182. key = 'centroidIn'
  183. elif rtype == TYPE_CENTROIDOUT:
  184. key = 'centroidOut'
  185. elif rtype == TYPE_CENTROIDDUP:
  186. key = 'centroidDup'
  187. elif rtype == TYPE_NODEONE:
  188. key = 'nodeOne'
  189. elif rtype == TYPE_NODETWO:
  190. key = 'nodeTwo'
  191. elif rtype == TYPE_VERTEX:
  192. key = 'vertex'
  193. elif rtype == TYPE_AREA:
  194. key = 'area'
  195. elif rtype == TYPE_ISLE:
  196. key = 'isle'
  197. elif rtype == TYPE_DIRECTION:
  198. key = 'direction'
  199. if key not in ('direction', 'area', 'isle'):
  200. self.topology[key] += 1
  201. if key in ('area', 'isle'):
  202. pen = wx.TRANSPARENT_PEN
  203. if key == 'area':
  204. pdc.SetBrush(wx.Brush(self.settings[key]['color'], wx.SOLID))
  205. else:
  206. pdc.SetBrush(wx.TRANSPARENT_BRUSH)
  207. else:
  208. pdc.SetPen(wx.Pen(self.settings[key]['color'], self.settings['lineWidth'], wx.SOLID))
  209. def _getDrawFlag(self):
  210. """!Get draw flag from the settings
  211. See vedit.h for list of draw flags.
  212. @return draw flag (int)
  213. """
  214. ret = 0
  215. if self.settings['point']['enabled']:
  216. ret |= TYPE_POINT
  217. if self.settings['line']['enabled']:
  218. ret |= TYPE_LINE
  219. if self.settings['boundaryNo']['enabled']:
  220. ret |= TYPE_BOUNDARYNO
  221. if self.settings['boundaryTwo']['enabled']:
  222. ret |= TYPE_BOUNDARYTWO
  223. if self.settings['boundaryOne']['enabled']:
  224. ret |= TYPE_BOUNDARYONE
  225. if self.settings['centroidIn']['enabled']:
  226. ret |= TYPE_CENTROIDIN
  227. if self.settings['centroidOut']['enabled']:
  228. ret |= TYPE_CENTROIDOUT
  229. if self.settings['centroidDup']['enabled']:
  230. ret |= TYPE_CENTROIDDUP
  231. if self.settings['nodeOne']['enabled']:
  232. ret |= TYPE_NODEONE
  233. if self.settings['nodeTwo']['enabled']:
  234. ret |= TYPE_NODETWO
  235. if self.settings['vertex']['enabled']:
  236. ret |= TYPE_VERTEX
  237. if self.settings['area']['enabled']:
  238. ret |= TYPE_AREA
  239. if self.settings['direction']['enabled']:
  240. ret |= TYPE_DIRECTION
  241. return ret
  242. def _printIds(self):
  243. pass
  244. def _isSelected(self, featId, foo = False):
  245. return False
  246. def _isDuplicated(self, featId):
  247. return False
  248. def _resetTopology(self):
  249. pass
  250. def _getRegionBox(self):
  251. """!Get bound_box() from current region
  252. @return bound_box
  253. """
  254. box = bound_box()
  255. box.N = self.region['n']
  256. box.S = self.region['s']
  257. box.E = self.region['e']
  258. box.W = self.region['w']
  259. box.T = PORT_DOUBLE_MAX
  260. box.B = -PORT_DOUBLE_MAX
  261. return box
  262. def DrawMap(self, force = False):
  263. """!Draw content of the vector map to the device
  264. @param force force drawing
  265. @return number of drawn features
  266. @return -1 on error
  267. """
  268. Debug.msg(1, "DisplayDriver.DrawMap(): force=%d", force)
  269. if not self.mapInfo or not self.dc or not self.dcTmp:
  270. return -1
  271. rlist = Vedit_render_map(byref(self.mapInfo), byref(self._getRegionBox()), self._getDrawFlag(),
  272. self.region['center_easting'], self.region['center_northing'],
  273. self.mapObj.width, self.mapObj.height,
  274. max(self.region['nsres'], self.region['ewres'])).contents
  275. # ResetTopology()
  276. self.dc.BeginDrawing()
  277. self.dcTmp.BeginDrawing()
  278. # draw objects
  279. for i in range(rlist.nitems):
  280. robj = rlist.item[i].contents
  281. self._drawObject(robj)
  282. self.dc.EndDrawing()
  283. self.dcTmp.EndDrawing()
  284. # reset list of selected features by cat
  285. # list of ids - see IsSelected()
  286. ### selected.field = -1;
  287. ### Vect_reset_list(selected.cats);
  288. def SelectLinesByBox(self):
  289. pass
  290. def SelectLineByPoint(self):
  291. pass
  292. def GetSelected(self, grassId = False):
  293. """!Get ids of selected objects
  294. @param grassId if true return GRASS line ids, false to return PseudoDC ids
  295. @return list of ids of selected vector objects
  296. """
  297. if grassId:
  298. # return ListToVector(selected.ids);
  299. pass
  300. dc_ids = list()
  301. if not self.drawSegments:
  302. dc_ids.append(1)
  303. else:
  304. # only first selected feature
  305. # Vect_read_line(byref(self.mapInfo), byref(self.points), None,
  306. # self.selected.ids->value[0]);
  307. npoints = self.points.n_points
  308. # node - segment - vertex - segment - node
  309. for i in range(1, 2 * self.points.npoints):
  310. dc_ids.append(i)
  311. return dc_ids
  312. def GetSelectedCoord(self):
  313. pass
  314. def GetDuplicates(self):
  315. pass
  316. def GetRegionSelected(self):
  317. pass
  318. def SetSelected(self, ids, layer = -1):
  319. """!Set selected vector objects
  320. @param ids list of feature ids to be set
  321. @param layer field number (-1 for ids instead of cats)
  322. """
  323. if ids:
  324. self.drawSelected = True
  325. else:
  326. self.drawSelected = False
  327. return
  328. if layer > 0:
  329. selected.field = layer
  330. # VectorToList(selected.cats, id);
  331. else:
  332. field = -1
  333. # VectorToList(selected.ids, id);
  334. return 1
  335. def UnSelect(self):
  336. pass
  337. def GetSelectedVertex(self):
  338. pass
  339. def DrawSelected(self):
  340. pass
  341. def CloseMap(self):
  342. """!Close vector map
  343. @return 0 on success
  344. @return non-zero on error
  345. """
  346. ret = 0
  347. if self.mapInfo:
  348. if self.mapInfo.mode == GV_MODE_RW:
  349. # rebuild topology
  350. Vect_build_partial(byref(self.mapInfo), GV_BUILD_NONE)
  351. Vect_build(byref(self.mapInfo))
  352. # close map and store topo/cidx
  353. ret = Vect_close(byref(self.mapInfo))
  354. del self.mapInfo
  355. self.mapInfo = None
  356. return ret
  357. def OpenMap(self, name, mapset, update = True):
  358. """!Open vector map by the driver
  359. @param name name of vector map to be open
  360. @param mapset name of mapset where the vector map lives
  361. @return topo level on success
  362. @return -1 on error
  363. """
  364. Debug.msg("DisplayDriver.OpenMap(): name=%s mapset=%s updated=%d",
  365. name, mapset, update)
  366. if not self.mapInfo:
  367. self.mapInfo = Map_info()
  368. # define open level (level 2: topology)
  369. Vect_set_open_level(2)
  370. # avoid GUI crash when G_fatal_error() is called (opening the vector map)
  371. Vect_set_fatal_error(GV_FATAL_PRINT)
  372. # open existing map
  373. if update:
  374. ret = Vect_open_update(byref(self.mapInfo), name, mapset)
  375. else:
  376. ret = Vect_open_old(byref(self.mapInfo), name, mapset)
  377. if ret == -1: # error
  378. del self.mapInfo
  379. self.mapInfo = None
  380. return ret
  381. def ReloadMap(self):
  382. pass
  383. def SetDevice(self):
  384. pass
  385. def GetMapBoundingBox(self):
  386. """!Get bounding box of (opened) vector map layer
  387. @return (w,s,b,e,n,t)
  388. """
  389. if not self.mapInfo:
  390. return None
  391. bbox = bound_box()
  392. Vect_get_map_box(byref(self.mapInfo), byref(bbox))
  393. return bbox.W, bbox.S, bbox.B, \
  394. bbox.E, bbox.N, bbox.T
  395. def Is3D(self):
  396. pass
  397. def SetRegion(self):
  398. pass
  399. def UpdateSettings(self, alpha = 255):
  400. """!Update display driver settings
  401. @todo map units
  402. @alpha color value for aplha channel
  403. """
  404. color = dict()
  405. for key in self.settings.keys():
  406. if key == 'lineWidth':
  407. self.settings[key] = int(UserSettings.Get(group = 'vdigit', key = 'lineWidth',
  408. subkey = 'value'))
  409. continue
  410. color = wx.Color(UserSettings.Get(group = 'vdigit', key = 'symbol',
  411. subkey = [key, 'color'])[0],
  412. UserSettings.Get(group = 'vdigit', key = 'symbol',
  413. subkey = [key, 'color'])[1],
  414. UserSettings.Get(group = 'vdigit', key = 'symbol',
  415. subkey = [key, 'color'])[2])
  416. if key == 'highlight':
  417. self.settings[key] = color
  418. continue
  419. if key == 'highlightDupl':
  420. self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'checkForDupl',
  421. subkey = 'enabled'))
  422. else:
  423. self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'symbol',
  424. subkey = [key, 'enabled']))
  425. self.settings[key]['color'] = color
  426. def UpdateRegion(self):
  427. """!Update geographical region used by display driver.
  428. """
  429. self.region = self.mapObj.GetCurrentRegion()
  430. def GetThreshold(self, type = 'snapping', value = None, units = None):
  431. """!Return threshold in map units
  432. @param value threshold to be set up
  433. @param units units (map, screen)
  434. """
  435. if value is None:
  436. value = UserSettings.Get(group = 'vdigit', key = type, subkey =' value')
  437. if units is None:
  438. units = UserSettings.Get(group = 'vdigit', key = type, subkey = 'units')
  439. if value < 0:
  440. value = (self.region['nsres'] + self.region['ewres']) / 2.0
  441. if units == "screen pixels":
  442. # pixel -> cell
  443. res = max(self.region['nsres'], self.region['ewres'])
  444. threshold = value * res
  445. else:
  446. threshold = value
  447. Debug.msg(4, "DisplayDriver.GetThreshold(): type=%s, thresh=%f" % (type, threshold))
  448. return threshold