mapwindow.py 45 KB

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