mapwindow.py 45 KB

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