mapdisp_vdigit.py 45 KB

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