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