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