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