mapwindow.py 45 KB

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