mapwindow.py 44 KB

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