|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
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()