mapdisp_vdigit.py 44 KB

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