GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
vclean.py
Go to the documentation of this file.
00001 """
00002 @package modules.vclean
00003 
00004 @brief Dialog for interactive construction of vector cleaning
00005 operations
00006 
00007 Classes:
00008  - vclean::VectorCleaningFrame
00009 
00010 (C) 2010-2011 by the GRASS Development Team
00011 This program is free software under the GNU General Public License
00012 (>=v2). Read the file COPYING that comes with GRASS for details.
00013 
00014 @author Markus Metz
00015 """
00016 
00017 import os
00018 
00019 import wx
00020 import wx.lib.scrolledpanel as scrolled
00021 
00022 from grass.script import core as grass
00023 
00024 from core.gcmd        import RunCommand, GError
00025 from core             import globalvar
00026 from gui_core.gselect import Select
00027 from core.debug       import Debug
00028 from core.settings    import UserSettings
00029 
00030 class VectorCleaningFrame(wx.Frame):
00031     def __init__(self, parent, id = wx.ID_ANY, title = _('Set up vector cleaning tools'),
00032                  style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
00033                  **kwargs):
00034         """!
00035         Dialog for interactively defining vector cleaning tools
00036         """
00037         wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
00038         
00039         self.parent = parent # GMFrame
00040         if self.parent:
00041             self.log = self.parent.GetLogWindow()
00042         else:
00043             self.log = None
00044         
00045         # grass command
00046         self.cmd = 'v.clean'
00047         
00048         # statusbar
00049         self.CreateStatusBar()
00050         
00051         # icon
00052         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
00053         
00054         self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
00055         
00056         # input map to clean
00057         self.inmap = ''
00058         
00059         # cleaned output map
00060         self.outmap = ''
00061         
00062         self.ftype = ''
00063         
00064         # cleaning tools
00065         self.toolslines = {}
00066         
00067         self.tool_desc_list = [
00068             _('break lines/boundaries'),
00069             _('remove duplicates'),
00070             _('remove dangles'),
00071             _('change boundary dangles to lines'),
00072             _('remove bridges'),
00073             _('change bridges to lines'),
00074             _('snap lines/boundaries'),
00075             _('remove duplicate area centroids'),
00076             _('break polygons'),
00077             _('prune lines/boundaries'),
00078             _('remove small areas'),
00079             _('remove lines/boundaries of zero length'),
00080             _('remove small angles at nodes')
00081             ]
00082         
00083         self.tool_list = [
00084             'break',
00085             'rmdupl',
00086             'rmdangle',
00087             'chdangle',
00088             'rmbridge',
00089             'chbridge',
00090             'snap',
00091             'rmdac',
00092             'bpol',
00093             'prune',
00094             'rmarea',
00095             'rmline',
00096             'rmsa'
00097             ]
00098         
00099         self.ftype = [
00100             'point',
00101             'line',
00102             'boundary',
00103             'centroid',
00104             'area',
00105             'face']
00106         
00107         self.n_ftypes = len(self.ftype)
00108         
00109         self.tools_string = ''
00110         self.thresh_string = ''
00111         self.ftype_string = ''
00112         
00113         self.SetStatusText(_("Set up vector cleaning tools"))
00114         self.elem = 'vector'
00115         self.ctlabel = _('Choose cleaning tools and set thresholds')
00116         
00117         # top controls
00118         self.inmaplabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00119                                          label= _('Select input vector map:'))
00120         self.selectionInput = Select(parent = self.panel, id = wx.ID_ANY,
00121                                      size = globalvar.DIALOG_GSELECT_SIZE,
00122                                      type = 'vector')
00123         self.ftype_check = {}
00124         ftypeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
00125                                 label = _(' Feature type: '))
00126         self.ftypeSizer = wx.StaticBoxSizer(ftypeBox, wx.HORIZONTAL)
00127 
00128         self.outmaplabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00129                                          label =  _('Select output vector map:'))
00130         self.selectionOutput = Select(parent = self.panel, id = wx.ID_ANY,
00131                                       size = globalvar.DIALOG_GSELECT_SIZE,
00132                                       type = 'vector')
00133         
00134         self.overwrite = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
00135                                        label = _('Allow output files to overwrite existing files'))
00136         self.overwrite.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
00137 
00138         # cleaning tools
00139         self.ct_label = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
00140                                       label = self.ctlabel)
00141 
00142         self.ct_panel = self._toolsPanel()
00143 
00144         # buttons to manage cleaning tools
00145         self.btn_add = wx.Button(parent = self.panel, id = wx.ID_ADD)
00146         self.btn_remove = wx.Button(parent = self.panel, id = wx.ID_REMOVE)
00147         self.btn_moveup = wx.Button(parent = self.panel, id = wx.ID_UP)
00148         self.btn_movedown = wx.Button(parent = self.panel, id = wx.ID_DOWN)
00149 
00150         # add one tool as default
00151         self.AddTool()
00152         self.selected = -1
00153         
00154         # Buttons
00155         self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
00156         self.btn_run = wx.Button(parent = self.panel, id = wx.ID_ANY, label = _("&Run"))
00157         self.btn_run.SetDefault()
00158         self.btn_clipboard = wx.Button(parent = self.panel, id = wx.ID_COPY)
00159         self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)"))
00160         self.btn_help = wx.Button(parent = self.panel, id = wx.ID_HELP)
00161         
00162         # bindings
00163         self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
00164         self.btn_run.Bind(wx.EVT_BUTTON, self.OnCleaningRun)
00165         self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
00166         self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
00167 
00168         self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddTool)
00169         self.btn_remove.Bind(wx.EVT_BUTTON, self.OnClearTool)
00170         self.btn_moveup.Bind(wx.EVT_BUTTON, self.OnMoveToolUp)
00171         self.btn_movedown.Bind(wx.EVT_BUTTON, self.OnMoveToolDown)
00172         
00173         # layout
00174         self._layout()
00175         
00176         self.SetMinSize(self.GetBestSize())
00177         
00178         self.CentreOnScreen()
00179                 
00180     def _layout(self):
00181         sizer = wx.BoxSizer(wx.VERTICAL)
00182         
00183         #
00184         # input output
00185         #
00186         inSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
00187 
00188         inSizer.Add(item = self.inmaplabel, pos = (0, 0),
00189                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
00190         inSizer.Add(item = self.selectionInput, pos = (1, 0),
00191                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
00192 
00193         self.ftype_check = [
00194             wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('point')),
00195             wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('line')),
00196             wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('boundary')),
00197             wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('centroid')),
00198             wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('area')),
00199             wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _('face'))
00200             ]
00201 
00202         typeoptSizer = wx.BoxSizer(wx.HORIZONTAL)
00203         for num in range(0, self.n_ftypes):
00204             type_box = self.ftype_check[num]
00205             typeoptSizer.Add(item = type_box, flag = wx.ALIGN_LEFT, border = 1)
00206 
00207         self.ftypeSizer.Add(item = typeoptSizer,
00208                    flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 2)
00209 
00210         outSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
00211 
00212         outSizer.Add(item = self.outmaplabel, pos = (0, 0),
00213                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
00214         outSizer.Add(item = self.selectionOutput, pos = (1, 0),
00215                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 1)
00216         replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
00217         replaceSizer.Add(item = self.overwrite, proportion = 1,
00218                          flag = wx.ALL | wx.EXPAND, border = 1)
00219 
00220         outSizer.Add(item = replaceSizer, pos = (2, 0),
00221                      flag = wx.ALL | wx.EXPAND, border = 1)
00222 
00223         #
00224         # tools selection
00225         #
00226         bodySizer = wx.GridBagSizer(hgap = 5, vgap = 5)
00227 
00228         bodySizer.Add(item = self.ct_label, pos = (0, 0), span = (1, 2),
00229                       flag = wx.ALL, border = 5)
00230 
00231         bodySizer.Add(item = self.ct_panel, pos = (1, 0), span = (1, 2))
00232 
00233         manageBoxSizer = wx.GridBagSizer(hgap = 10, vgap = 1)
00234         # start with row 1 for nicer layout
00235         manageBoxSizer.Add(item = self.btn_add, pos = (1, 0), border = 2, flag = wx.ALL | wx.EXPAND)
00236         manageBoxSizer.Add(item = self.btn_remove, pos = (2, 0), border = 2, flag = wx.ALL | wx.EXPAND)
00237         manageBoxSizer.Add(item = self.btn_moveup, pos = (3, 0), border = 2, flag = wx.ALL | wx.EXPAND)
00238         manageBoxSizer.Add(item = self.btn_movedown, pos = (4, 0), border = 2, flag = wx.ALL | wx.EXPAND)
00239 
00240         bodySizer.Add(item = manageBoxSizer, pos = (1, 2),
00241                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
00242 
00243         bodySizer.AddGrowableCol(2)
00244         
00245         #
00246         # standard buttons
00247         #
00248         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00249         btnSizer.Add(self.btn_close,
00250                      flag = wx.LEFT | wx.RIGHT, border = 5)
00251         btnSizer.Add(self.btn_run,
00252                      flag = wx.LEFT | wx.RIGHT, border = 5)
00253         btnSizer.Add(self.btn_clipboard,
00254                      flag = wx.LEFT | wx.RIGHT, border = 5)
00255         btnSizer.Add(self.btn_help,
00256                      flag = wx.LEFT | wx.RIGHT, border = 5)
00257         
00258         #
00259         # put it all together
00260         #
00261         sizer.Add(item = inSizer, proportion = 0,
00262                   flag = wx.ALL | wx.EXPAND, border = 5)
00263         
00264         sizer.Add(item = self.ftypeSizer, proportion = 0,
00265                   flag = wx.ALL | wx.EXPAND, border = 5)
00266 
00267         sizer.Add(item = outSizer, proportion = 0,
00268                   flag = wx.ALL | wx.EXPAND, border = 5)
00269 
00270         sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
00271                   style = wx.LI_HORIZONTAL), proportion = 0,
00272                   flag = wx.EXPAND | wx.ALL, border = 5) 
00273         
00274         sizer.Add(item = bodySizer, proportion = 1,
00275                   flag = wx.ALL | wx.EXPAND, border = 5)
00276 
00277         sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
00278                   style = wx.LI_HORIZONTAL), proportion = 0,
00279                   flag = wx.EXPAND | wx.ALL, border = 5) 
00280         
00281         sizer.Add(item = btnSizer, proportion = 0,
00282                   flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
00283         
00284         self.panel.SetAutoLayout(True)
00285         self.panel.SetSizer(sizer)
00286         sizer.Fit(self.panel)
00287         
00288         self.Layout()
00289         
00290     def _toolsPanel(self):
00291         ct_panel = scrolled.ScrolledPanel(parent = self.panel, id = wx.ID_ANY,
00292                                           size = (500, 240),
00293                                           style = wx.SUNKEN_BORDER)
00294 
00295         self.ct_sizer = wx.GridBagSizer(vgap = 2, hgap = 4)
00296         
00297         ct_panel.SetSizer(self.ct_sizer)
00298         ct_panel.SetAutoLayout(True)
00299         
00300         return ct_panel        
00301 
00302     def OnAddTool(self, event):
00303         """!Add tool button pressed"""
00304         self.AddTool()
00305 
00306     def AddTool(self):
00307         snum = len(self.toolslines.keys())
00308         num = snum + 1
00309         # tool number
00310         tool_no = wx.StaticText(parent = self.ct_panel, id = 3000+num,
00311                                          label =  str(num)+'.')
00312         # tool
00313         tool_cbox = wx.ComboBox(parent = self.ct_panel, id = 1000+num, 
00314                                 size = (300, -1), choices = self.tool_desc_list,
00315                                 style = wx.CB_DROPDOWN |
00316                                 wx.CB_READONLY | wx.TE_PROCESS_ENTER)
00317         self.Bind(wx.EVT_COMBOBOX, self.OnSetTool, tool_cbox)
00318         # threshold
00319         txt_ctrl = wx.TextCtrl(parent = self.ct_panel, id = 2000+num, value = '0.00',
00320                                size = (100,-1),
00321                                style = wx.TE_NOHIDESEL)
00322         self.Bind(wx.EVT_TEXT, self.OnThreshValue, txt_ctrl)
00323 
00324         # select
00325         select = wx.CheckBox(parent = self.ct_panel, id = num)
00326         select.SetValue(False)
00327         self.Bind(wx.EVT_CHECKBOX, self.OnSelect, select)
00328 
00329         # start with row 1 and col 1 for nicer layout
00330         self.ct_sizer.Add(item = tool_no, pos = (num, 1),
00331                           flag = wx.ALIGN_CENTER_VERTICAL, border = 5)
00332         self.ct_sizer.Add(item = tool_cbox, pos = (num, 2),
00333                           flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
00334         self.ct_sizer.Add(item = txt_ctrl, pos = (num, 3),
00335                           flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
00336         self.ct_sizer.Add(item = select, pos = (num, 4),
00337                           flag = wx.ALIGN_CENTER | wx.RIGHT)
00338 
00339         self.toolslines[num] = {
00340             'tool_desc' : '' ,
00341             'tool' : '' ,
00342             'thresh' : '0.00' }
00343         
00344         self.ct_panel.Layout()
00345         self.ct_panel.SetupScrolling()
00346         
00347     def OnClearTool(self, event):
00348         """!Remove tool button pressed"""
00349         id = self.selected
00350 
00351         if id > 0:
00352             self.FindWindowById(id+1000).SetValue('')
00353             self.toolslines[id]['tool_desc'] = ''
00354             self.toolslines[id]['tool'] = ''
00355             self.SetStatusText(_("%s. cleaning tool removed, will be ignored") % id)
00356         else:
00357             self.SetStatusText(_("Please select a cleaning tool to remove"))
00358 
00359     def OnMoveToolUp(self, event):
00360         """!Move up tool button pressed"""
00361         id = self.selected
00362 
00363         if id > 1:
00364             id_up = id - 1
00365             this_toolline = self.toolslines[id]
00366             up_toolline = self.toolslines[id_up]
00367             
00368             self.FindWindowById(id_up).SetValue(True)
00369             self.FindWindowById(id_up+1000).SetValue(this_toolline['tool_desc'])
00370             self.FindWindowById(id_up+2000).SetValue(this_toolline['thresh'])
00371             self.toolslines[id_up] = this_toolline
00372             
00373             self.FindWindowById(id).SetValue(False)
00374             self.FindWindowById(id+1000).SetValue(up_toolline['tool_desc'])
00375             self.FindWindowById(id+2000).SetValue(up_toolline['thresh'])
00376             self.toolslines[id] = up_toolline
00377             self.selected = id_up
00378             self.SetStatusText(_("%s. cleaning tool moved up") % id)
00379         elif id == 1:
00380             self.SetStatusText(_("1. cleaning tool can not be moved up "))
00381         elif id == -1:
00382             self.SetStatusText(_("Please select a cleaning tool to move up"))
00383 
00384 
00385     def OnMoveToolDown(self, event):
00386         """!Move down tool button pressed"""
00387         id = self.selected
00388         snum = len(self.toolslines.keys())
00389 
00390         if id > 0 and id < snum:
00391             id_down = id + 1
00392             this_toolline = self.toolslines[id]
00393             down_toolline = self.toolslines[id_down]
00394             
00395             self.FindWindowById(id_down).SetValue(True)
00396             self.FindWindowById(id_down+1000).SetValue(this_toolline['tool_desc'])
00397             self.FindWindowById(id_down+2000).SetValue(this_toolline['thresh'])
00398             self.toolslines[id_down] = this_toolline
00399             
00400             self.FindWindowById(id).SetValue(False)
00401             self.FindWindowById(id+1000).SetValue(down_toolline['tool_desc'])
00402             self.FindWindowById(id+2000).SetValue(down_toolline['thresh'])
00403             self.toolslines[id] = down_toolline
00404             self.selected = id_down
00405             self.SetStatusText(_("%s. cleaning tool moved down") % id)
00406         elif id == snum:
00407             self.SetStatusText(_("Last cleaning tool can not be moved down "))
00408         elif id == -1:
00409             self.SetStatusText(_("Please select a cleaning tool to move down"))
00410 
00411     def OnSetTool(self, event):
00412         """!Tool was defined"""
00413         id = event.GetId()
00414         tool_no = id-1000
00415         num = self.FindWindowById(id).GetCurrentSelection()
00416 
00417         self.toolslines[tool_no]['tool_desc'] = self.tool_desc_list[num]
00418         self.toolslines[tool_no]['tool'] = self.tool_list[num]
00419 
00420         self.SetStatusText( str(tool_no) + '. ' + _("cleaning tool: '%s'") % (self.tool_list[num]))
00421 
00422     def OnThreshValue(self, event):
00423         """!Threshold value was entered"""
00424         id = event.GetId()
00425         num = id-2000
00426         self.toolslines[num]['thresh'] = self.FindWindowById(id).GetValue()
00427 
00428         self.SetStatusText(_("Threshold for %(num)s. tool '%(tool)s': %(thresh)s") % \
00429                                { 'num' : num,
00430                                  'tool' : self.toolslines[num]['tool'],
00431                                  'thresh' : self.toolslines[num]['thresh'] })
00432 
00433     def OnSelect(self, event):
00434         """!Tool was selected"""
00435         id = event.GetId()
00436 
00437         if self.selected > -1 and self.selected != id:
00438             win = self.FindWindowById(self.selected)
00439             win.SetValue(False)
00440 
00441         if self.selected != id:
00442             self.selected = id
00443         else:
00444             self.selected = -1
00445 
00446     def OnDone(self, cmd, returncode):
00447         """!Command done"""
00448         self.SetStatusText('')
00449 
00450     def OnCleaningRun(self, event):
00451         """!Builds options and runs v.clean
00452         """
00453         self.GetCmdStrings()
00454 
00455         err = list()
00456         for p, name in ((self.inmap, _('Name of input vector map')),
00457                         (self.outmap, _('Name for output vector map')),
00458                         (self.tools_string, _('Tools')),
00459                         (self.thresh_string, _('Threshold'))):
00460             if not p:
00461                 err.append(_("'%s' not defined") % name)
00462         if err:
00463             GError(_("Some parameters not defined. Operation "
00464                      "canceled.\n\n%s") % '\n'.join(err),
00465                    parent = self)
00466             return
00467 
00468         self.SetStatusText(_("Executing selected cleaning operations..."))
00469         snum = len(self.toolslines.keys())
00470         
00471         if self.log:
00472             cmd = [ self.cmd,
00473                     'input=%s' % self.inmap,
00474                     'output=%s' % self.outmap,
00475                     'tool=%s' % self.tools_string,
00476                     'thres=%s' % self.thresh_string ]
00477             if self.ftype_string:
00478                 cmd.append('type=%s' % self.ftype_string)
00479             if self.overwrite.IsChecked():
00480                 cmd.append('--overwrite')
00481             
00482             self.log.RunCmd(cmd, onDone = self.OnDone)
00483             self.parent.Raise()
00484         else:
00485             if self.overwrite.IsChecked():
00486                 overwrite = True
00487             else:
00488                 overwrite = False
00489             
00490             RunCommand(self.cmd,
00491                        input = self.inmap,
00492                        output = self.outmap,
00493                        type = self.ftype_string,
00494                        tool = self.tools_string,
00495                        thresh = self.thresh_string,
00496                        overwrite = overwrite)
00497 
00498     def OnClose(self, event):
00499         self.Destroy()
00500         
00501     def OnHelp(self, event):
00502         """!Show GRASS manual page"""
00503         RunCommand('g.manual',
00504                    quiet = True,
00505                    parent = self,
00506                    entry = self.cmd)
00507         
00508     def OnCopy(self, event):
00509         """!Copy the command"""
00510         cmddata = wx.TextDataObject()
00511         # get tool and thresh strings
00512         self.GetCmdStrings()
00513         cmdstring = '%s' % (self.cmd)
00514         # list -> string
00515         cmdstring += ' input=%s output=%s type=%s tool=%s thres=%s' % \
00516             (self.inmap, self.outmap, self.ftype_string, self.tools_string, self.thresh_string)
00517         if self.overwrite.IsChecked():
00518             cmdstring += ' --overwrite'
00519  
00520         cmddata.SetText(cmdstring)
00521         if wx.TheClipboard.Open():
00522             wx.TheClipboard.SetData(cmddata)
00523             wx.TheClipboard.Close()
00524             self.SetStatusText(_("Vector cleaning command copied to clipboard"))
00525 
00526     def GetCmdStrings(self):
00527         self.tools_string = ''
00528         self.thresh_string = ''
00529         self.ftype_string = ''
00530         # feature types
00531         first = 1
00532         for num in range(0, self.n_ftypes - 1):
00533             if self.ftype_check[num].IsChecked():
00534                 if first:
00535                     self.ftype_string = '%s' % self.ftype[num]
00536                     first = 0
00537                 else:
00538                     self.ftype_string += ',%s' % self.ftype[num]
00539                     
00540             
00541         # cleaning tools
00542         first = 1
00543         snum = len(self.toolslines.keys())
00544         for num in range(1, snum + 1):
00545             if self.toolslines[num]['tool']:
00546                 if first:
00547                     self.tools_string = '%s' % self.toolslines[num]['tool']
00548                     self.thresh_string = '%s' % self.toolslines[num]['thresh']
00549                     first = 0
00550                 else:
00551                     self.tools_string += ',%s' % self.toolslines[num]['tool']
00552                     self.thresh_string += ',%s' % self.toolslines[num]['thresh']
00553 
00554         self.inmap = self.selectionInput.GetValue()
00555         self.outmap = self.selectionOutput.GetValue()