mapwindow.py 45 KB

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