mapwindow.py 44 KB

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