mapwindow.py 46 KB

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