GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
vclean.py
Go to the documentation of this file.
1 """
2 @package modules.vclean
3 
4 @brief Dialog for interactive construction of vector cleaning
5 operations
6 
7 Classes:
8  - vclean::VectorCleaningFrame
9 
10 (C) 2010-2011 by the GRASS Development Team
11 This program is free software under the GNU General Public License
12 (>=v2). Read the file COPYING that comes with GRASS for details.
13 
14 @author Markus Metz
15 """
16 
17 import os
18 
19 import wx
20 import wx.lib.scrolledpanel as scrolled
21 
22 from grass.script import core as grass
23 
24 from core.gcmd import RunCommand, GError
25 from core import globalvar
26 from gui_core.gselect import Select
27 from core.debug import Debug
28 from core.settings import UserSettings
29 
30 class VectorCleaningFrame(wx.Frame):
31  def __init__(self, parent, id = wx.ID_ANY, title = _('Set up vector cleaning tools'),
32  style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
33  **kwargs):
34  """!
35  Dialog for interactively defining vector cleaning tools
36  """
37  wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
38 
39  self.parent = parent # GMFrame
40  if self.parent:
41  self.log = self.parent.GetLogWindow()
42  else:
43  self.log = None
44 
45  # grass command
46  self.cmd = 'v.clean'
47 
48  # statusbar
49  self.CreateStatusBar()
50 
51  # icon
52  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
53 
54  self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
55 
56  # input map to clean
57  self.inmap = ''
58 
59  # cleaned output map
60  self.outmap = ''
61 
62  self.ftype = ''
63 
64  # cleaning tools
65  self.toolslines = {}
66 
67  self.tool_desc_list = [
68  _('break lines/boundaries'),
69  _('remove duplicates'),
70  _('remove dangles'),
71  _('change boundary dangles to lines'),
72  _('remove bridges'),
73  _('change bridges to lines'),
74  _('snap lines/boundaries'),
75  _('remove duplicate area centroids'),
76  _('break polygons'),
77  _('prune lines/boundaries'),
78  _('remove small areas'),
79  _('remove lines/boundaries of zero length'),
80  _('remove small angles at nodes')
81  ]
82 
83  self.tool_list = [
84  'break',
85  'rmdupl',
86  'rmdangle',
87  'chdangle',
88  'rmbridge',
89  'chbridge',
90  'snap',
91  'rmdac',
92  'bpol',
93  'prune',
94  'rmarea',
95  'rmline',
96  'rmsa'
97  ]
98 
99  self.ftype = [
100  'point',
101  'line',
102  'boundary',
103  'centroid',
104  'area',
105  'face']
106 
107  self.n_ftypes = len(self.ftype)
108 
109  self.tools_string = ''
110  self.thresh_string = ''
111  self.ftype_string = ''
112 
113  self.SetStatusText(_("Set up vector cleaning tools"))
114  self.elem = 'vector'
115  self.ctlabel = _('Choose cleaning tools and set thresholds')
116 
117  # top controls
118  self.inmaplabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
119  label= _('Select input vector map:'))
120  self.selectionInput = Select(parent = self.panel, id = wx.ID_ANY,
121  size = globalvar.DIALOG_GSELECT_SIZE,
122  type = 'vector')
123  self.ftype_check = {}
124  ftypeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
125  label = _(' Feature type: '))
126  self.ftypeSizer = wx.StaticBoxSizer(ftypeBox, wx.HORIZONTAL)
127 
128  self.outmaplabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
129  label = _('Select output vector map:'))
130  self.selectionOutput = Select(parent = self.panel, id = wx.ID_ANY,
131  size = globalvar.DIALOG_GSELECT_SIZE,
132  type = 'vector')
133 
134  self.overwrite = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
135  label = _('Allow output files to overwrite existing files'))
136  self.overwrite.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
137 
138  # cleaning tools
139  self.ct_label = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
140  label = self.ctlabel)
141 
142  self.ct_panel = self._toolsPanel()
143 
144  # buttons to manage cleaning tools
145  self.btn_add = wx.Button(parent = self.panel, id = wx.ID_ADD)
146  self.btn_remove = wx.Button(parent = self.panel, id = wx.ID_REMOVE)
147  self.btn_moveup = wx.Button(parent = self.panel, id = wx.ID_UP)
148  self.btn_movedown = wx.Button(parent = self.panel, id = wx.ID_DOWN)
149 
150  # add one tool as default
151  self.AddTool()
152  self.selected = -1
153 
154  # Buttons
155  self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
156  self.btn_run = wx.Button(parent = self.panel, id = wx.ID_ANY, label = _("&Run"))
157  self.btn_run.SetDefault()
158  self.btn_clipboard = wx.Button(parent = self.panel, id = wx.ID_COPY)
159  self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)"))
160  self.btn_help = wx.Button(parent = self.panel, id = wx.ID_HELP)
161 
162  # bindings
163  self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
164  self.btn_run.Bind(wx.EVT_BUTTON, self.OnCleaningRun)
165  self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
166  self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
167 
168  self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddTool)
169  self.btn_remove.Bind(wx.EVT_BUTTON, self.OnClearTool)
170  self.btn_moveup.Bind(wx.EVT_BUTTON, self.OnMoveToolUp)
171  self.btn_movedown.Bind(wx.EVT_BUTTON, self.OnMoveToolDown)
172 
173  # layout
174  self._layout()
175 
176  self.SetMinSize(self.GetBestSize())
177 
178  self.CentreOnScreen()
179 
180  def _layout(self):
181  sizer = wx.BoxSizer(wx.VERTICAL)
182 
183  #
184  # input output
185  #
186  inSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
187 
188  inSizer.Add(item = self.inmaplabel, pos = (0, 0),
189  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
190  inSizer.Add(item = self.selectionInput, pos = (1, 0),
191  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
192 
193  self.ftype_check = [
194  wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('point')),
195  wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('line')),
196  wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('boundary')),
197  wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('centroid')),
198  wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('area')),
199  wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('face'))
200  ]
201 
202  typeoptSizer = wx.BoxSizer(wx.HORIZONTAL)
203  for num in range(0, self.n_ftypes):
204  type_box = self.ftype_check[num]
205  typeoptSizer.Add(item = type_box, flag = wx.ALIGN_LEFT, border = 1)
206 
207  self.ftypeSizer.Add(item = typeoptSizer,
208  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 2)
209 
210  outSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
211 
212  outSizer.Add(item = self.outmaplabel, pos = (0, 0),
213  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
214  outSizer.Add(item = self.selectionOutput, pos = (1, 0),
215  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
216  replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
217  replaceSizer.Add(item = self.overwrite, proportion = 1,
218  flag = wx.ALL | wx.EXPAND, border = 1)
219 
220  outSizer.Add(item = replaceSizer, pos = (2, 0),
221  flag = wx.ALL | wx.EXPAND, border = 1)
222 
223  #
224  # tools selection
225  #
226  bodySizer = wx.GridBagSizer(hgap = 5, vgap = 5)
227 
228  bodySizer.Add(item = self.ct_label, pos = (0, 0), span = (1, 2),
229  flag = wx.ALL, border = 5)
230 
231  bodySizer.Add(item = self.ct_panel, pos = (1, 0), span = (1, 2))
232 
233  manageBoxSizer = wx.GridBagSizer(hgap = 10, vgap = 1)
234  # start with row 1 for nicer layout
235  manageBoxSizer.Add(item = self.btn_add, pos = (1, 0), border = 2, flag = wx.ALL | wx.EXPAND)
236  manageBoxSizer.Add(item = self.btn_remove, pos = (2, 0), border = 2, flag = wx.ALL | wx.EXPAND)
237  manageBoxSizer.Add(item = self.btn_moveup, pos = (3, 0), border = 2, flag = wx.ALL | wx.EXPAND)
238  manageBoxSizer.Add(item = self.btn_movedown, pos = (4, 0), border = 2, flag = wx.ALL | wx.EXPAND)
239 
240  bodySizer.Add(item = manageBoxSizer, pos = (1, 2),
241  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
242 
243  bodySizer.AddGrowableCol(2)
244 
245  #
246  # standard buttons
247  #
248  btnSizer = wx.BoxSizer(wx.HORIZONTAL)
249  btnSizer.Add(self.btn_close,
250  flag = wx.LEFT | wx.RIGHT, border = 5)
251  btnSizer.Add(self.btn_run,
252  flag = wx.LEFT | wx.RIGHT, border = 5)
253  btnSizer.Add(self.btn_clipboard,
254  flag = wx.LEFT | wx.RIGHT, border = 5)
255  btnSizer.Add(self.btn_help,
256  flag = wx.LEFT | wx.RIGHT, border = 5)
257 
258  #
259  # put it all together
260  #
261  sizer.Add(item = inSizer, proportion = 0,
262  flag = wx.ALL | wx.EXPAND, border = 5)
263 
264  sizer.Add(item = self.ftypeSizer, proportion = 0,
265  flag = wx.ALL | wx.EXPAND, border = 5)
266 
267  sizer.Add(item = outSizer, proportion = 0,
268  flag = wx.ALL | wx.EXPAND, border = 5)
269 
270  sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
271  style = wx.LI_HORIZONTAL), proportion = 0,
272  flag = wx.EXPAND | wx.ALL, border = 5)
273 
274  sizer.Add(item = bodySizer, proportion = 1,
275  flag = wx.ALL | wx.EXPAND, border = 5)
276 
277  sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
278  style = wx.LI_HORIZONTAL), proportion = 0,
279  flag = wx.EXPAND | wx.ALL, border = 5)
280 
281  sizer.Add(item = btnSizer, proportion = 0,
282  flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
283 
284  self.panel.SetAutoLayout(True)
285  self.panel.SetSizer(sizer)
286  sizer.Fit(self.panel)
287 
288  self.Layout()
289 
290  def _toolsPanel(self):
291  ct_panel = scrolled.ScrolledPanel(parent = self.panel, id = wx.ID_ANY,
292  size = (500, 240),
293  style = wx.SUNKEN_BORDER)
294 
295  self.ct_sizer = wx.GridBagSizer(vgap = 2, hgap = 4)
296 
297  ct_panel.SetSizer(self.ct_sizer)
298  ct_panel.SetAutoLayout(True)
299 
300  return ct_panel
301 
302  def OnAddTool(self, event):
303  """!Add tool button pressed"""
304  self.AddTool()
305 
306  def AddTool(self):
307  snum = len(self.toolslines.keys())
308  num = snum + 1
309  # tool number
310  tool_no = wx.StaticText(parent = self.ct_panel, id = 3000+num,
311  label = str(num)+'.')
312  # tool
313  tool_cbox = wx.ComboBox(parent = self.ct_panel, id = 1000+num,
314  size = (300, -1), choices = self.tool_desc_list,
315  style = wx.CB_DROPDOWN |
316  wx.CB_READONLY | wx.TE_PROCESS_ENTER)
317  self.Bind(wx.EVT_COMBOBOX, self.OnSetTool, tool_cbox)
318  # threshold
319  txt_ctrl = wx.TextCtrl(parent = self.ct_panel, id = 2000+num, value = '0.00',
320  size = (100,-1),
321  style = wx.TE_NOHIDESEL)
322  self.Bind(wx.EVT_TEXT, self.OnThreshValue, txt_ctrl)
323 
324  # select
325  select = wx.CheckBox(parent = self.ct_panel, id = num)
326  select.SetValue(False)
327  self.Bind(wx.EVT_CHECKBOX, self.OnSelect, select)
328 
329  # start with row 1 and col 1 for nicer layout
330  self.ct_sizer.Add(item = tool_no, pos = (num, 1),
331  flag = wx.ALIGN_CENTER_VERTICAL, border = 5)
332  self.ct_sizer.Add(item = tool_cbox, pos = (num, 2),
333  flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
334  self.ct_sizer.Add(item = txt_ctrl, pos = (num, 3),
335  flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
336  self.ct_sizer.Add(item = select, pos = (num, 4),
337  flag = wx.ALIGN_CENTER | wx.RIGHT)
338 
339  self.toolslines[num] = {
340  'tool_desc' : '' ,
341  'tool' : '' ,
342  'thresh' : '0.00' }
343 
344  self.ct_panel.Layout()
345  self.ct_panel.SetupScrolling()
346 
347  def OnClearTool(self, event):
348  """!Remove tool button pressed"""
349  id = self.selected
350 
351  if id > 0:
352  self.FindWindowById(id+1000).SetValue('')
353  self.toolslines[id]['tool_desc'] = ''
354  self.toolslines[id]['tool'] = ''
355  self.SetStatusText(_("%s. cleaning tool removed, will be ignored") % id)
356  else:
357  self.SetStatusText(_("Please select a cleaning tool to remove"))
358 
359  def OnMoveToolUp(self, event):
360  """!Move up tool button pressed"""
361  id = self.selected
362 
363  if id > 1:
364  id_up = id - 1
365  this_toolline = self.toolslines[id]
366  up_toolline = self.toolslines[id_up]
367 
368  self.FindWindowById(id_up).SetValue(True)
369  self.FindWindowById(id_up+1000).SetValue(this_toolline['tool_desc'])
370  self.FindWindowById(id_up+2000).SetValue(this_toolline['thresh'])
371  self.toolslines[id_up] = this_toolline
372 
373  self.FindWindowById(id).SetValue(False)
374  self.FindWindowById(id+1000).SetValue(up_toolline['tool_desc'])
375  self.FindWindowById(id+2000).SetValue(up_toolline['thresh'])
376  self.toolslines[id] = up_toolline
377  self.selected = id_up
378  self.SetStatusText(_("%s. cleaning tool moved up") % id)
379  elif id == 1:
380  self.SetStatusText(_("1. cleaning tool can not be moved up "))
381  elif id == -1:
382  self.SetStatusText(_("Please select a cleaning tool to move up"))
383 
384 
385  def OnMoveToolDown(self, event):
386  """!Move down tool button pressed"""
387  id = self.selected
388  snum = len(self.toolslines.keys())
389 
390  if id > 0 and id < snum:
391  id_down = id + 1
392  this_toolline = self.toolslines[id]
393  down_toolline = self.toolslines[id_down]
394 
395  self.FindWindowById(id_down).SetValue(True)
396  self.FindWindowById(id_down+1000).SetValue(this_toolline['tool_desc'])
397  self.FindWindowById(id_down+2000).SetValue(this_toolline['thresh'])
398  self.toolslines[id_down] = this_toolline
399 
400  self.FindWindowById(id).SetValue(False)
401  self.FindWindowById(id+1000).SetValue(down_toolline['tool_desc'])
402  self.FindWindowById(id+2000).SetValue(down_toolline['thresh'])
403  self.toolslines[id] = down_toolline
404  self.selected = id_down
405  self.SetStatusText(_("%s. cleaning tool moved down") % id)
406  elif id == snum:
407  self.SetStatusText(_("Last cleaning tool can not be moved down "))
408  elif id == -1:
409  self.SetStatusText(_("Please select a cleaning tool to move down"))
410 
411  def OnSetTool(self, event):
412  """!Tool was defined"""
413  id = event.GetId()
414  tool_no = id-1000
415  num = self.FindWindowById(id).GetCurrentSelection()
416 
417  self.toolslines[tool_no]['tool_desc'] = self.tool_desc_list[num]
418  self.toolslines[tool_no]['tool'] = self.tool_list[num]
419 
420  self.SetStatusText( str(tool_no) + '. ' + _("cleaning tool: '%s'") % (self.tool_list[num]))
421 
422  def OnThreshValue(self, event):
423  """!Threshold value was entered"""
424  id = event.GetId()
425  num = id-2000
426  self.toolslines[num]['thresh'] = self.FindWindowById(id).GetValue()
427 
428  self.SetStatusText(_("Threshold for %(num)s. tool '%(tool)s': %(thresh)s") % \
429  { 'num' : num,
430  'tool' : self.toolslines[num]['tool'],
431  'thresh' : self.toolslines[num]['thresh'] })
432 
433  def OnSelect(self, event):
434  """!Tool was selected"""
435  id = event.GetId()
436 
437  if self.selected > -1 and self.selected != id:
438  win = self.FindWindowById(self.selected)
439  win.SetValue(False)
440 
441  if self.selected != id:
442  self.selected = id
443  else:
444  self.selected = -1
445 
446  def OnDone(self, cmd, returncode):
447  """!Command done"""
448  self.SetStatusText('')
449 
450  def OnCleaningRun(self, event):
451  """!Builds options and runs v.clean
452  """
453  self.GetCmdStrings()
454 
455  err = list()
456  for p, name in ((self.inmap, _('Name of input vector map')),
457  (self.outmap, _('Name for output vector map')),
458  (self.tools_string, _('Tools')),
459  (self.thresh_string, _('Threshold'))):
460  if not p:
461  err.append(_("'%s' not defined") % name)
462  if err:
463  GError(_("Some parameters not defined. Operation "
464  "canceled.\n\n%s") % '\n'.join(err),
465  parent = self)
466  return
467 
468  self.SetStatusText(_("Executing selected cleaning operations..."))
469  snum = len(self.toolslines.keys())
470 
471  if self.log:
472  cmd = [ self.cmd,
473  'input=%s' % self.inmap,
474  'output=%s' % self.outmap,
475  'tool=%s' % self.tools_string,
476  'thres=%s' % self.thresh_string ]
477  if self.ftype_string:
478  cmd.append('type=%s' % self.ftype_string)
479  if self.overwrite.IsChecked():
480  cmd.append('--overwrite')
481 
482  self.log.RunCmd(cmd, onDone = self.OnDone)
483  self.parent.Raise()
484  else:
485  if self.overwrite.IsChecked():
486  overwrite = True
487  else:
488  overwrite = False
489 
490  RunCommand(self.cmd,
491  input = self.inmap,
492  output = self.outmap,
493  type = self.ftype_string,
494  tool = self.tools_string,
495  thresh = self.thresh_string,
496  overwrite = overwrite)
497 
498  def OnClose(self, event):
499  self.Destroy()
500 
501  def OnHelp(self, event):
502  """!Show GRASS manual page"""
503  RunCommand('g.manual',
504  quiet = True,
505  parent = self,
506  entry = self.cmd)
507 
508  def OnCopy(self, event):
509  """!Copy the command"""
510  cmddata = wx.TextDataObject()
511  # get tool and thresh strings
512  self.GetCmdStrings()
513  cmdstring = '%s' % (self.cmd)
514  # list -> string
515  cmdstring += ' input=%s output=%s type=%s tool=%s thres=%s' % \
516  (self.inmap, self.outmap, self.ftype_string, self.tools_string, self.thresh_string)
517  if self.overwrite.IsChecked():
518  cmdstring += ' --overwrite'
519 
520  cmddata.SetText(cmdstring)
521  if wx.TheClipboard.Open():
522  wx.TheClipboard.SetData(cmddata)
523  wx.TheClipboard.Close()
524  self.SetStatusText(_("Vector cleaning command copied to clipboard"))
525 
526  def GetCmdStrings(self):
527  self.tools_string = ''
528  self.thresh_string = ''
529  self.ftype_string = ''
530  # feature types
531  first = 1
532  for num in range(0, self.n_ftypes - 1):
533  if self.ftype_check[num].IsChecked():
534  if first:
535  self.ftype_string = '%s' % self.ftype[num]
536  first = 0
537  else:
538  self.ftype_string += ',%s' % self.ftype[num]
539 
540 
541  # cleaning tools
542  first = 1
543  snum = len(self.toolslines.keys())
544  for num in range(1, snum + 1):
545  if self.toolslines[num]['tool']:
546  if first:
547  self.tools_string = '%s' % self.toolslines[num]['tool']
548  self.thresh_string = '%s' % self.toolslines[num]['thresh']
549  first = 0
550  else:
551  self.tools_string += ',%s' % self.toolslines[num]['tool']
552  self.thresh_string += ',%s' % self.toolslines[num]['thresh']
553 
554  self.inmap = self.selectionInput.GetValue()
555  self.outmap = self.selectionOutput.GetValue()
def GetValue
Definition: widgets.py:118
def OnCopy
Copy the command.
Definition: vclean.py:508
wxGUI command interface
def OnMoveToolUp
Move up tool button pressed.
Definition: vclean.py:359
def OnAddTool
Add tool button pressed.
Definition: vclean.py:302
wxGUI debugging
def OnHelp
Show GRASS manual page.
Definition: vclean.py:501
def SetValue
Definition: widgets.py:115
def OnDone
Command done.
Definition: vclean.py:446
Custom control that selects elements.
def OnClearTool
Remove tool button pressed.
Definition: vclean.py:347
def OnSetTool
Tool was defined.
Definition: vclean.py:411
def OnMoveToolDown
Move down tool button pressed.
Definition: vclean.py:385
def OnCleaningRun
Builds options and runs v.clean.
Definition: vclean.py:450
def OnSelect
Tool was selected.
Definition: vclean.py:433
def OnThreshValue
Threshold value was entered.
Definition: vclean.py:422
Default GUI settings.
tuple range
Definition: tools.py:1406
def __init__
Dialog for interactively defining vector cleaning tools.
Definition: vclean.py:33
def RunCommand
Run GRASS command.
Definition: gcmd.py:625