mapwindow.py 44 KB

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