mapwindow.py 45 KB

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