mapwindow.py 45 KB

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