mapwindow.py 46 KB

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