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