""" @package vclean.py @brief Dialog for interactive construction of vector cleaning operations Classes: - VectorCleaningFrame (C) 2010-2011 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Markus Metz """ import os import sys import shutil import wx import wx.lib.scrolledpanel as scrolled from grass.script import core as grass import dbm import gcmd import globalvar import gselect import render import utils from debug import Debug as Debug from preferences import globalSettings as UserSettings class VectorCleaningFrame(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title=_('set up vector cleaning tools'), pos=wx.DefaultPosition, size=(-1, -1), style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER, **kwargs): """! Dialog for interactively defining vector cleaning tools """ wx.Frame.__init__(self, parent, id, title, pos, size, style) self.parent = parent # GMFrame if self.parent: self.log = self.parent.GetLogWindow() else: self.log = None # grass command self.cmd = 'v.clean' # statusbar self.CreateStatusBar() # icon self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) # self.panel not set as in colorrules # self.panel = wx.Panel(parent = self, id = wx.ID_ANY) # input map to clean self.inmap = '' # cleaned output map self.outmap = '' self.ftype = '' # cleaning tools self.toolslines = {} self.tool_desc_list = [ _('break lines/boundaries'), _('remove duplicates'), _('remove dangles'), _('change boundary dangles to lines'), _('remove bridges'), _('change bridges to lines'), _('snap lines/boundaries'), _('remove duplicate area centroids'), _('break polygons'), _('prune lines/boundaries'), _('remove small areas'), _('remove lines/boundaries of zero length'), _('remove small angles at nodes') ] self.tool_list = [ 'break', 'rmdupl', 'rmdangle', 'chdangle', 'rmbridge', 'chbridge', 'snap', 'rmdac', 'bpol', 'prune', 'rmarea', 'rmline', 'rmsa' ] self.ftype = [ 'point', 'line', 'boundary', 'centroid', 'area', 'face'] self.n_ftypes = 6 self.tools_string = '' self.thresh_string = '' self.ftype_string = '' self.SetTitle(_('Set up vector cleaning tools')) self.SetStatusText(_("Set up vector cleaning tools")) self.elem = 'vector' self.ctlabel = _('Choose cleaning tools and set thresholds') # top controls self.inmaplabel = wx.StaticText(parent = self, id = wx.ID_ANY, label= _('Select input vector map:')) self.selectionInput = gselect.Select(parent=self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type='vector') self.ftype_check = {} ftypeBox = wx.StaticBox(parent=self, id=wx.ID_ANY, label=_(' Feature type: ')) self.ftypeSizer = wx.StaticBoxSizer(ftypeBox, wx.HORIZONTAL) self.outmaplabel = wx.StaticText(parent = self, id = wx.ID_ANY, label= _('Select output vector map:')) self.selectionOutput = gselect.Select(parent=self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type='vector') self.overwrite = wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('Allow output files to overwrite existing files')) self.overwrite.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled')) # cleaning tools self.ct_label = wx.StaticText(parent=self, id=wx.ID_ANY, label=self.ctlabel) self.ct_panel = self.__toolsPanel() # buttons to manage cleaning tools self.btn_add = wx.Button(parent=self, id=wx.ID_ADD) self.btn_remove = wx.Button(parent=self, id=wx.ID_REMOVE) self.btn_moveup = wx.Button(parent=self, id=wx.ID_UP) self.btn_movedown = wx.Button(parent=self, id=wx.ID_DOWN) # add one tool as default self.AddTool() self.selected = -1 # Buttons self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE) self.btn_run = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Run")) self.btn_run.SetDefault() self.btn_clipboard = wx.Button(parent=self, id=wx.ID_COPY) self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)")) self.btn_help = wx.Button(parent = self, id = wx.ID_HELP) # bindings self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose) self.btn_run.Bind(wx.EVT_BUTTON, self.OnCleaningRun) self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy) self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp) self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddTool) self.btn_remove.Bind(wx.EVT_BUTTON, self.OnClearTool) self.btn_moveup.Bind(wx.EVT_BUTTON, self.OnMoveToolUp) self.btn_movedown.Bind(wx.EVT_BUTTON, self.OnMoveToolDown) self.SetMinSize(self.GetBestSize()) # layout self._layout() self.CentreOnScreen() self.Show() def _layout(self): sizer = wx.BoxSizer(wx.VERTICAL) # # input output # inSizer = wx.GridBagSizer(hgap=5, vgap=5) inSizer.Add(item=self.inmaplabel, pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1) inSizer.Add(item=self.selectionInput, pos=(1, 0), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1) self.ftype_check = [ wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('point')), wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('line')), wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('boundary')), wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('centroid')), wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('area')), wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('face')) ] typeoptSizer = wx.BoxSizer(wx.HORIZONTAL) for num in range(0, self.n_ftypes): type_box = self.ftype_check[num] typeoptSizer.Add(item=type_box, flag=wx.ALIGN_LEFT, border=1) self.ftypeSizer.Add(item = typeoptSizer, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=2) outSizer = wx.GridBagSizer(hgap=5, vgap=5) outSizer.Add(item=self.outmaplabel, pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1) outSizer.Add(item=self.selectionOutput, pos=(1, 0), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1) replaceSizer = wx.BoxSizer(wx.HORIZONTAL) replaceSizer.Add(item=self.overwrite, proportion=1, flag=wx.ALL | wx.EXPAND, border=1) outSizer.Add(item=replaceSizer, pos=(2, 0), flag=wx.ALL | wx.EXPAND, border=1) # # tools selection # bodySizer = wx.GridBagSizer(hgap=5, vgap=5) bodySizer.Add(item=self.ct_label, pos=(0, 0), span=(1, 2), flag=wx.ALL, border=5) bodySizer.Add(item=self.ct_panel, pos=(1, 0), span=(1, 2)) manageBoxSizer = wx.GridBagSizer(hgap=10, vgap=1) # start with row 1 for nicer layout manageBoxSizer.Add(item=self.btn_add, pos=(1, 0), border=2, flag=wx.ALL | wx.EXPAND) manageBoxSizer.Add(item=self.btn_remove, pos=(2, 0), border=2, flag=wx.ALL | wx.EXPAND) manageBoxSizer.Add(item=self.btn_moveup, pos=(3, 0), border=2, flag=wx.ALL | wx.EXPAND) manageBoxSizer.Add(item=self.btn_movedown, pos=(4, 0), border=2, flag=wx.ALL | wx.EXPAND) bodySizer.Add(item=manageBoxSizer, pos=(1, 2), flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5) bodySizer.AddGrowableCol(2) # # standard buttons # btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(self.btn_close, flag=wx.LEFT | wx.RIGHT, border=5) btnSizer.Add(self.btn_run, flag=wx.LEFT | wx.RIGHT, border=5) btnSizer.Add(self.btn_clipboard, flag=wx.LEFT | wx.RIGHT, border=5) btnSizer.Add(self.btn_help, flag=wx.LEFT | wx.RIGHT, border=5) # # put it all together # sizer.Add(item=inSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5) sizer.Add(item=self.ftypeSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5) sizer.Add(item=outSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5) sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY, style=wx.LI_HORIZONTAL), proportion=0, flag=wx.EXPAND | wx.ALL, border=5) sizer.Add(item=bodySizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5) sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY, style=wx.LI_HORIZONTAL), proportion=0, flag=wx.EXPAND | wx.ALL, border=5) sizer.Add(item=btnSizer, proportion=0, flag=wx.ALL | wx.ALIGN_RIGHT, border=5) self.SetSizer(sizer) sizer.Fit(self) self.Layout() def __toolsPanel(self): ct_panel = scrolled.ScrolledPanel(parent=self, id=wx.ID_ANY, size=(500, 240), style=wx.SUNKEN_BORDER) self.ct_sizer = wx.GridBagSizer(vgap=2, hgap=4) ct_panel.SetSizer(self.ct_sizer) ct_panel.SetAutoLayout(True) return ct_panel def OnAddTool(self, event): """!Add tool button pressed""" self.AddTool() def AddTool(self): snum = len(self.toolslines.keys()) num = snum + 1 # tool number tool_no = wx.StaticText(parent = self.ct_panel, id = 3000+num, label= str(num)+'.') # tool tool_cbox = wx.ComboBox(parent = self.ct_panel, id=1000+num, size = (300, -1), choices = self.tool_desc_list, style = wx.CB_DROPDOWN | wx.CB_READONLY | wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_COMBOBOX, self.OnSetTool, tool_cbox) # threshold txt_ctrl = wx.TextCtrl(parent=self.ct_panel, id=2000+num, value='0.00', size=(100,-1), style=wx.TE_NOHIDESEL) self.Bind(wx.EVT_TEXT, self.OnThreshValue, txt_ctrl) # select select = wx.CheckBox(parent=self.ct_panel, id=num) select.SetValue(False) self.Bind(wx.EVT_CHECKBOX, self.OnSelect, select) # start with row 1 and col 1 for nicer layout self.ct_sizer.Add(item=tool_no, pos=(num, 1), flag=wx.ALIGN_CENTER_VERTICAL, border=5) self.ct_sizer.Add(item=tool_cbox, pos=(num, 2), flag=wx.ALIGN_CENTER | wx.RIGHT, border=5) self.ct_sizer.Add(item=txt_ctrl, pos=(num, 3), flag=wx.ALIGN_CENTER | wx.RIGHT, border=5) self.ct_sizer.Add(item=select, pos=(num, 4), flag=wx.ALIGN_CENTER | wx.RIGHT) self.toolslines[num] = { 'tool_desc' : '' , 'tool' : '' , 'thresh' : '0.00' } self.ct_panel.Layout() self.ct_panel.SetupScrolling() def OnClearTool(self, event): """!Remove tool button pressed""" id = self.selected if id > 0: self.FindWindowById(id+1000).SetValue('') self.toolslines[id]['tool_desc'] = '' self.toolslines[id]['tool'] = '' self.SetStatusText(_("%s. cleaning tool removed, will be ignored") % id) else: self.SetStatusText(_("Please select a cleaning tool to remove")) def OnMoveToolUp(self, event): """!Move up tool button pressed""" id = self.selected if id > 1: id_up = id - 1 this_toolline = self.toolslines[id] up_toolline = self.toolslines[id_up] self.FindWindowById(id_up).SetValue(True) self.FindWindowById(id_up+1000).SetValue(this_toolline['tool_desc']) self.FindWindowById(id_up+2000).SetValue(this_toolline['thresh']) self.toolslines[id_up] = this_toolline self.FindWindowById(id).SetValue(False) self.FindWindowById(id+1000).SetValue(up_toolline['tool_desc']) self.FindWindowById(id+2000).SetValue(up_toolline['thresh']) self.toolslines[id] = up_toolline self.selected = id_up self.SetStatusText(_("%s. cleaning tool moved up") % id) elif id == 1: self.SetStatusText(_("1. cleaning tool can not be moved up ")) elif id == -1: self.SetStatusText(_("Please select a cleaning tool to move up")) def OnMoveToolDown(self, event): """!Move down tool button pressed""" id = self.selected snum = len(self.toolslines.keys()) if id > 0 and id < snum: id_down = id + 1 this_toolline = self.toolslines[id] down_toolline = self.toolslines[id_down] self.FindWindowById(id_down).SetValue(True) self.FindWindowById(id_down+1000).SetValue(this_toolline['tool_desc']) self.FindWindowById(id_down+2000).SetValue(this_toolline['thresh']) self.toolslines[id_down] = this_toolline self.FindWindowById(id).SetValue(False) self.FindWindowById(id+1000).SetValue(down_toolline['tool_desc']) self.FindWindowById(id+2000).SetValue(down_toolline['thresh']) self.toolslines[id] = down_toolline self.selected = id_down self.SetStatusText(_("%s. cleaning tool moved down") % id) elif id == snum: self.SetStatusText(_("Last cleaning tool can not be moved down ")) elif id == -1: self.SetStatusText(_("Please select a cleaning tool to move down")) def OnSetTool(self, event): """!Tool was defined""" id = event.GetId() tool_no = id-1000 num = self.FindWindowById(id).GetCurrentSelection() self.toolslines[tool_no]['tool_desc'] = self.tool_desc_list[num] self.toolslines[tool_no]['tool'] = self.tool_list[num] self.SetStatusText( str(tool_no) + '. ' + _("cleaning tool: '%s'") % (self.tool_list[num])) def OnThreshValue(self, event): """!Threshold value was entered""" id = event.GetId() num = id-2000 self.toolslines[num]['thresh'] = self.FindWindowById(id).GetValue() self.SetStatusText(_("Threshold for %(num)s. tool '%(tool)s': %(thresh)s") % \ { 'num' : num, 'tool' : self.toolslines[num]['tool'], 'thresh' : self.toolslines[num]['thresh'] }) def OnSelect(self, event): """!Tool was selected""" id = event.GetId() if self.selected > -1 and self.selected != id: win = self.FindWindowById(self.selected) win.SetValue(False) if self.selected != id: self.selected = id else: self.selected = -1 def OnCleaningRun(self, event): """!Builds options and runs v.clean """ self.SetStatusText(_("Executing selected cleaning operations...")) snum = len(self.toolslines.keys()) self.GetCmdStrings() if self.log: cmd = [ self.cmd, 'input=%s' % self.inmap, 'output=%s' % self.outmap, 'tool=%s' % self.tools_string, 'thres=%s' % self.thresh_string ] if self.ftype_string: cmd.append('type=%s' % self.ftype_string) if self.overwrite.IsChecked(): cmd.append('--overwrite') self.log.RunCmd(cmd) self.parent.Raise() else: if self.overwrite.IsChecked(): overwrite = True else: overwrite = False gcmd.RunCommand(self.cmd, input = self.inmap, output = self.outmap, type = self.ftype_string, tool = self.tools_string, thresh = self.thresh_string, overwrite = overwrite) def OnClose(self, event): self.Destroy() def OnHelp(self, event): """!Show GRASS manual page""" gcmd.RunCommand('g.manual', quiet = True, parent = self, entry = self.cmd) def OnCopy(self, event): """!Copy the command""" cmddata = wx.TextDataObject() # get tool and thresh strings self.GetCmdStrings() cmdstring = '%s' % (self.cmd) # list -> string cmdstring += ' input=%s output=%s type=%s tool=%s thres=%s' % \ (self.inmap, self.outmap, self.ftype_string, self.tools_string, self.thresh_string) if self.overwrite.IsChecked(): cmdstring += ' --overwrite' cmddata.SetText(cmdstring) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(cmddata) wx.TheClipboard.Close() self.SetStatusText(_("Vector cleaning command copied to clipboard")) def GetCmdStrings(self): self.tools_string = '' self.thresh_string = '' self.ftype_string = '' # feature types first = 1 for num in range(0, self.n_ftypes - 1): if self.ftype_check[num].IsChecked(): if first: self.ftype_string = '%s' % self.ftype[num] first = 0 else: self.ftype_string += ',%s' % self.ftype[num] # cleaning tools first = 1 snum = len(self.toolslines.keys()) for num in range(1, snum + 1): if self.toolslines[num]['tool']: if first: self.tools_string = '%s' % self.toolslines[num]['tool'] self.thresh_string = '%s' % self.toolslines[num]['thresh'] first = 0 else: self.tools_string += ',%s' % self.toolslines[num]['tool'] self.thresh_string += ',%s' % self.toolslines[num]['thresh'] self.inmap = self.selectionInput.GetValue() self.outmap = self.selectionOutput.GetValue()