mapwindow.py 45 KB

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