mapwindow.py 44 KB

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