mapdisp_vdigit.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. """!
  2. @package mapdisp_vdigit.py
  3. @brief Map display canvas extended for vector digitizer
  4. See also vdigit.py, wxvdriver.py and wxvdigit.py
  5. Classes:
  6. - VDigitWindow
  7. (C) 2011 by the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. @author Martin Landa <landa.martin gmail.com>
  12. """
  13. import wx
  14. import dbm_dialogs
  15. from debug import Debug
  16. from mapdisp_window import BufferedWindow
  17. from preferences import globalSettings as UserSettings
  18. from vdigit import VDigitCategoryDialog
  19. from vdigit import VDigitZBulkDialog
  20. from vdigit import VDigitDuplicatesDialog
  21. class VDigitWindow(BufferedWindow):
  22. """!A Buffered window extended for vector digitizer.
  23. """
  24. def __init__(self, parent, id = wx.ID_ANY,
  25. Map = None, tree = None, lmgr = None,
  26. style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
  27. BufferedWindow.__init__(self, parent, id, Map, tree, lmgr,
  28. style, **kwargs)
  29. self.pdcVector = wx.PseudoDC()
  30. self.toolbar = self.parent.toolbars['vdigit']
  31. self.digit = None
  32. self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
  33. def SetToolbar(self, toolbar):
  34. """!Set up related toolbar
  35. """
  36. self.toolbar = toolbar
  37. def _onMotion(self, coord, precision):
  38. """!Track mouse motion and update statusbar (see self.Motion)
  39. @parem coord easting, northing
  40. @param precision formatting precision
  41. """
  42. e, n = coord
  43. if self.toolbar.GetAction() != 'addLine' or \
  44. self.toolbar.GetAction('type') not in ('line', 'boundary') or \
  45. len(self.polycoords) == 0:
  46. return False
  47. # for linear feature show segment and total length
  48. distance_seg = self.Distance(self.polycoords[-1],
  49. (e, n), screen = False)[0]
  50. distance_tot = distance_seg
  51. for idx in range(1, len(self.polycoords)):
  52. distance_tot += self.Distance(self.polycoords[idx-1],
  53. self.polycoords[idx],
  54. screen = False)[0]
  55. self.parent.statusbar.SetStatusText("%.*f, %.*f (seg: %.*f; tot: %.*f)" % \
  56. (precision, e, precision, n,
  57. precision, distance_seg,
  58. precision, distance_tot), 0)
  59. return True
  60. def OnKeyDown(self, event):
  61. """!Key pressed"""
  62. shift = event.ShiftDown()
  63. kc = event.GetKeyCode()
  64. event = None
  65. if not shift:
  66. if kc == ord('P'):
  67. event = wx.CommandEvent(winid = self.toolbar.addPoint)
  68. tool = self.toolbar.OnAddPoint
  69. elif kc == ord('L'):
  70. event = wx.CommandEvent(winid = self.toolbar.addLine)
  71. tool = self.toolbar.OnAddLine
  72. if event:
  73. self.toolbar.OnTool(event)
  74. tool(event)
  75. def _updateMap(self):
  76. if not self.toolbar or \
  77. not self.toolbar.GetLayer():
  78. return
  79. # set region
  80. self.digit.GetDisplay().UpdateRegion()
  81. # re-calculate threshold for digitization tool
  82. # self.parent.digit.GetDisplay().GetThreshold()
  83. # draw map
  84. # self.pdcVector.Clear()
  85. self.pdcVector.RemoveAll()
  86. try:
  87. item = self.tree.FindItemByData('maplayer', self.toolbar.GetLayer())
  88. except TypeError:
  89. item = None
  90. if item and self.tree.IsItemChecked(item):
  91. self.redrawAll = True
  92. self.digit.GetDisplay().DrawMap()
  93. # translate tmp objects (pointer position)
  94. if self.toolbar.GetAction() == 'moveLine' and \
  95. hasattr(self, "moveInfo"):
  96. if 'beginDiff' in self.moveInfo:
  97. # move line
  98. for id in self.moveInfo['id']:
  99. self.pdcTmp.TranslateId(id,
  100. self.moveInfo['beginDiff'][0],
  101. self.moveInfo['beginDiff'][1])
  102. del self.moveInfo['beginDiff']
  103. def OnLeftDownAddLine(self, event):
  104. """!Left mouse button pressed - add new feature
  105. """
  106. try:
  107. mapLayer = self.toolbar.GetLayer().GetName()
  108. except:
  109. return
  110. if self.toolbar.GetAction('type') in ['point', 'centroid']:
  111. # add new point / centroiud
  112. east, north = self.Pixel2Cell(self.mouse['begin'])
  113. nfeat, fids = self.digit.AddFeature(self.toolbar.GetAction('type'), [(east, north)])
  114. if nfeat < 1:
  115. return
  116. self.UpdateMap(render = False) # redraw map
  117. # add new record into atribute table
  118. if UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled'):
  119. # select attributes based on layer and category
  120. cats = { fids[0] : {
  121. UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value') :
  122. (UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'), )
  123. }}
  124. posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
  125. self.mouse['end'][1] + self.dialogOffset))
  126. addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent = self, map = mapLayer,
  127. cats = cats,
  128. pos = posWindow,
  129. action = "add")
  130. if self.toolbar.GetAction('type') == 'centroid':
  131. for fid in fids:
  132. self._geomAttrb(fid, addRecordDlg, 'area')
  133. self._geomAttrb(fid, addRecordDlg, 'perimeter')
  134. if addRecordDlg.mapDBInfo and \
  135. addRecordDlg.ShowModal() == wx.ID_OK:
  136. sqlfile = tempfile.NamedTemporaryFile(mode = "w")
  137. for sql in addRecordDlg.GetSQLString():
  138. sqlfile.file.write(sql + ";\n")
  139. sqlfile.file.flush()
  140. gcmd.RunCommand('db.execute',
  141. parent = self,
  142. quiet = True,
  143. input = sqlfile.name)
  144. if addRecordDlg.mapDBInfo:
  145. self._updateATM()
  146. elif self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
  147. # add new point to the line
  148. self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
  149. self.DrawLines(pdc = self.pdcTmp)
  150. def _geomAttrb(self, fid, dialog, attrb):
  151. """!Define geometry attributes
  152. """
  153. mapLayer = self.toolbar.GetLayer()
  154. item = self.tree.FindItemByData('maplayer', mapLayer)
  155. vdigit = self.tree.GetPyData(item)[0]['vdigit']
  156. if not vdigit or \
  157. 'geomAttr' not in vdigit or \
  158. attrb not in vdigit['geomAttr']:
  159. return
  160. val = -1
  161. if attrb == 'length':
  162. val = self.digit.GetLineLength(fid)
  163. type = attrb
  164. elif attrb == 'area':
  165. val = self.digit.GetAreaSize(fid)
  166. type = attrb
  167. elif attrb == 'perimeter':
  168. val = self.digit.GetAreaPerimeter(fid)
  169. type = 'length'
  170. if val > 0:
  171. layer = int(UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value'))
  172. column = vdigit['geomAttr'][attrb]['column']
  173. val = UnitsConvertValue(val, type, vdigit['geomAttr'][attrb]['units'])
  174. dialog.SetColumnValue(layer, column, val)
  175. dialog.OnReset()
  176. def _geomAttrbUpdate(self, fids):
  177. """!Update geometry atrributes of currently selected features
  178. @param fid list feature id
  179. """
  180. mapLayer = self.parent.toolbars['vdigit'].GetLayer()
  181. vectorName = mapLayer.GetName()
  182. item = self.tree.FindItemByData('maplayer', mapLayer)
  183. vdigit = self.tree.GetPyData(item)[0]['vdigit']
  184. if vdigit is None or 'geomAttr' not in vdigit:
  185. return
  186. dbInfo = gselect.VectorDBInfo(vectorName)
  187. sqlfile = tempfile.NamedTemporaryFile(mode = "w")
  188. for fid in fids:
  189. for layer, cats in self.digit.GetLineCats(fid).iteritems():
  190. table = dbInfo.GetTable(layer)
  191. for attrb, item in vdigit['geomAttr'].iteritems():
  192. val = -1
  193. if attrb == 'length':
  194. val = self.digit.GetLineLength(fid)
  195. type = attrb
  196. elif attrb == 'area':
  197. val = self.digit.GetAreaSize(fid)
  198. type = attrb
  199. elif attrb == 'perimeter':
  200. val = self.digit.GetAreaPerimeter(fid)
  201. type = 'length'
  202. if val < 0:
  203. continue
  204. val = UnitsConvertValue(val, type, item['units'])
  205. for cat in cats:
  206. sqlfile.write('UPDATE %s SET %s = %f WHERE %s = %d;\n' % \
  207. (table, item['column'], val,
  208. dbInfo.GetKeyColumn(layer), cat))
  209. sqlfile.file.flush()
  210. gcmd.RunCommand('db.execute',
  211. parent = True,
  212. quiet = True,
  213. input = sqlfile.name)
  214. def _updateATM(self):
  215. """!Update open Attribute Table Manager
  216. @todo: use AddDataRow() instead
  217. """
  218. # update ATM
  219. digitVector = self.toolbar.GetLayer().GetName()
  220. for atm in self.lmgr.dialogs['atm']:
  221. atmVector = atm.GetVectorName()
  222. if atmVector == digitVector:
  223. layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
  224. # TODO: use AddDataRow instead
  225. atm.LoadData(layer)
  226. def OnLeftDownEditLine(self, event):
  227. """!Left mouse button pressed - edit linear feature - add new
  228. vertex.
  229. """
  230. self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
  231. self.moveInfo['id'].append(wx.NewId())
  232. self.DrawLines(pdc = self.pdcTmp)
  233. def OnLeftDownMoveLine(self, event):
  234. """!Left mouse button pressed - vector digitizer move
  235. feature/vertex, edit linear feature
  236. """
  237. self.moveInfo = dict()
  238. # geographic coordinates of initial position (left-down)
  239. self.moveInfo['begin'] = None
  240. # list of ids to modify
  241. self.moveInfo['id'] = list()
  242. if self.toolbar.GetAction() in ["moveVertex", "editLine"]:
  243. # set pen
  244. pcolor = UserSettings.Get(group = 'vdigit', key = "symbol",
  245. subkey = ["highlight", "color"])
  246. self.pen = self.polypen = wx.Pen(colour = pcolor,
  247. width = 2, style = wx.SHORT_DASH)
  248. self.pdcTmp.SetPen(self.polypen)
  249. def OnLeftDownDisplayCA(self, event):
  250. """!Left mouse button pressed - vector digitizer display categories
  251. or attributes action
  252. """
  253. try:
  254. mapLayer = self.toolbar.GetLayer().GetName()
  255. except:
  256. return
  257. coords = self.Pixel2Cell(self.mouse['begin'])
  258. # unselect
  259. self.digit.GetDisplay().SetSelected([])
  260. # select feature by point
  261. cats = {}
  262. if self.digit.GetDisplay().SelectLineByPoint(coords) is None:
  263. return
  264. if UserSettings.Get(group = 'vdigit', key = 'checkForDupl',
  265. subkey = 'enabled'):
  266. lines = self.digit.GetDisplay().GetSelected()
  267. else:
  268. lines = (self.digit.GetDisplay().GetSelected()[0],) # only first found
  269. for line in lines:
  270. cats[line] = self.digit.GetLineCats(line)
  271. posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
  272. self.mouse['end'][1] + self.dialogOffset))
  273. if self.toolbar.GetAction() == "displayAttrs":
  274. # select attributes based on coordinates (all layers)
  275. if self.parent.dialogs['attributes'] is None:
  276. self.parent.dialogs['attributes'] = \
  277. dbm_dialogs.DisplayAttributesDialog(parent = self, map = mapLayer,
  278. cats = cats,
  279. action = "update")
  280. else:
  281. # upgrade dialog
  282. self.parent.dialogs['attributes'].UpdateDialog(cats = cats)
  283. if self.parent.dialogs['attributes']:
  284. if len(cats.keys()) > 0:
  285. # highlight feature & re-draw map
  286. if not self.parent.dialogs['attributes'].IsShown():
  287. self.parent.dialogs['attributes'].Show()
  288. else:
  289. if self.parent.dialogs['attributes'] and \
  290. self.parent.dialogs['attributes'].IsShown():
  291. self.parent.dialogs['attributes'].Hide()
  292. else: # displayCats
  293. if self.parent.dialogs['category'] is None:
  294. # open new dialog
  295. dlg = VDigitCategoryDialog(parent = self,
  296. map = mapLayer,
  297. cats = cats,
  298. pos = posWindow,
  299. title = _("Update categories"))
  300. self.parent.dialogs['category'] = dlg
  301. else:
  302. # update currently open dialog
  303. self.parent.dialogs['category'].UpdateDialog(cats = cats)
  304. if self.parent.dialogs['category']:
  305. if len(cats.keys()) > 0:
  306. # highlight feature & re-draw map
  307. if not self.parent.dialogs['category'].IsShown():
  308. self.parent.dialogs['category'].Show()
  309. else:
  310. if self.parent.dialogs['category'].IsShown():
  311. self.parent.dialogs['category'].Hide()
  312. self.UpdateMap(render = False, renderVector = True)
  313. def OnLeftDownCopyCA(self, event):
  314. """!Left mouse button pressed - vector digitizer copy
  315. categories or attributes action
  316. """
  317. if not hasattr(self, "copyCatsList"):
  318. self.copyCatsList = []
  319. else:
  320. self.copyCatsIds = []
  321. self.mouse['box'] = 'box'
  322. def OnLeftDownCopyLine(self, event):
  323. """!Left mouse button pressed - vector digitizer copy lines
  324. action
  325. """
  326. if not hasattr(self, "copyIds"):
  327. self.copyIds = []
  328. self.layerTmp = None
  329. def OnLeftDownBulkLine(self, event):
  330. """!Left mouse button pressed - vector digitizer label 3D
  331. vector lines
  332. """
  333. if len(self.polycoords) > 1: # start new line
  334. self.polycoords = []
  335. self.ClearLines(pdc = self.pdcTmp)
  336. self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
  337. if len(self.polycoords) == 1:
  338. begin = self.Pixel2Cell(self.polycoords[-1])
  339. end = self.Pixel2Cell(self.mouse['end'])
  340. else:
  341. end = self.Pixel2Cell(self.polycoords[-1])
  342. begin = self.Pixel2Cell(self.mouse['begin'])
  343. self.DrawLines(self.pdcTmp, polycoords = (begin, end))
  344. def OnLeftDownUndo(self, event):
  345. """!Left mouse button pressed with control key - vector
  346. digitizer undo functionality
  347. """
  348. if self.mouse["use"] != "pointer" or not self.toolbar:
  349. return
  350. if (self.toolbar.GetAction() == "addLine" and \
  351. self.toolbar.GetAction('type') in ["line", "boundary", "area"]) or \
  352. self.toolbar.GetAction() == "editLine":
  353. # add line or boundary -> remove last point from the line
  354. try:
  355. removed = self.polycoords.pop()
  356. Debug.msg(4, "BufferedWindow.OnMiddleDown(): polycoords_poped=%s" % \
  357. [removed,])
  358. # self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
  359. except:
  360. pass
  361. if self.toolbar.GetAction() == "editLine":
  362. # remove last vertex & line
  363. if len(self.moveInfo['id']) > 1:
  364. self.moveInfo['id'].pop()
  365. self.UpdateMap(render = False, renderVector = False)
  366. elif self.toolbar.GetAction() in ["deleteLine", "moveLine", "splitLine",
  367. "addVertex", "removeVertex", "moveVertex",
  368. "copyCats", "flipLine", "mergeLine",
  369. "snapLine", "connectLine", "copyLine",
  370. "queryLine", "breakLine", "typeConv"]:
  371. # varios tools -> unselected selected features
  372. self.digit.GetDisplay().SetSelected([])
  373. if self.toolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] and \
  374. hasattr(self, "moveInfo"):
  375. del self.moveInfo
  376. elif self.toolbar.GetAction() == "copyCats":
  377. try:
  378. del self.copyCatsList
  379. del self.copyCatsIds
  380. except AttributeError:
  381. pass
  382. elif self.toolbar.GetAction() == "copyLine":
  383. del self.copyIds
  384. if self.layerTmp:
  385. self.Map.DeleteLayer(self.layerTmp)
  386. self.UpdateMap(render = True, renderVector = False)
  387. del self.layerTmp
  388. self.polycoords = []
  389. self.UpdateMap(render = False) # render vector
  390. elif self.toolbar.GetAction() == "zbulkLine":
  391. # reset polyline
  392. self.polycoords = []
  393. self.digit.GetDisplay().SetSelected([])
  394. self.UpdateMap(render = False)
  395. self.redrawAll = True
  396. self.UpdateMap(render = False, renderVector = False)
  397. def _onLeftDown(self, event):
  398. """!Left mouse button donw - vector digitizer various actions
  399. """
  400. try:
  401. mapLayer = self.toolbar.GetLayer().GetName()
  402. except:
  403. wx.MessageBox(parent = self,
  404. message = _("No vector map selected for editing."),
  405. caption = _("Vector digitizer"),
  406. style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
  407. event.Skip()
  408. return
  409. if self.toolbar.GetAction() not in ("moveVertex",
  410. "addVertex",
  411. "removeVertex",
  412. "editLine"):
  413. # set pen
  414. self.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
  415. self.polypen = wx.Pen(colour = 'dark green', width = 2, style = wx.SOLID)
  416. if self.toolbar.GetAction() in ("addVertex",
  417. "removeVertex",
  418. "splitLines"):
  419. # unselect
  420. self.digit.GetDisplay().SetSelected([])
  421. if self.toolbar.GetAction() == "addLine":
  422. self.OnLeftDownAddLine(event)
  423. elif self.toolbar.GetAction() == "editLine" and \
  424. hasattr(self, "moveInfo"):
  425. self.OnLeftDownEditLine(event)
  426. elif self.toolbar.GetAction() in ("moveLine", "moveVertex", "editLine") and \
  427. not hasattr(self, "moveInfo"):
  428. self.OnLeftDownMoveLine(event)
  429. elif self.toolbar.GetAction() in ("displayAttrs"
  430. "displayCats"):
  431. self.OnLeftDownDisplayCA(event)
  432. elif self.toolbar.GetAction() in ("copyCats",
  433. "copyAttrs"):
  434. self.OnLeftDownCopyCA(event)
  435. elif self.toolbar.GetAction() == "copyLine":
  436. self.OnLeftDownCopyLine(event)
  437. elif self.toolbar.GetAction() == "zbulkLine":
  438. self.OnLeftDownBulkLine(event)
  439. def _onLeftUp(self, event):
  440. """!Left mouse button released - vector digitizer various
  441. actions
  442. """
  443. pos1 = self.Pixel2Cell(self.mouse['begin'])
  444. pos2 = self.Pixel2Cell(self.mouse['end'])
  445. nselected = 0
  446. # -> delete line || move line || move vertex
  447. if self.toolbar.GetAction() in ("moveVertex",
  448. "editLine"):
  449. if len(self.digit.GetDisplay().GetSelected()) == 0:
  450. nselected = self.digit.GetDisplay().SelectLineByPoint(pos1)['point']
  451. if self.toolbar.GetAction() == "editLine":
  452. try:
  453. selVertex = self.digit.GetDisplay().GetSelectedVertex(pos1)[0]
  454. except IndexError:
  455. selVertex = None
  456. if selVertex:
  457. # self.UpdateMap(render=False)
  458. ids = self.digit.GetDisplay().GetSelected(grassId = False)
  459. # move this line to tmp layer
  460. self.polycoords = []
  461. for id in ids:
  462. if id % 2: # register only vertices
  463. e, n = self.Pixel2Cell(self.pdcVector.GetIdBounds(id)[0:2])
  464. self.polycoords.append((e, n))
  465. self.digit.GetDisplay().DrawSelected(False)
  466. if selVertex < ids[-1] / 2:
  467. # choose first or last node of line
  468. self.moveInfo['id'].reverse()
  469. self.polycoords.reverse()
  470. else:
  471. # unselect
  472. self.digit.GetDisplay().SetSelected([])
  473. del self.moveInfo
  474. self.UpdateMap(render = False)
  475. elif self.toolbar.GetAction() in ("copyCats",
  476. "copyAttrs"):
  477. if not hasattr(self, "copyCatsIds"):
  478. # 'from' -> select by point
  479. nselected = self.digit.GetDisplay().SelectLineByPoint(pos1)['point']
  480. if nselected:
  481. self.copyCatsList = self.digit.GetDisplay().GetSelected()
  482. else:
  483. # -> 'to' -> select by bbox
  484. self.digit.GetDisplay().SetSelected([])
  485. # return number of selected features (by box/point)
  486. nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2))
  487. if nselected == 0:
  488. if self.digit.GetDisplay().SelectLineByPoint(pos1) is not None:
  489. nselected = 1
  490. if nselected > 0:
  491. self.copyCatsIds = self.digit.GetDisplay().GetSelected()
  492. elif self.toolbar.GetAction() == "queryLine":
  493. selected = self.digit.SelectLinesByQuery(bbox = (pos1, pos2))
  494. nselected = len(selected)
  495. if nselected > 0:
  496. self.digit.GetDisplay().SetSelected(selected)
  497. else:
  498. # -> moveLine || deleteLine, etc. (select by point/box)
  499. if self.toolbar.GetAction() == 'moveLine' and \
  500. len(self.digit.GetDisplay().GetSelected()) > 0:
  501. nselected = 0
  502. else:
  503. if self.toolbar.GetAction() == 'moveLine':
  504. drawSeg = True
  505. else:
  506. drawSeg = False
  507. nselected = self.digit.GetDisplay().SelectLinesByBox(bbox = (pos1, pos2),
  508. drawSeg = drawSeg)
  509. if nselected == 0:
  510. if self.digit.GetDisplay().SelectLineByPoint(pos1) is not None:
  511. nselected = 1
  512. if nselected > 0:
  513. if self.toolbar.GetAction() in ("moveLine", "moveVertex") and \
  514. hasattr(self, "moveInfo"):
  515. # get pseudoDC id of objects which should be redrawn
  516. if self.toolbar.GetAction() == "moveLine":
  517. # -> move line
  518. self.moveInfo['id'] = self.digit.GetDisplay().GetSelected(grassId = False)
  519. else: # moveVertex
  520. self.moveInfo['id'] = self.digit.GetDisplay().GetSelectedVertex(pos1)
  521. if len(self.moveInfo['id']) == 0: # no vertex found
  522. self.digit.GetDisplay().SetSelected([])
  523. #
  524. # check for duplicates
  525. #
  526. if UserSettings.Get(group = 'vdigit', key = 'checkForDupl', subkey = 'enabled'):
  527. dupl = self.digit.GetDisplay().GetDuplicates()
  528. self.UpdateMap(render = False)
  529. if dupl:
  530. posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
  531. self.mouse['end'][1] + self.dialogOffset))
  532. dlg = VDigitDuplicatesDialog(parent = self, data = dupl, pos = posWindow)
  533. if dlg.ShowModal() == wx.ID_OK:
  534. self.digit.GetDisplay().UnSelect(dlg.GetUnSelected())
  535. # update selected
  536. self.UpdateMap(render = False)
  537. if self.toolbar.GetAction() != "editLine":
  538. # -> move line || move vertex
  539. self.UpdateMap(render = False)
  540. else: # no vector object found
  541. if not (self.toolbar.GetAction() in ("moveLine",
  542. "moveVertex") and \
  543. hasattr(self, "moveInfo") and \
  544. len(self.moveInfo['id']) > 0):
  545. # avoid left-click when features are already selected
  546. self.UpdateMap(render = False, renderVector = False)
  547. def OnLeftUpModifyLine(self, event):
  548. """!Left mouse button released - vector digitizer split line,
  549. add/remove vertex action
  550. """
  551. pos1 = self.Pixel2Cell(self.mouse['begin'])
  552. pointOnLine = self.digit.GetDisplay().SelectLineByPoint(pos1)['point']
  553. if not pointOnLine:
  554. return
  555. if self.toolbar.GetAction() in ["splitLine", "addVertex"]:
  556. self.UpdateMap(render = False) # highlight object
  557. self.DrawCross(pdc = self.pdcTmp, coords = self.Cell2Pixel((pointOnLine[0], pointOnLine[1])),
  558. size = 5)
  559. else: # removeVertex
  560. # get only id of vertex
  561. try:
  562. id = self.digit.GetDisplay().GetSelectedVertex(pos1)[0]
  563. except IndexError:
  564. id = None
  565. if id:
  566. x, y = self.pdcVector.GetIdBounds(id)[0:2]
  567. self.pdcVector.RemoveId(id)
  568. self.UpdateMap(render = False) # highlight object
  569. self.DrawCross(pdc = self.pdcTmp, coords = (x, y),
  570. size = 5)
  571. else:
  572. # unselect
  573. self.digit.GetDisplay().SetSelected([])
  574. self.UpdateMap(render = False)
  575. def OnLeftUpCopyLine(self, event):
  576. """!Left mouse button released - vector digitizer copy feature
  577. action
  578. """
  579. pos1 = self.Pixel2Cell(self.mouse['begin'])
  580. pos2 = self.Pixel2Cell(self.mouse['end'])
  581. if UserSettings.Get(group = 'vdigit', key = 'bgmap',
  582. subkey = 'value', internal = True) == '':
  583. # no background map -> copy from current vector map layer
  584. nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2))
  585. if nselected > 0:
  586. # highlight selected features
  587. self.UpdateMap(render = False)
  588. else:
  589. self.UpdateMap(render = False, renderVector = False)
  590. else:
  591. # copy features from background map
  592. self.copyIds += self.digit.SelectLinesFromBackgroundMap(bbox = (pos1, pos2))
  593. if len(self.copyIds) > 0:
  594. color = UserSettings.Get(group = 'vdigit', key = 'symbol',
  595. subkey = ['highlight', 'color'])
  596. colorStr = str(color[0]) + ":" + \
  597. str(color[1]) + ":" + \
  598. str(color[2])
  599. dVectTmp = ['d.vect',
  600. 'map=%s' % UserSettings.Get(group = 'vdigit', key = 'bgmap',
  601. subkey = 'value', internal = True),
  602. 'cats=%s' % utils.ListOfCatsToRange(self.copyIds),
  603. '-i',
  604. 'color=%s' % colorStr,
  605. 'fcolor=%s' % colorStr,
  606. 'type=point,line,boundary,centroid',
  607. 'width=2']
  608. if not self.layerTmp:
  609. self.layerTmp = self.Map.AddLayer(type = 'vector',
  610. name = globalvar.QUERYLAYER,
  611. command = dVectTmp)
  612. else:
  613. self.layerTmp.SetCmd(dVectTmp)
  614. self.UpdateMap(render = True, renderVector = False)
  615. else:
  616. self.UpdateMap(render = False, renderVector = False)
  617. self.redrawAll = None
  618. def OnLeftUpBulkLine(self, event):
  619. """!Left mouse button released - vector digitizer z-bulk line
  620. action
  621. """
  622. # select lines to be labeled
  623. pos1 = self.polycoords[0]
  624. pos2 = self.polycoords[1]
  625. nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2))
  626. if nselected > 0:
  627. # highlight selected features
  628. self.UpdateMap(render = False)
  629. self.DrawLines(pdc = self.pdcTmp) # redraw temp line
  630. else:
  631. self.UpdateMap(render = False, renderVector = False)
  632. def OnLeftUpConnectLine(self, event):
  633. """!Left mouse button released - vector digitizer connect line
  634. action
  635. """
  636. if len(self.digit.GetDisplay().GetSelected()) > 0:
  637. self.UpdateMap(render = False)
  638. def OnLeftUp(self, event):
  639. if hasattr(self, "moveInfo"):
  640. if len(self.digit.GetDisplay().GetSelected()) == 0:
  641. self.moveInfo['begin'] = self.Pixel2Cell(self.mouse['begin']) # left down
  642. # eliminate initial mouse moving efect
  643. self.mouse['begin'] = self.mouse['end']
  644. if self.toolbar.GetAction() in ("deleteLine",
  645. "moveLine",
  646. "moveVertex",
  647. "copyCats",
  648. "copyAttrs",
  649. "editLine",
  650. "flipLine",
  651. "mergeLine",
  652. "snapLine",
  653. "queryLine",
  654. "breakLine",
  655. "typeConv",
  656. "connectLine"):
  657. self._onLeftUp(event)
  658. elif self.toolbar.GetAction() in ("splitLine",
  659. "addVertex",
  660. "removeVertex"):
  661. self.OnLeftUpModifyLine(event)
  662. elif self.toolbar.GetAction() == "copyLine":
  663. self.OnLeftUpCopyLine(event)
  664. elif self.toolbar.GetAction() == "zbulkLine" and \
  665. len(self.polycoords) == 2:
  666. self.OnLeftUpBulkLine(event)
  667. elif self.toolbar.GetAction() == "connectLine":
  668. self.OnLeftUpConnectLine(event)
  669. if len(self.digit.GetDisplay().GetSelected()) > 0:
  670. self.redrawAll = None
  671. def _onRightDown(self, event):
  672. # digitization tool (confirm action)
  673. if self.toolbar.GetAction() in ("moveLine", "moveVertex") and \
  674. hasattr(self, "moveInfo"):
  675. pFrom = self.moveInfo['begin']
  676. pTo = self.Pixel2Cell(event.GetPositionTuple())
  677. move = (pTo[0] - pFrom[0],
  678. pTo[1] - pFrom[1])
  679. if self.toolbar.GetAction() == "moveLine":
  680. # move line
  681. if self.digit.MoveSelectedLines(move) < 0:
  682. return
  683. elif self.toolbar.GetAction() == "moveVertex":
  684. # move vertex
  685. fid = self.digit.MoveSelectedVertex(pFrom, move)
  686. if fid < 0:
  687. return
  688. self._geomAttrbUpdate([fid,])
  689. del self.moveInfo
  690. def _onRightUp(self, event):
  691. """!Right mouse button released
  692. """
  693. # digitization tool (confirm action)
  694. if self.toolbar.GetAction() == "addLine" and \
  695. self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
  696. # -> add new line / boundary
  697. try:
  698. mapName = self.toolbar.GetLayer().GetName()
  699. except:
  700. mapName = None
  701. gcmd.GError(parent = self,
  702. message = _("No vector map selected for editing."))
  703. if mapName:
  704. if self.toolbar.GetAction('type') == 'line':
  705. line = True
  706. else:
  707. line = False
  708. if len(self.polycoords) < 2: # ignore 'one-point' lines
  709. return
  710. nfeat, fids = self.digit.AddFeature(self.toolbar.GetAction('type'), self.polycoords)
  711. if nfeat < 0:
  712. return
  713. position = self.Cell2Pixel(self.polycoords[-1])
  714. self.polycoords = []
  715. self.UpdateMap(render = False)
  716. self.redrawAll = True
  717. self.Refresh()
  718. # add new record into atribute table
  719. if UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled') and \
  720. (line is True or \
  721. (not line and nfeat > 0)):
  722. posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
  723. position[1] + self.dialogOffset))
  724. # select attributes based on layer and category
  725. cats = { fids[0] : {
  726. UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value') :
  727. (UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'), )
  728. }}
  729. addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent = self, map = mapName,
  730. cats = cats,
  731. pos = posWindow,
  732. action = "add")
  733. for fid in fids:
  734. self._geomAttrb(fid, addRecordDlg, 'length')
  735. # auto-placing centroid
  736. self._geomAttrb(fid, addRecordDlg, 'area')
  737. self._geomAttrb(fid, addRecordDlg, 'perimeter')
  738. if addRecordDlg.mapDBInfo and \
  739. addRecordDlg.ShowModal() == wx.ID_OK:
  740. sqlfile = tempfile.NamedTemporaryFile(mode = "w")
  741. for sql in addRecordDlg.GetSQLString():
  742. sqlfile.file.write(sql + ";\n")
  743. sqlfile.file.flush()
  744. gcmd.RunCommand('db.execute',
  745. parent = True,
  746. quiet = True,
  747. input = sqlfile.name)
  748. if addRecordDlg.mapDBInfo:
  749. self._updateATM()
  750. elif self.toolbar.GetAction() == "deleteLine":
  751. # -> delete selected vector features
  752. if self.digit.DeleteSelectedLines() < 0:
  753. return
  754. self._updateATM()
  755. elif self.toolbar.GetAction() == "splitLine":
  756. # split line
  757. if self.digit.SplitLine(self.Pixel2Cell(self.mouse['begin'])) < 0:
  758. return
  759. elif self.toolbar.GetAction() == "addVertex":
  760. # add vertex
  761. fid = self.digit.AddVertex(self.Pixel2Cell(self.mouse['begin']))
  762. if fid < 0:
  763. return
  764. elif self.toolbar.GetAction() == "removeVertex":
  765. # remove vertex
  766. fid = self.digit.RemoveVertex(self.Pixel2Cell(self.mouse['begin']))
  767. if fid < 0:
  768. return
  769. self._geomAttrbUpdate([fid,])
  770. elif self.toolbar.GetAction() in ("copyCats", "copyAttrs"):
  771. try:
  772. if self.toolbar.GetAction() == 'copyCats':
  773. if self.digit.CopyCats(self.copyCatsList,
  774. self.copyCatsIds, copyAttrb = False) < 0:
  775. return
  776. else:
  777. if self.digit.CopyCats(self.copyCatsList,
  778. self.copyCatsIds, copyAttrb = True) < 0:
  779. return
  780. del self.copyCatsList
  781. del self.copyCatsIds
  782. except AttributeError:
  783. pass
  784. self._updateATM()
  785. elif self.toolbar.GetAction() == "editLine" and \
  786. hasattr(self, "moveInfo"):
  787. line = self.digit.GetDisplay().GetSelected()
  788. if self.digit.EditLine(line, self.polycoords) < 0:
  789. return
  790. del self.moveInfo
  791. elif self.toolbar.GetAction() == "flipLine":
  792. if self.digit.FlipLine() < 0:
  793. return
  794. elif self.toolbar.GetAction() == "mergeLine":
  795. if self.digit.MergeLine() < 0:
  796. return
  797. elif self.toolbar.GetAction() == "breakLine":
  798. if self.digit.BreakLine() < 0:
  799. return
  800. elif self.toolbar.GetAction() == "snapLine":
  801. if self.digit.SnapLine() < 0:
  802. return
  803. elif self.toolbar.GetAction() == "connectLine":
  804. if len(self.digit.GetDisplay().GetSelected()) > 1:
  805. if self.digit.ConnectLine() < 0:
  806. return
  807. elif self.toolbar.GetAction() == "copyLine":
  808. if self.digit.CopyLine(self.copyIds) < 0:
  809. return
  810. del self.copyIds
  811. if self.layerTmp:
  812. self.Map.DeleteLayer(self.layerTmp)
  813. self.UpdateMap(render = True, renderVector = False)
  814. del self.layerTmp
  815. elif self.toolbar.GetAction() == "zbulkLine" and len(self.polycoords) == 2:
  816. pos1 = self.polycoords[0]
  817. pos2 = self.polycoords[1]
  818. selected = self.digit.GetDisplay().GetSelected()
  819. dlg = VDigitZBulkDialog(parent = self, title = _("Z bulk-labeling dialog"),
  820. nselected = len(selected))
  821. if dlg.ShowModal() == wx.ID_OK:
  822. if self.digit.ZBulkLines(pos1, pos2, dlg.value.GetValue(),
  823. dlg.step.GetValue()) < 0:
  824. return
  825. self.UpdateMap(render = False, renderVector = True)
  826. elif self.toolbar.GetAction() == "typeConv":
  827. # -> feature type conversion
  828. # - point <-> centroid
  829. # - line <-> boundary
  830. if self.digit.TypeConvForSelectedLines() < 0:
  831. return
  832. if self.toolbar.GetAction() != "addLine":
  833. # unselect and re-render
  834. self.digit.GetDisplay().SetSelected([])
  835. self.polycoords = []
  836. self.UpdateMap(render = False)
  837. def _onMouseMoving(self, event):
  838. self.mouse['end'] = event.GetPositionTuple()[:]
  839. Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
  840. (self.mouse['end'][0], self.mouse['end'][1]))
  841. if self.toolbar.GetAction() == "addLine" and \
  842. self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
  843. if len(self.polycoords) > 0:
  844. self.MouseDraw(pdc = self.pdcTmp, begin = self.Cell2Pixel(self.polycoords[-1]))
  845. elif self.toolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] \
  846. and hasattr(self, "moveInfo"):
  847. dx = self.mouse['end'][0] - self.mouse['begin'][0]
  848. dy = self.mouse['end'][1] - self.mouse['begin'][1]
  849. if len(self.moveInfo['id']) > 0:
  850. # draw lines on new position
  851. if self.toolbar.GetAction() == "moveLine":
  852. # move line
  853. for id in self.moveInfo['id']:
  854. self.pdcTmp.TranslateId(id, dx, dy)
  855. elif self.toolbar.GetAction() in ["moveVertex", "editLine"]:
  856. # move vertex ->
  857. # (vertex, left vertex, left line,
  858. # right vertex, right line)
  859. # do not draw static lines
  860. if self.toolbar.GetAction() == "moveVertex":
  861. self.polycoords = []
  862. ### self.pdcTmp.TranslateId(self.moveInfo['id'][0], dx, dy)
  863. self.pdcTmp.RemoveId(self.moveInfo['id'][0])
  864. if self.moveInfo['id'][1] > 0: # previous vertex
  865. x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.moveInfo['id'][1])[0:2])
  866. self.pdcTmp.RemoveId(self.moveInfo['id'][1] + 1)
  867. self.polycoords.append((x, y))
  868. ### x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.moveInfo['id'][0])[0:2])
  869. self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
  870. if self.moveInfo['id'][2] > 0: # next vertex
  871. x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.moveInfo['id'][2])[0:2])
  872. self.pdcTmp.RemoveId(self.moveInfo['id'][2]-1)
  873. self.polycoords.append((x, y))
  874. self.ClearLines(pdc = self.pdcTmp)
  875. self.DrawLines(pdc = self.pdcTmp)
  876. else: # edit line
  877. try:
  878. if self.moveInfo['id'][-1] > 0: # previous vertex
  879. self.MouseDraw(pdc = self.pdcTmp,
  880. begin = self.Cell2Pixel(self.polycoords[-1]))
  881. except: # no line
  882. self.moveInfo['id'] = []
  883. self.polycoords = []
  884. self.Refresh() # TODO: use RefreshRect()
  885. self.mouse['begin'] = self.mouse['end']
  886. elif self.toolbar.GetAction() == "zbulkLine":
  887. if len(self.polycoords) == 1:
  888. # draw mouse moving
  889. self.MouseDraw(self.pdcTmp)
  890. def _zoom(self, event):
  891. tmp1 = self.mouse['end']
  892. tmp2 = self.Cell2Pixel(self.moveInfo['begin'])
  893. dx = tmp1[0] - tmp2[0]
  894. dy = tmp1[1] - tmp2[1]
  895. self.moveInfo['beginDiff'] = (dx, dy)
  896. for id in self.moveInfo['id']:
  897. self.pdcTmp.RemoveId(id)