mapdisp_vdigit.py 44 KB

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