mapwindow.py 44 KB

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