mapdisp_vdigit.py 45 KB

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