mapwindow.py 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  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.globalvar import QUERYLAYER
  22. from vdigit.dialogs import VDigitCategoryDialog, VDigitZBulkDialog, VDigitDuplicatesDialog
  23. from gui_core import gselect
  24. from gui_core.wrap import PseudoDC, NewId
  25. class VDigitWindow(BufferedMapWindow):
  26. """A Buffered window extended for vector digitizer.
  27. """
  28. def __init__(self, parent, giface, Map, properties, tree=None,
  29. id=wx.ID_ANY, lmgr=None,
  30. style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
  31. BufferedMapWindow.__init__(self, parent=parent, giface=giface, Map=Map,
  32. properties=properties,
  33. style=style, **kwargs)
  34. self.lmgr = lmgr
  35. self.tree = tree
  36. self.pdcVector = PseudoDC()
  37. self.toolbar = self.parent.GetToolbar('vdigit')
  38. self.digit = None # wxvdigit.IVDigit
  39. self._digitizingInfo = False # digitizing with info
  40. # Emitted when info about digitizing updated
  41. # Parameter text is a string with information
  42. # currently used only for coordinates of mouse cursor + segmnt and
  43. # total feature length
  44. self.digitizingInfo = Signal('VDigitWindow.digitizingInfo')
  45. # Emitted when some info about digitizing is or will be availbale
  46. self.digitizingInfoAvailable = Signal('VDigitWindow.digitizingInfo')
  47. # Emitted when some info about digitizing is or will be availbale
  48. # digitizingInfo signal is emmited only between digitizingInfoAvailable
  49. # and digitizingInfoUnavailable signals
  50. self.digitizingInfoUnavailable = Signal('VDigitWindow.digitizingInfo')
  51. self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
  52. self.mouseMoving.connect(self._mouseMovingToDigitizingInfo)
  53. def GetDisplay(self):
  54. if self.digit:
  55. return self.digit.GetDisplay()
  56. return None
  57. def GetDigit(self):
  58. """Get digit class"""
  59. return self.digit
  60. def SetToolbar(self, toolbar):
  61. """Set up related toolbar
  62. """
  63. self.toolbar = toolbar
  64. def _mouseMovingToDigitizingInfo(self, x, y):
  65. e, n = x, y
  66. precision = int(UserSettings.Get(group='projection', key='format',
  67. subkey='precision'))
  68. if self.toolbar.GetAction() != 'addLine' or \
  69. self.toolbar.GetAction('type') not in ('line', 'boundary') or \
  70. len(self.polycoords) == 0:
  71. # we cannot provide info, so find out if it is something new
  72. if self._digitizingInfo:
  73. self._digitizingInfo = False
  74. self.digitizingInfoUnavailable.emit()
  75. return
  76. # else, we can provide info, so find out if it is first time
  77. if not self._digitizingInfo:
  78. self._digitizingInfo = True
  79. self.digitizingInfoAvailable.emit()
  80. # for linear feature show segment and total length
  81. distance_seg = self.Distance(self.polycoords[-1],
  82. (e, n), screen=False)[0]
  83. distance_tot = distance_seg
  84. for idx in range(1, len(self.polycoords)):
  85. distance_tot += self.Distance(self.polycoords[idx - 1],
  86. self.polycoords[idx],
  87. screen=False)[0]
  88. text = "seg: %.*f; tot: %.*f" % (precision, distance_seg,
  89. precision, distance_tot)
  90. self.digitizingInfo.emit(text=text)
  91. def OnKeyDown(self, event):
  92. """Key pressed"""
  93. shift = event.ShiftDown()
  94. kc = event.GetKeyCode()
  95. event = None
  96. if not shift:
  97. if kc == ord('P'):
  98. event = wx.CommandEvent(winid=self.toolbar.addPoint)
  99. tool = self.toolbar.OnAddPoint
  100. elif kc == ord('L'):
  101. event = wx.CommandEvent(winid=self.toolbar.addLine)
  102. tool = self.toolbar.OnAddLine
  103. if event:
  104. self.toolbar.OnTool(event)
  105. tool(event)
  106. def _updateMap(self):
  107. if not self.toolbar or \
  108. not self.toolbar.GetLayer():
  109. return
  110. # set region
  111. self.digit.GetDisplay().UpdateRegion()
  112. # re-calculate threshold for digitization tool
  113. # self.parent.digit.GetDisplay().GetThreshold()
  114. # draw map
  115. # self.pdcVector.Clear()
  116. self.pdcVector.RemoveAll()
  117. item = None
  118. if self.tree:
  119. try:
  120. item = self.tree.FindItemByData(
  121. 'maplayer', self.toolbar.GetLayer())
  122. except TypeError:
  123. pass
  124. if not self.tree or \
  125. (self.tree and item and
  126. self.tree.IsItemChecked(item)):
  127. self.redrawAll = True
  128. self.digit.GetDisplay().DrawMap()
  129. # translate tmp objects (pointer position)
  130. if self.toolbar.GetAction() == 'moveLine' and \
  131. hasattr(self, "moveInfo"):
  132. if 'beginDiff' in self.moveInfo:
  133. # move line
  134. for id in self.moveInfo['id']:
  135. self.pdcTmp.TranslateId(id,
  136. self.moveInfo['beginDiff'][0],
  137. self.moveInfo['beginDiff'][1])
  138. del self.moveInfo['beginDiff']
  139. def OnLeftDownAddLine(self, event):
  140. """Left mouse button pressed - add new feature
  141. """
  142. try:
  143. mapLayer = self.toolbar.GetLayer().GetName()
  144. except:
  145. return
  146. if self.toolbar.GetAction('type') in ['point', 'centroid']:
  147. # add new point / centroiud
  148. east, north = self.Pixel2Cell(self.mouse['begin'])
  149. nfeat, fids = self.digit.AddFeature(
  150. self.toolbar.GetAction('type'),
  151. [(east, north)])
  152. if nfeat < 1:
  153. return
  154. self.UpdateMap(render=False) # redraw map
  155. # add new record into atribute table
  156. if UserSettings.Get(
  157. group='vdigit', key="addRecord", subkey='enabled'):
  158. # select attributes based on layer and category
  159. cats = {
  160. fids[0]: {
  161. UserSettings.Get(
  162. group='vdigit',
  163. key="layer",
  164. subkey='value'): (
  165. UserSettings.Get(
  166. group='vdigit',
  167. key="category",
  168. subkey='value'),
  169. )}}
  170. posWindow = self.ClientToScreen(
  171. (self.mouse['end'][0] + self.dialogOffset,
  172. self.mouse['end'][1] + self.dialogOffset))
  173. addRecordDlg = DisplayAttributesDialog(
  174. parent=self, map=mapLayer, cats=cats, pos=posWindow,
  175. action="add", ignoreError=True)
  176. if self.toolbar.GetAction('type') == 'centroid':
  177. for fid in fids:
  178. self._geomAttrb(fid, addRecordDlg, 'area')
  179. self._geomAttrb(fid, addRecordDlg, 'perimeter')
  180. if addRecordDlg.IsFound():
  181. addRecordDlg.ShowModal()
  182. addRecordDlg.Destroy()
  183. elif self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
  184. # add new point to the line
  185. self.polycoords.append(
  186. self.Pixel2Cell(
  187. event.GetPosition()))
  188. self.DrawLines(pdc=self.pdcTmp)
  189. def _geomAttrb(self, fid, dialog, attrb):
  190. """Define geometry attributes
  191. """
  192. mapLayer = self.toolbar.GetLayer()
  193. if self.tree:
  194. item = self.tree.FindItemByData('maplayer', mapLayer)
  195. vdigit = self.tree.GetLayerInfo(item, key='vdigit')
  196. else:
  197. item = vdigit = None
  198. if not vdigit or \
  199. 'geomAttr' not in vdigit or \
  200. attrb not in vdigit['geomAttr']:
  201. return
  202. val = -1
  203. if attrb == 'length':
  204. val = self.digit.GetLineLength(fid)
  205. type = attrb
  206. elif attrb == 'area':
  207. val = self.digit.GetAreaSize(fid)
  208. type = attrb
  209. elif attrb == 'perimeter':
  210. val = self.digit.GetAreaPerimeter(fid)
  211. type = 'length'
  212. if val > 0:
  213. layer = int(
  214. UserSettings.Get(
  215. group='vdigit',
  216. key="layer",
  217. subkey='value'))
  218. column = vdigit['geomAttr'][attrb]['column']
  219. val = UnitsConvertValue(
  220. val, type, vdigit['geomAttr'][attrb]['units'])
  221. dialog.SetColumnValue(layer, column, val)
  222. dialog.OnReset()
  223. def _geomAttrbUpdate(self, fids):
  224. """Update geometry atrributes of currently selected features
  225. :param fid: list feature id
  226. """
  227. mapLayer = self.parent.toolbars['vdigit'].GetLayer()
  228. vectorName = mapLayer.GetName()
  229. if self.tree:
  230. item = self.tree.FindItemByData('maplayer', mapLayer)
  231. vdigit = self.tree.GetLayerInfo(item, key='vdigit')
  232. else:
  233. item = vdigit = None
  234. if not vdigit or 'geomAttr' not in vdigit:
  235. return
  236. dbInfo = gselect.VectorDBInfo(vectorName)
  237. sqlfile = tempfile.NamedTemporaryFile(mode="w")
  238. for fid in fids:
  239. for layer, cats in six.iteritems(self.digit.GetLineCats(fid)):
  240. table = dbInfo.GetTable(layer)
  241. for attrb, item in six.iteritems(vdigit['geomAttr']):
  242. val = -1
  243. if attrb == 'length':
  244. val = self.digit.GetLineLength(fid)
  245. type = attrb
  246. elif attrb == 'area':
  247. val = self.digit.GetAreaSize(fid)
  248. type = attrb
  249. elif attrb == 'perimeter':
  250. val = self.digit.GetAreaPerimeter(fid)
  251. type = 'length'
  252. if val < 0:
  253. continue
  254. val = UnitsConvertValue(val, type, item['units'])
  255. for cat in cats:
  256. sqlfile.write(
  257. 'UPDATE %s SET %s = %f WHERE %s = %d;\n' %
  258. (table, item['column'], val, dbInfo.GetKeyColumn(layer), cat))
  259. sqlfile.file.flush()
  260. RunCommand('db.execute',
  261. parent=True,
  262. quiet=True,
  263. input=sqlfile.name)
  264. def _updateATM(self):
  265. """Update open Attribute Table Manager
  266. .. todo::
  267. use AddDataRow() instead
  268. """
  269. if not self.lmgr:
  270. return
  271. # update ATM
  272. digitVector = self.toolbar.GetLayer().GetName()
  273. for atm in self.lmgr.dialogs['atm']:
  274. atmVector = atm.GetVectorName()
  275. if atmVector == digitVector:
  276. layer = UserSettings.Get(
  277. group='vdigit', key="layer", subkey='value')
  278. # TODO: use AddDataRow instead
  279. atm.LoadData(layer)
  280. def OnLeftDownEditLine(self, event):
  281. """Left mouse button pressed - edit linear feature - add new
  282. vertex.
  283. """
  284. self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
  285. self.moveInfo['id'].append(NewId())
  286. self.DrawLines(pdc=self.pdcTmp)
  287. def OnLeftDownMoveLine(self, event):
  288. """Left mouse button pressed - vector digitizer move
  289. feature/vertex, edit linear feature
  290. """
  291. self.moveInfo = dict()
  292. # geographic coordinates of initial position (left-down)
  293. self.moveInfo['begin'] = None
  294. # list of ids to modify
  295. self.moveInfo['id'] = list()
  296. # set pen
  297. if self.toolbar.GetAction() in ["moveVertex", "editLine"]:
  298. pcolor = UserSettings.Get(group='vdigit', key="symbol",
  299. subkey=["highlight", "color"])
  300. self.pen = self.polypen = wx.Pen(colour=pcolor,
  301. width=2, style=wx.SHORT_DASH)
  302. self.pdcTmp.SetPen(self.polypen)
  303. def OnLeftDownDisplayCA(self, event):
  304. """Left mouse button pressed - vector digitizer display categories
  305. or attributes action
  306. """
  307. try:
  308. mapLayer = self.toolbar.GetLayer().GetName()
  309. except:
  310. return
  311. coords = self.Pixel2Cell(self.mouse['begin'])
  312. # unselect
  313. self.digit.GetDisplay().SetSelected([])
  314. # select feature by point
  315. cats = {}
  316. self.digit.GetDisplay().SelectLineByPoint(coords)
  317. if not self.digit.GetDisplay().GetSelected():
  318. for key in ('attributes', 'category'):
  319. if self.parent.dialogs[key] and \
  320. self.parent.dialogs[key].IsShown():
  321. self.parent.dialogs[key].Hide()
  322. self.UpdateMap(render=False, renderVector=True)
  323. return
  324. if UserSettings.Get(group='vdigit', key='checkForDupl',
  325. subkey='enabled'):
  326. lines = self.digit.GetDisplay().GetSelected()
  327. else:
  328. lines = (
  329. self.digit.GetDisplay().GetSelected()[0],
  330. ) # only first found
  331. for line in lines:
  332. cats[line] = self.digit.GetLineCats(line)
  333. posWindow = self.ClientToScreen(
  334. (self.mouse['end'][0] + self.dialogOffset,
  335. self.mouse['end'][1] + self.dialogOffset))
  336. if self.toolbar.GetAction() == "displayAttrs":
  337. # select attributes based on coordinates (all layers)
  338. if self.parent.dialogs['attributes'] is None:
  339. self.parent.dialogs['attributes'] = \
  340. DisplayAttributesDialog(parent=self, map=mapLayer,
  341. cats=cats,
  342. action="update")
  343. else:
  344. # upgrade dialog
  345. self.parent.dialogs['attributes'].UpdateDialog(cats=cats)
  346. if self.parent.dialogs['attributes'] and \
  347. self.parent.dialogs['attributes'].mapDBInfo:
  348. if len(cats.keys()) > 0:
  349. # highlight feature & re-draw map
  350. if not self.parent.dialogs['attributes'].IsShown():
  351. self.parent.dialogs['attributes'].Show()
  352. else:
  353. if self.parent.dialogs['attributes'] and \
  354. self.parent.dialogs['attributes'].IsShown():
  355. self.parent.dialogs['attributes'].Hide()
  356. else: # displayCats
  357. if self.parent.dialogs['category'] is None:
  358. # open new dialog
  359. dlg = VDigitCategoryDialog(parent=self,
  360. vectorName=mapLayer,
  361. cats=cats,
  362. pos=posWindow,
  363. title=_("Update categories"))
  364. self.parent.dialogs['category'] = dlg
  365. else:
  366. # update currently open dialog
  367. self.parent.dialogs['category'].UpdateDialog(cats=cats)
  368. if self.parent.dialogs['category']:
  369. if len(cats.keys()) > 0:
  370. # highlight feature & re-draw map
  371. if not self.parent.dialogs['category'].IsShown():
  372. self.parent.dialogs['category'].Show()
  373. else:
  374. if self.parent.dialogs['category'].IsShown():
  375. self.parent.dialogs['category'].Hide()
  376. self.UpdateMap(render=False, renderVector=True)
  377. def OnLeftDownCopyCA(self, event):
  378. """Left mouse button pressed - vector digitizer copy
  379. categories or attributes action
  380. """
  381. if not hasattr(self, "copyCatsList"):
  382. self.copyCatsList = []
  383. else:
  384. self.copyCatsIds = []
  385. self.mouse['box'] = 'box'
  386. def OnLeftDownCopyLine(self, event):
  387. """Left mouse button pressed - vector digitizer copy lines
  388. action
  389. """
  390. if not hasattr(self, "copyIds"):
  391. self.copyIds = []
  392. self.layerTmp = None
  393. def OnLeftDownBulkLine(self, event):
  394. """Left mouse button pressed - vector digitizer label 3D
  395. vector lines
  396. """
  397. if len(self.polycoords) > 1: # start new line
  398. self.polycoords = []
  399. self.ClearLines(pdc=self.pdcTmp)
  400. self.polycoords.append(self.Pixel2Cell(event.GetPosition()))
  401. if len(self.polycoords) == 1:
  402. begin = self.Pixel2Cell(self.polycoords[-1])
  403. end = self.Pixel2Cell(self.mouse['end'])
  404. else:
  405. end = self.Pixel2Cell(self.polycoords[-1])
  406. begin = self.Pixel2Cell(self.mouse['begin'])
  407. self.DrawLines(self.pdcTmp, polycoords=(begin, end))
  408. def OnLeftDownUndo(self, event):
  409. """Left mouse button pressed with control key - vector
  410. digitizer undo functionality
  411. """
  412. if self.mouse["use"] != "pointer" or not self.toolbar:
  413. return
  414. action = self.toolbar.GetAction()
  415. if (action == "addLine" and self.toolbar.GetAction('type')
  416. in ["line", "boundary", "area"]) or action == "editLine":
  417. # add line or boundary -> remove last point from the line
  418. try:
  419. removed = self.polycoords.pop()
  420. Debug.msg(
  421. 4, "VDigitWindow.OnMiddleDown(): polycoords_poped=%s" %
  422. [removed, ])
  423. # self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
  424. except:
  425. pass
  426. if action == "editLine":
  427. # remove last vertex & line
  428. if len(self.moveInfo['id']) > 1:
  429. self.moveInfo['id'].pop()
  430. self.UpdateMap(render=False, renderVector=False)
  431. elif action in ["deleteLine", "deleteArea", "moveLine", "splitLine",
  432. "addVertex", "removeVertex", "moveVertex",
  433. "copyCats", "flipLine", "mergeLine",
  434. "snapLine", "connectLine", "copyLine",
  435. "queryLine", "breakLine", "typeConv"]:
  436. # various tools -> unselected selected features
  437. self.digit.GetDisplay().SetSelected([])
  438. if action in ["moveLine", "moveVertex", "editLine"] and \
  439. hasattr(self, "moveInfo"):
  440. del self.moveInfo
  441. elif action == "copyCats":
  442. try:
  443. del self.copyCatsList
  444. del self.copyCatsIds
  445. except AttributeError:
  446. pass
  447. elif action == "copyLine":
  448. del self.copyIds
  449. if self.layerTmp:
  450. self.Map.DeleteLayer(self.layerTmp)
  451. self.UpdateMap(render=True, renderVector=False)
  452. del self.layerTmp
  453. self.polycoords = []
  454. self.UpdateMap(render=False) # render vector
  455. elif action == "zbulkLine":
  456. # reset polyline
  457. self.polycoords = []
  458. self.digit.GetDisplay().SetSelected([])
  459. self.UpdateMap(render=False)
  460. self.redrawAll = True
  461. self.UpdateMap(render=False, renderVector=False)
  462. def _onLeftDown(self, event):
  463. """Left mouse button donw - vector digitizer various actions
  464. """
  465. try:
  466. mapLayer = self.toolbar.GetLayer().GetName()
  467. except:
  468. GMessage(parent=self,
  469. message=_("No vector map selected for editing."))
  470. event.Skip()
  471. return
  472. action = self.toolbar.GetAction()
  473. if not action:
  474. GMessage(
  475. parent=self, message=_(
  476. "Nothing to do. "
  477. "Choose appropriate tool from digitizer toolbar."))
  478. event.Skip()
  479. return
  480. if action not in ("moveVertex",
  481. "addVertex",
  482. "removeVertex",
  483. "editLine"):
  484. # set pen
  485. self.pen = wx.Pen(
  486. colour=UserSettings.Get(
  487. group='vdigit',
  488. key='symbol',
  489. subkey=[
  490. 'newSegment',
  491. 'color']),
  492. width=2,
  493. style=wx.SHORT_DASH)
  494. self.polypen = wx.Pen(
  495. colour=UserSettings.Get(
  496. group='vdigit',
  497. key='symbol',
  498. subkey=[
  499. 'newLine',
  500. 'color']),
  501. width=2,
  502. style=wx.SOLID)
  503. if action in ("addVertex",
  504. "removeVertex",
  505. "splitLines"):
  506. # unselect
  507. self.digit.GetDisplay().SetSelected([])
  508. if action == "addLine":
  509. self.OnLeftDownAddLine(event)
  510. elif action == "editLine" and \
  511. hasattr(self, "moveInfo"):
  512. self.OnLeftDownEditLine(event)
  513. elif action in ("moveLine", "moveVertex", "editLine") and \
  514. not hasattr(self, "moveInfo"):
  515. self.OnLeftDownMoveLine(event)
  516. elif action in ("displayAttrs"
  517. "displayCats"):
  518. self.OnLeftDownDisplayCA(event)
  519. elif action in ("copyCats",
  520. "copyAttrs"):
  521. self.OnLeftDownCopyCA(event)
  522. elif action == "copyLine":
  523. self.OnLeftDownCopyLine(event)
  524. elif action == "zbulkLine":
  525. self.OnLeftDownBulkLine(event)
  526. def OnLeftUpVarious(self, event):
  527. """Left mouse button released - vector digitizer various
  528. actions
  529. """
  530. pos1 = self.Pixel2Cell(self.mouse['begin'])
  531. pos2 = self.Pixel2Cell(self.mouse['end'])
  532. nselected = 0
  533. action = self.toolbar.GetAction()
  534. # -> delete line || move line || move vertex
  535. if action in ("moveVertex",
  536. "editLine"):
  537. if len(self.digit.GetDisplay().GetSelected()) == 0:
  538. nselected = int(
  539. self.digit.GetDisplay().SelectLineByPoint(pos1)['line'] != -1)
  540. if action == "editLine":
  541. try:
  542. selVertex = self.digit.GetDisplay(
  543. ).GetSelectedVertex(pos1)[0]
  544. except IndexError:
  545. selVertex = None
  546. if selVertex:
  547. # self.UpdateMap(render=False)
  548. ids = self.digit.GetDisplay().GetSelected(grassId=False)
  549. # move this line to tmp layer
  550. self.polycoords = []
  551. for id in ids:
  552. if id % 2: # register only vertices
  553. e, n = self.Pixel2Cell(
  554. self.pdcVector.GetIdBounds(id)[0:2])
  555. self.polycoords.append((e, n))
  556. self.digit.GetDisplay().DrawSelected(False)
  557. if selVertex < ids[-1] / 2:
  558. # choose first or last node of line
  559. self.moveInfo['id'].reverse()
  560. self.polycoords.reverse()
  561. else:
  562. # unselect
  563. self.digit.GetDisplay().SetSelected([])
  564. if hasattr(self, "moveInfo"):
  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 atribute 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(
  892. [
  893. fid,
  894. ]
  895. )
  896. elif action in ("copyCats", "copyAttrs") and hasattr(self, "copyCatsIds"):
  897. if action == "copyCats":
  898. if (
  899. self.digit.CopyCats(
  900. self.copyCatsList, self.copyCatsIds, copyAttrb=False
  901. )
  902. < 0
  903. ):
  904. return
  905. else:
  906. if self.digit.CopyCats(self.copyCatsList,
  907. self.copyCatsIds, copyAttrb=True) < 0:
  908. return
  909. del self.copyCatsList
  910. del self.copyCatsIds
  911. self._updateATM()
  912. elif action == "editLine" and \
  913. hasattr(self, "moveInfo"):
  914. line = self.digit.GetDisplay().GetSelected()[0]
  915. if self.digit.EditLine(line, self.polycoords) < 0:
  916. return
  917. del self.moveInfo
  918. elif action == "flipLine":
  919. if self.digit.FlipLine() < 0:
  920. return
  921. elif action == "mergeLine":
  922. if self.digit.MergeLine() < 0:
  923. return
  924. elif action == "breakLine":
  925. if self.digit.BreakLine() < 0:
  926. return
  927. elif action == "snapLine":
  928. if self.digit.SnapLine() < 0:
  929. return
  930. elif action == "connectLine":
  931. if len(self.digit.GetDisplay().GetSelected()) > 1:
  932. if self.digit.ConnectLine() < 0:
  933. return
  934. elif action == "copyLine":
  935. if self.digit.CopyLine(self.copyIds) < 0:
  936. return
  937. del self.copyIds
  938. if self.layerTmp:
  939. self.Map.DeleteLayer(self.layerTmp)
  940. self.UpdateMap(render=True, renderVector=False)
  941. del self.layerTmp
  942. elif action == "zbulkLine" and len(self.polycoords) == 2:
  943. pos1 = self.polycoords[0]
  944. pos2 = self.polycoords[1]
  945. selected = self.digit.GetDisplay().GetSelected()
  946. dlg = VDigitZBulkDialog(
  947. parent=self,
  948. title=_("Z bulk-labeling dialog"),
  949. nselected=len(selected))
  950. if dlg.ShowModal() == wx.ID_OK:
  951. if self.digit.ZBulkLines(pos1, pos2, dlg.value.GetValue(),
  952. dlg.step.GetValue()) < 0:
  953. return
  954. self.UpdateMap(render=False)
  955. elif action == "typeConv":
  956. # -> feature type conversion
  957. # - point <-> centroid
  958. # - line <-> boundary
  959. if self.digit.TypeConvForSelectedLines() < 0:
  960. return
  961. if action != "addLine":
  962. # unselect and re-render
  963. self.digit.GetDisplay().SetSelected([])
  964. self.polycoords = []
  965. self.UpdateMap(render=False)
  966. def _onMouseMoving(self, event):
  967. self.mouse['end'] = event.GetPosition()
  968. Debug.msg(5, "VDigitWindow.OnMouseMoving(): coords=%f,%f" %
  969. (self.mouse['end'][0], self.mouse['end'][1]))
  970. action = self.toolbar.GetAction()
  971. if action == "addLine" and \
  972. self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
  973. if len(self.polycoords) > 0:
  974. self.MouseDraw(pdc=self.pdcTmp,
  975. begin=self.Cell2Pixel(self.polycoords[-1]))
  976. elif action in ["moveLine", "moveVertex", "editLine"] \
  977. and hasattr(self, "moveInfo"):
  978. dx = self.mouse['end'][0] - self.mouse['begin'][0]
  979. dy = self.mouse['end'][1] - self.mouse['begin'][1]
  980. # draw lines on new position
  981. if action == "moveLine" and \
  982. len(self.moveInfo['id']) > 0:
  983. # move line
  984. for id in self.moveInfo['id']:
  985. self.pdcTmp.TranslateId(id, dx, dy)
  986. elif action in ["moveVertex", "editLine"]:
  987. # move vertex ->
  988. # (vertex, left vertex, left line,
  989. # right vertex, right line)
  990. # do not draw static lines
  991. if action == "moveVertex" and \
  992. len(self.moveInfo['id']) > 0:
  993. self.polycoords = []
  994. self.pdcTmp.RemoveId(self.moveInfo['id'][0])
  995. if self.moveInfo['id'][1] > 0: # previous vertex
  996. x, y = self.Pixel2Cell(
  997. self.pdcTmp.GetIdBounds(
  998. self.moveInfo['id'][1])[
  999. 0:2])
  1000. self.pdcTmp.RemoveId(self.moveInfo['id'][1] + 1)
  1001. self.polycoords.append((x, y))
  1002. self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
  1003. if self.moveInfo['id'][2] > 0: # next vertex
  1004. x, y = self.Pixel2Cell(
  1005. self.pdcTmp.GetIdBounds(
  1006. self.moveInfo['id'][2])[
  1007. 0:2])
  1008. self.pdcTmp.RemoveId(self.moveInfo['id'][2] - 1)
  1009. self.polycoords.append((x, y))
  1010. self.ClearLines(pdc=self.pdcTmp)
  1011. self.DrawLines(pdc=self.pdcTmp)
  1012. if action == "editLine":
  1013. if self.polycoords:
  1014. self.MouseDraw(
  1015. pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1])
  1016. )
  1017. self.Refresh() # TODO: use RefreshRect()
  1018. self.mouse['begin'] = self.mouse['end']
  1019. elif action == "zbulkLine":
  1020. if len(self.polycoords) == 1:
  1021. # draw mouse moving
  1022. self.MouseDraw(self.pdcTmp)
  1023. def _zoom(self, event):
  1024. tmp1 = self.mouse['end']
  1025. tmp2 = self.Cell2Pixel(self.moveInfo['begin'])
  1026. dx = tmp1[0] - tmp2[0]
  1027. dy = tmp1[1] - tmp2[1]
  1028. self.moveInfo['beginDiff'] = (dx, dy)
  1029. for id in self.moveInfo['id']:
  1030. self.pdcTmp.RemoveId(id)
  1031. def _addRecord(self):
  1032. return UserSettings.Get(
  1033. group='vdigit', key="addRecord", subkey='enabled')