GRASS Programmer's Manual  6.4.2(2012)
vdigit.py
Go to the documentation of this file.
00001 """!
00002 @package vdigit
00003 
00004 @brief Dialogs for wxGUI vector digitizer
00005 
00006 Classes:
00007  - VDigit
00008  - VDigitSettingsDialog
00009  - VDigitCategoryDialog
00010  - CategoryListCtrl
00011  - VDigitZBulkDialog
00012  - VDigitDuplicatesDialog
00013  - CheckListFeature
00014 
00015 (C) 2007-2011 by the GRASS Development Team
00016 This program is free software under the GNU General Public License
00017 (>=v2). Read the file COPYING that comes with GRASS for details.
00018 
00019 @author Martin Landa <landa.martin gmail.com>
00020 """
00021 
00022 import os
00023 import sys
00024 import string
00025 import copy
00026 import textwrap
00027 import traceback
00028 
00029 from threading import Thread
00030 
00031 import wx
00032 import wx.lib.colourselect as csel
00033 import wx.lib.mixins.listctrl as listmix
00034 
00035 import gcmd
00036 import dbm
00037 from debug import Debug as Debug
00038 import gselect
00039 import globalvar
00040 from units import Units
00041 from preferences import globalSettings as UserSettings
00042 try:
00043     from wxvdigit  import IVDigit, GV_LINES
00044     haveVDigit = True
00045     errorMsg = ''
00046 except (ImportError, NameError), err:
00047     haveVDigit = False
00048     errorMsg = err
00049     GV_LINES = -1
00050     class IVDigit:
00051         def __init__(self):
00052             pass
00053 
00054 class VDigit(IVDigit):
00055     def __init__(self, mapwindow):
00056         """!Base class of vector digitizer
00057         
00058         @param mapwindow reference to mapwindow (mapdisp_window.BufferedWindow) instance
00059         """
00060         IVDigit.__init__(self, mapwindow)
00061         
00062 class VDigitSettingsDialog(wx.Dialog):
00063     def __init__(self, parent, title, style = wx.DEFAULT_DIALOG_STYLE):
00064         """!Standard settings dialog for digitization purposes
00065         """
00066         wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
00067         
00068         self.parent = parent # mapdisplay.MapFrame class instance
00069         self.digit = self.parent.MapWindow.digit
00070         
00071         # notebook
00072         notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
00073         self._createSymbologyPage(notebook)
00074         self.digit.SetCategory()
00075         self._createGeneralPage(notebook)
00076         self._createAttributesPage(notebook)
00077         self._createQueryPage(notebook)
00078 
00079         # buttons
00080         btnApply = wx.Button(self, wx.ID_APPLY)
00081         btnCancel = wx.Button(self, wx.ID_CANCEL)
00082         btnSave = wx.Button(self, wx.ID_SAVE)
00083         btnSave.SetDefault()
00084 
00085         # bindigs
00086         btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
00087         btnApply.SetToolTipString(_("Apply changes for this session"))
00088         btnApply.SetDefault()
00089         btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
00090         btnSave.SetToolTipString(_("Close dialog and save changes to user settings file"))
00091         btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
00092         btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
00093         
00094         # sizers
00095         btnSizer = wx.StdDialogButtonSizer()
00096         btnSizer.AddButton(btnCancel)
00097         btnSizer.AddButton(btnApply)
00098         btnSizer.AddButton(btnSave)
00099         btnSizer.Realize()
00100         
00101         mainSizer = wx.BoxSizer(wx.VERTICAL)
00102         mainSizer.Add(item = notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
00103         mainSizer.Add(item = btnSizer, proportion = 0,
00104                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
00105         
00106         self.Bind(wx.EVT_CLOSE, self.OnCancel)
00107         
00108         self.SetSizer(mainSizer)
00109         mainSizer.Fit(self)
00110 
00111     def _createSymbologyPage(self, notebook):
00112         """!Create notebook page concerning symbology settings"""
00113         panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
00114         notebook.AddPage(page = panel, text = _("Symbology"))
00115 
00116         sizer = wx.BoxSizer(wx.VERTICAL)
00117         
00118         flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
00119         flexSizer.AddGrowableCol(0)
00120 
00121         self.symbology = {}
00122         for label, key in self._symbologyData():
00123             textLabel = wx.StaticText(panel, wx.ID_ANY, label)
00124             color = csel.ColourSelect(panel, id = wx.ID_ANY,
00125                                       colour = UserSettings.Get(group = 'vdigit', key = 'symbol',
00126                                                               subkey = [key, 'color']), size = globalvar.DIALOG_COLOR_SIZE)
00127             isEnabled = UserSettings.Get(group = 'vdigit', key = 'symbol',
00128                                          subkey = [key, 'enabled'])
00129             if isEnabled is not None:
00130                 enabled = wx.CheckBox(panel, id = wx.ID_ANY, label = "")
00131                 enabled.SetValue(isEnabled)
00132                 self.symbology[key] = (enabled, color)
00133             else:
00134                 enabled = (1, 1)
00135                 self.symbology[key] = (None, color)
00136             
00137             flexSizer.Add(textLabel, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00138             flexSizer.Add(enabled, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00139             flexSizer.Add(color, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
00140             color.SetName("GetColour")
00141         
00142         sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 10)
00143         
00144         panel.SetSizer(sizer)
00145         
00146         return panel
00147 
00148     def _createGeneralPage(self, notebook):
00149         """!Create notebook page concerning general settings"""
00150         panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
00151         notebook.AddPage(page = panel, text = _("General"))
00152 
00153         border = wx.BoxSizer(wx.VERTICAL)
00154         
00155         #
00156         # display section
00157         #
00158         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Display"))
00159         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00160         flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
00161         flexSizer.AddGrowableCol(0)
00162         # line width
00163         text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Line width"))
00164         self.lineWidthValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
00165                                           initial = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'value'),
00166                                           min = 1, max = 1e6)
00167         units = wx.StaticText(parent = panel, id = wx.ID_ANY, size = (115, -1),
00168                               label = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'units'),
00169                               style = wx.ALIGN_LEFT)
00170         flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00171         flexSizer.Add(self.lineWidthValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00172         flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
00173                       border = 10)
00174 
00175         sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
00176         border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
00177 
00178         #
00179         # snapping section
00180         #
00181         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Snapping"))
00182         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00183         flexSizer = wx.FlexGridSizer(cols = 3, hgap = 5, vgap = 5)
00184         flexSizer.AddGrowableCol(0)
00185 
00186         # snapping
00187         text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Snapping threshold"))
00188         self.snappingValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
00189                                          initial = UserSettings.Get(group = 'vdigit', key = "snapping", subkey = 'value'),
00190                                          min = -1, max = 1e6)
00191         self.snappingValue.Bind(wx.EVT_SPINCTRL, self.OnChangeSnappingValue)
00192         self.snappingValue.Bind(wx.EVT_TEXT, self.OnChangeSnappingValue)
00193         self.snappingUnit = wx.Choice(parent = panel, id = wx.ID_ANY, size = (125, -1),
00194                                       choices = [_("screen pixels"), _("map units")])
00195         self.snappingUnit.SetStringSelection(UserSettings.Get(group = 'vdigit', key = "snapping", subkey = 'units'))
00196         self.snappingUnit.Bind(wx.EVT_CHOICE, self.OnChangeSnappingUnits)
00197         flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00198         flexSizer.Add(self.snappingValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00199         flexSizer.Add(self.snappingUnit, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
00200 
00201         vertexSizer = wx.BoxSizer(wx.VERTICAL)
00202         self.snapVertex = wx.CheckBox(parent = panel, id = wx.ID_ANY,
00203                                       label = _("Snap also to vertex"))
00204         self.snapVertex.SetValue(UserSettings.Get(group = 'vdigit', key = "snapToVertex", subkey = 'enabled'))
00205         vertexSizer.Add(item = self.snapVertex, proportion = 0, flag = wx.EXPAND)
00206         self.mapUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
00207         self.snappingInfo = wx.StaticText(parent = panel, id = wx.ID_ANY,
00208                                           label = _("Snapping threshold is %(value).1f %(units)s") % \
00209                                               {'value' : self.digit.GetDisplay().GetThreshold(),
00210                                                'units' : self.mapUnits})
00211         vertexSizer.Add(item = self.snappingInfo, proportion = 0,
00212                         flag = wx.ALL | wx.EXPAND, border = 1)
00213 
00214         sizer.Add(item = flexSizer, proportion = 1, flag = wx.EXPAND)
00215         sizer.Add(item = vertexSizer, proportion = 1, flag = wx.EXPAND)
00216         border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
00217 
00218         #
00219         # select box
00220         #
00221         box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Select vector features"))
00222         # feature type
00223         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00224         inSizer = wx.BoxSizer(wx.HORIZONTAL)
00225         self.selectFeature = {}
00226         for feature in ('point', 'line',
00227                         'centroid', 'boundary'):
00228             chkbox = wx.CheckBox(parent = panel, label = feature)
00229             self.selectFeature[feature] = chkbox.GetId()
00230             chkbox.SetValue(UserSettings.Get(group = 'vdigit', key = 'selectType',
00231                                              subkey = [feature, 'enabled']))
00232             inSizer.Add(item = chkbox, proportion = 0,
00233                         flag = wx.EXPAND | wx.ALL, border = 5)
00234         sizer.Add(item = inSizer, proportion = 0, flag = wx.EXPAND)
00235         # threshold
00236         flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
00237         flexSizer.AddGrowableCol(0)
00238         text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select threshold"))
00239         self.selectThreshValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
00240                                              initial = UserSettings.Get(group = 'vdigit', key = "selectThresh", subkey = 'value'),
00241                                              min = 1, max = 1e6)
00242         units = wx.StaticText(parent = panel, id = wx.ID_ANY, size = (115, -1),
00243                               label = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'units'),
00244                               style = wx.ALIGN_LEFT)
00245         flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00246         flexSizer.Add(self.selectThreshValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00247         flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
00248                       border = 10)
00249 
00250         self.selectIn = wx.CheckBox(parent = panel, id = wx.ID_ANY,
00251                                     label = _("Select only features inside of selection bounding box"))
00252         self.selectIn.SetValue(UserSettings.Get(group = 'vdigit', key = "selectInside", subkey = 'enabled'))
00253         self.selectIn.SetToolTipString(_("By default are selected all features overlapping selection bounding box "))
00254         
00255         self.checkForDupl = wx.CheckBox(parent = panel, id = wx.ID_ANY,
00256                                         label = _("Check for duplicates"))
00257         self.checkForDupl.SetValue(UserSettings.Get(group = 'vdigit', key = "checkForDupl", subkey = 'enabled'))
00258 
00259 
00260         sizer.Add(item = flexSizer, proportion = 0, flag = wx.EXPAND)
00261         sizer.Add(item = self.selectIn, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1)
00262         sizer.Add(item = self.checkForDupl, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1)        
00263         border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
00264 
00265         #
00266         # digitize lines box
00267         #
00268         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Digitize line features"))
00269         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00270 
00271         self.intersect = wx.CheckBox(parent = panel, label = _("Break lines at intersection"))
00272         self.intersect.SetValue(UserSettings.Get(group = 'vdigit', key = 'breakLines', subkey = 'enabled'))
00273         
00274         sizer.Add(item = self.intersect, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00275 
00276         border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
00277 
00278         #
00279         # save-on-exit box
00280         #
00281         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Save changes"))
00282         # save changes on exit?
00283         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00284         self.save = wx.CheckBox(parent = panel, label = _("Save changes on exit"))
00285         self.save.SetValue(UserSettings.Get(group = 'vdigit', key = 'saveOnExit', subkey = 'enabled'))
00286         sizer.Add(item = self.save, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00287         border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
00288 
00289         panel.SetSizer(border)
00290         
00291         return panel
00292 
00293     def _createQueryPage(self, notebook):
00294         """!Create notebook page for query tool"""
00295         panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
00296         notebook.AddPage(page = panel, text = _("Query tool"))
00297 
00298         border = wx.BoxSizer(wx.VERTICAL)
00299 
00300         #
00301         # query tool box
00302         #
00303         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Choose query tool"))
00304         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00305 
00306         LocUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
00307 
00308         self.queryBox = wx.CheckBox(parent = panel, id = wx.ID_ANY, label = _("Select by box"))
00309         self.queryBox.SetValue(UserSettings.Get(group = 'vdigit', key = "query", subkey = 'box'))
00310 
00311         sizer.Add(item = self.queryBox, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00312         sizer.Add((0, 5))
00313 
00314         #
00315         # length
00316         #
00317         self.queryLength = wx.RadioButton(parent = panel, id = wx.ID_ANY, label = _("length"))
00318         self.queryLength.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
00319         sizer.Add(item = self.queryLength, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00320         flexSizer = wx.FlexGridSizer (cols = 4, hgap = 5, vgap = 5)
00321         flexSizer.AddGrowableCol(0)
00322         txt = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select lines"))
00323         self.queryLengthSL = wx.Choice (parent = panel, id = wx.ID_ANY, 
00324                                         choices  =  [_("shorter than"), _("longer than")])
00325         self.queryLengthSL.SetSelection(UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'than-selection'))
00326         self.queryLengthValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
00327                                             initial = 1,
00328                                             min = 0, max = 1e6)
00329         self.queryLengthValue.SetValue(UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'thresh'))
00330         units = wx.StaticText(parent = panel, id = wx.ID_ANY, label = "%s" % LocUnits)
00331         flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00332         flexSizer.Add(self.queryLengthSL, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00333         flexSizer.Add(self.queryLengthValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00334         flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00335         sizer.Add(item = flexSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00336 
00337         #
00338         # dangle
00339         #
00340         self.queryDangle = wx.RadioButton(parent = panel, id = wx.ID_ANY, label = _("dangle"))
00341         self.queryDangle.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
00342         sizer.Add(item = self.queryDangle, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00343         flexSizer = wx.FlexGridSizer (cols = 4, hgap = 5, vgap = 5)
00344         flexSizer.AddGrowableCol(0)
00345         txt = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select dangles"))
00346         self.queryDangleSL = wx.Choice (parent = panel, id = wx.ID_ANY, 
00347                                         choices = [_("shorter than"), _("longer than")])
00348         self.queryDangleSL.SetSelection(UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'than-selection'))
00349         self.queryDangleValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
00350                                        initial = 1,
00351                                        min = 0, max = 1e6)
00352         self.queryDangleValue.SetValue(UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'thresh'))
00353         units = wx.StaticText(parent = panel, id = wx.ID_ANY, label = "%s" % LocUnits)
00354         flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00355         flexSizer.Add(self.queryDangleSL, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00356         flexSizer.Add(self.queryDangleValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
00357         flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00358         sizer.Add(item = flexSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00359 
00360         if UserSettings.Get(group = 'vdigit', key = "query", subkey = 'selection') == 0:
00361             self.queryLength.SetValue(True)
00362         else:
00363             self.queryDangle.SetValue(True)
00364 
00365         # enable & disable items
00366         self.OnChangeQuery(None)
00367 
00368         border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
00369 
00370         panel.SetSizer(border)
00371         
00372         return panel
00373 
00374     def _createAttributesPage(self, notebook):
00375         """!Create notebook page for attributes"""
00376         panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
00377         notebook.AddPage(page = panel, text = _("Attributes"))
00378 
00379         border = wx.BoxSizer(wx.VERTICAL)
00380 
00381         #
00382         # add new record
00383         #
00384         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Digitize new feature"))
00385         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00386 
00387         # checkbox
00388         self.addRecord = wx.CheckBox(parent = panel, id = wx.ID_ANY,
00389                                      label = _("Add new record into table"))
00390         self.addRecord.SetValue(UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled'))
00391         sizer.Add(item = self.addRecord, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00392         # settings
00393         flexSizer = wx.FlexGridSizer(cols = 2, hgap = 3, vgap = 3)
00394         flexSizer.AddGrowableCol(0)
00395         settings = ((_("Layer"), 1), (_("Category"), 1), (_("Mode"), _("Next to use")))
00396         # layer
00397         text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Layer"))
00398         self.layer = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (125, -1),
00399                                  min = 1, max = 1e3)
00400         self.layer.SetValue(int(UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')))
00401         flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00402         flexSizer.Add(item = self.layer, proportion = 0,
00403                       flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
00404         # category number
00405         text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Category number"))
00406         self.category = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (125, -1),
00407                                     initial = UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'),
00408                                     min = -1e9, max = 1e9) 
00409         if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') != 1:
00410             self.category.Enable(False)
00411         flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00412         flexSizer.Add(item = self.category, proportion = 0,
00413                       flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
00414         # category mode
00415         text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Category mode"))
00416         self.categoryMode = wx.Choice(parent = panel, id = wx.ID_ANY, size = (125, -1),
00417                                       choices = [_("Next to use"), _("Manual entry"), _("No category")])
00418         self.categoryMode.SetSelection(UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection'))
00419         flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
00420         flexSizer.Add(item = self.categoryMode, proportion = 0,
00421                       flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
00422 
00423         sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
00424         border.Add(item = sizer, proportion = 0,
00425                    flag = wx.ALL | wx.EXPAND, border = 5)
00426 
00427         #
00428         # delete existing record
00429         #
00430         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Delete existing feature(s)"))
00431         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00432         
00433         # checkbox
00434         self.deleteRecord = wx.CheckBox(parent = panel, id = wx.ID_ANY,
00435                                         label = _("Delete record from table"))
00436         self.deleteRecord.SetValue(UserSettings.Get(group = 'vdigit', key = "delRecord", subkey = 'enabled'))
00437         sizer.Add(item = self.deleteRecord, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
00438         border.Add(item = sizer, proportion = 0,
00439                    flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
00440 
00441         #
00442         # geometry attributes (currently only length and area are supported)
00443         #
00444         box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
00445                               label = " %s " % _("Geometry attributes"))
00446         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00447         gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
00448         gridSizer.AddGrowableCol(0)
00449         self.geomAttrb = { 'length' : { 'label' : _('length') },
00450                            'area' : { 'label' : _('area') },
00451                            'perimeter' : { 'label' : _('perimeter') } }
00452 
00453         digitToolbar = self.parent.toolbars['vdigit']
00454         try:
00455             vectorName = digitToolbar.GetLayer().GetName()
00456         except AttributeError:
00457             vectorName = None # no vector selected for editing
00458         layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
00459         mapLayer = self.parent.toolbars['vdigit'].GetLayer()
00460         tree = self.parent.tree
00461         item = tree.FindItemByData('maplayer', mapLayer)
00462         row = 0
00463         for attrb in ['length', 'area', 'perimeter']:
00464             # checkbox
00465             check = wx.CheckBox(parent = panel, id = wx.ID_ANY,
00466                                 label = self.geomAttrb[attrb]['label'])
00467             ### self.deleteRecord.SetValue(UserSettings.Get(group='vdigit', key="delRecord", subkey='enabled'))
00468             check.Bind(wx.EVT_CHECKBOX, self.OnGeomAttrb)
00469             # column (only numeric)
00470             column = gselect.ColumnSelect(parent = panel, size = (200, -1))
00471             column.InsertColumns(vector = vectorName,
00472                                  layer = layer, excludeKey = True,
00473                                  type = ['integer', 'double precision'])
00474             # units 
00475             if attrb == 'area':
00476                 choices = Units.GetUnitsList('area')
00477             else:
00478                 choices = Units.GetUnitsList('length')
00479             win_units = wx.Choice(parent = panel, id = wx.ID_ANY,
00480                                   choices = choices, size = (120, -1))
00481             
00482             # default values
00483             check.SetValue(False)
00484             if item and tree.GetPyData(item)[0]['vdigit'] and \
00485                     'geomAttr' in tree.GetPyData(item)[0]['vdigit'] and \
00486                     attrb in tree.GetPyData(item)[0]['vdigit']['geomAttr']:
00487                 check.SetValue(True)
00488                 column.SetStringSelection(tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['column'])
00489                 if attrb == 'area':
00490                     type = 'area'
00491                 else:
00492                     type = 'length'
00493                 unitsIdx = Units.GetUnitsIndex(type, 
00494                                                 tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['units'])
00495                 win_units.SetSelection(unitsIdx)
00496 
00497             if not vectorName:
00498                 check.Enable(False)
00499                 column.Enable(False)
00500             
00501             if not check.IsChecked():
00502                 column.Enable(False)
00503             
00504             self.geomAttrb[attrb]['check']  = check.GetId()
00505             self.geomAttrb[attrb]['column'] = column.GetId()
00506             self.geomAttrb[attrb]['units']  = win_units.GetId()
00507 
00508             gridSizer.Add(item = check,
00509                           flag = wx.ALIGN_CENTER_VERTICAL,
00510                           pos = (row, 0))
00511             gridSizer.Add(item = column,
00512                           pos = (row, 1))
00513             gridSizer.Add(item = win_units,
00514                           pos = (row, 2))
00515             row += 1
00516         
00517         note = '\n'.join(textwrap.wrap(_("Note: These settings are stored "
00518                                          "in the workspace not in the vector digitizer "
00519                                          "preferences."), 55))
00520         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
00521                                            label = note),
00522                       pos = (3, 0), span = (1, 3))
00523                       
00524         sizer.Add(item = gridSizer, proportion = 1,
00525                   flag = wx.ALL | wx.EXPAND, border = 1)
00526         border.Add(item = sizer, proportion = 0,
00527                    flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
00528 
00529         # bindings
00530         self.Bind(wx.EVT_CHECKBOX, self.OnChangeAddRecord, self.addRecord)
00531         self.Bind(wx.EVT_CHOICE, self.OnChangeCategoryMode, self.categoryMode)
00532         self.Bind(wx.EVT_SPINCTRL, self.OnChangeLayer, self.layer)
00533 
00534         panel.SetSizer(border)
00535         
00536         return panel
00537 
00538     def _symbologyData(self):
00539         """!Data for _createSymbologyPage()
00540 
00541         label | checkbox | color
00542         """
00543 
00544         return (
00545             #            ("Background", "symbolBackground"),
00546             (_("Highlight"), "highlight"),
00547             (_("Highlight (duplicates)"), "highlightDupl"),
00548             (_("Point"), "point"),
00549             (_("Line"), "line"),
00550             (_("Boundary (no area)"), "boundaryNo"),
00551             (_("Boundary (one area)"), "boundaryOne"),
00552             (_("Boundary (two areas)"), "boundaryTwo"),
00553             (_("Centroid (in area)"), "centroidIn"),
00554             (_("Centroid (outside area)"), "centroidOut"),
00555             (_("Centroid (duplicate in area)"), "centroidDup"),
00556             (_("Node (one line)"), "nodeOne"),
00557             (_("Node (two lines)"), "nodeTwo"),
00558             (_("Vertex"), "vertex"),
00559             (_("Area (closed boundary + centroid)"), "area"),
00560             (_("Direction"), "direction"),)
00561 
00562     def OnGeomAttrb(self, event):
00563         """!Register geometry attributes (enable/disable)
00564         """
00565         checked = event.IsChecked()
00566         id = event.GetId()
00567         key = None
00568         for attrb, val in self.geomAttrb.iteritems():
00569             if val['check'] == id:
00570                 key = attrb
00571                 break
00572         
00573         column = self.FindWindowById(self.geomAttrb[key]['column'])
00574         if checked:
00575             column.Enable()
00576         else:
00577             column.Enable(False)
00578         
00579     def OnChangeCategoryMode(self, event):
00580         """!Change category mode
00581         """
00582         mode = event.GetSelection()
00583         UserSettings.Set(group = 'vdigit', key = "categoryMode", subkey = 'selection', value = mode)
00584         if mode == 1: # manual entry
00585             self.category.Enable(True)
00586         elif self.category.IsEnabled(): # disable
00587             self.category.Enable(False)
00588         
00589         if mode == 2 and self.addRecord.IsChecked(): # no category
00590             self.addRecord.SetValue(False)
00591         
00592         self.digit.SetCategory()
00593         self.category.SetValue(UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value'))
00594 
00595     def OnChangeLayer(self, event):
00596         """!Layer changed
00597         """
00598         layer = event.GetInt()
00599         if layer > 0:
00600             UserSettings.Set(group = 'vdigit', key = 'layer', subkey = 'value', value = layer)
00601             self.digit.SetCategory()
00602             self.category.SetValue(UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value'))
00603             
00604         event.Skip()
00605 
00606     def OnChangeAddRecord(self, event):
00607         """!Checkbox 'Add new record' status changed
00608         """
00609         self.category.SetValue(self.digit.SetCategory())
00610             
00611     def OnChangeSnappingValue(self, event):
00612         """!Change snapping value - update static text
00613         """
00614         value = self.snappingValue.GetValue()
00615         
00616         if value < 0:
00617             region = self.parent.MapWindow.Map.GetRegion()
00618             res = (region['nsres'] + region['ewres']) / 2.
00619             threshold = self.digit.GetDisplay().GetThreshold(value = res)
00620         else:
00621             if self.snappingUnit.GetStringSelection() == "map units":
00622                 threshold = value
00623             else:
00624                 threshold = self.digit.GetDisplay().GetThreshold(value = value)
00625             
00626         if value == 0:
00627             self.snappingInfo.SetLabel(_("Snapping disabled"))
00628         elif value < 0:
00629             self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s "
00630                                          "(based on comp. resolution)") % 
00631                                        {'value' : threshold,
00632                                         'units' : self.mapUnits.lower()})
00633         else:
00634             self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") % 
00635                                        {'value' : threshold,
00636                                         'units' : self.mapUnits.lower()})
00637         
00638         event.Skip()
00639 
00640     def OnChangeSnappingUnits(self, event):
00641         """!Snapping units change -> update static text
00642         """
00643         value = self.snappingValue.GetValue()
00644         units = self.snappingUnit.GetStringSelection()
00645         threshold = self.digit.GetDisplay().GetThreshold(value = value, units = units)
00646 
00647         if units == "map units":
00648             self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") % 
00649                                        {'value' : value,
00650                                         'units' : self.mapUnits})
00651         else:
00652             self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") % 
00653                                        {'value' : threshold,
00654                                         'units' : self.mapUnits})
00655             
00656         event.Skip()
00657 
00658     def OnChangeQuery(self, event):
00659         """!Change query
00660         """
00661         if self.queryLength.GetValue():
00662             # length
00663             self.queryLengthSL.Enable(True)
00664             self.queryLengthValue.Enable(True)
00665             self.queryDangleSL.Enable(False)
00666             self.queryDangleValue.Enable(False)
00667         else:
00668             # dangle
00669             self.queryLengthSL.Enable(False)
00670             self.queryLengthValue.Enable(False)
00671             self.queryDangleSL.Enable(True)
00672             self.queryDangleValue.Enable(True)
00673 
00674     def OnSave(self, event):
00675         """!Button 'Save' pressed
00676         """
00677         self.UpdateSettings()
00678         self.parent.toolbars['vdigit'].settingsDialog = None
00679 
00680         fileSettings = {}
00681         UserSettings.ReadSettingsFile(settings = fileSettings)
00682         fileSettings['vdigit'] = UserSettings.Get(group = 'vdigit')
00683         
00684         file = UserSettings.SaveToFile(fileSettings)
00685         self.parent.GetLayerManager().goutput.WriteLog(_('Vector digitizer settings saved to file <%s>.') % file)
00686         
00687         self.Destroy()
00688 
00689         event.Skip()
00690         
00691     def OnApply(self, event):
00692         """!Button 'Apply' pressed
00693         """
00694         self.UpdateSettings()
00695 
00696     def OnCancel(self, event):
00697         """!Button 'Cancel' pressed
00698         """
00699         self.parent.toolbars['vdigit'].settingsDialog = None
00700         self.Destroy()
00701 
00702         if event:
00703             event.Skip()
00704         
00705     def UpdateSettings(self):
00706         """!Update digitizer settings
00707         """
00708         self.parent.GetLayerManager().WorkspaceChanged() # geometry attributes
00709         # symbology
00710         for key, (enabled, color) in self.symbology.iteritems():
00711             if enabled:
00712                 UserSettings.Set(group = 'vdigit', key = 'symbol',
00713                                  subkey = [key, 'enabled'],
00714                                  value = enabled.IsChecked())
00715                 UserSettings.Set(group = 'vdigit', key = 'symbol',
00716                                  subkey = [key, 'color'],
00717                                  value = tuple(color.GetColour()))
00718             else:
00719                 UserSettings.Set(group = 'vdigit', key = 'symbol',
00720                                  subkey = [key, 'color'],
00721                                  value = tuple(color.GetColour()))
00722         # display
00723         UserSettings.Set(group = 'vdigit', key = "lineWidth", subkey = 'value',
00724                          value = int(self.lineWidthValue.GetValue()))
00725 
00726         # snapping
00727         UserSettings.Set(group = 'vdigit', key = "snapping", subkey = 'value',
00728                          value = int(self.snappingValue.GetValue()))
00729         UserSettings.Set(group = 'vdigit', key = "snapping", subkey = 'units',
00730                          value = self.snappingUnit.GetStringSelection())
00731         UserSettings.Set(group = 'vdigit', key = "snapToVertex", subkey = 'enabled',
00732                          value = self.snapVertex.IsChecked())
00733         
00734         # digitize new feature
00735         UserSettings.Set(group = 'vdigit', key = "addRecord", subkey = 'enabled',
00736                          value = self.addRecord.IsChecked())
00737         UserSettings.Set(group = 'vdigit', key = "layer", subkey = 'value',
00738                          value = int(self.layer.GetValue()))
00739         UserSettings.Set(group = 'vdigit', key = "category", subkey = 'value',
00740                          value = int(self.category.GetValue()))
00741         UserSettings.Set(group = 'vdigit', key = "categoryMode", subkey = 'selection',
00742                          value = self.categoryMode.GetSelection())
00743 
00744         # delete existing feature
00745         UserSettings.Set(group = 'vdigit', key = "delRecord", subkey = 'enabled',
00746                          value = self.deleteRecord.IsChecked())
00747 
00748         # geometry attributes (workspace)
00749         mapLayer = self.parent.toolbars['vdigit'].GetLayer()
00750         tree = self.parent.tree
00751         item = tree.FindItemByData('maplayer', mapLayer)
00752         for key, val in self.geomAttrb.iteritems():
00753             checked = self.FindWindowById(val['check']).IsChecked()
00754             column  = self.FindWindowById(val['column']).GetValue()
00755             unitsIdx = self.FindWindowById(val['units']).GetSelection()
00756             if item and not tree.GetPyData(item)[0]['vdigit']: 
00757                 tree.GetPyData(item)[0]['vdigit'] = { 'geomAttr' : dict() }
00758             
00759             if checked: # enable
00760                 if key == 'area':
00761                     type = key
00762                 else:
00763                     type = 'length'
00764                 unitsKey = Units.GetUnitsKey(type, unitsIdx)
00765                 tree.GetPyData(item)[0]['vdigit']['geomAttr'][key] = { 'column' : column,
00766                                                                        'units' : unitsKey }
00767             else:
00768                 if item and tree.GetPyData(item)[0]['vdigit'] and \
00769                         key in tree.GetPyData(item)[0]['vdigit']['geomAttr']:
00770                     del tree.GetPyData(item)[0]['vdigit']['geomAttr'][key]
00771         
00772         # query tool
00773         if self.queryLength.GetValue():
00774             UserSettings.Set(group = 'vdigit', key = "query", subkey = 'selection',
00775                              value = 0)
00776         else:
00777             UserSettings.Set(group = 'vdigit', key = "query", subkey = 'type',
00778                              value = 1)
00779         UserSettings.Set(group = 'vdigit', key = "query", subkey = 'box',
00780                          value = self.queryBox.IsChecked())
00781         UserSettings.Set(group = 'vdigit', key = "queryLength", subkey = 'than-selection',
00782                          value = self.queryLengthSL.GetSelection())
00783         UserSettings.Set(group = 'vdigit', key = "queryLength", subkey = 'thresh',
00784                          value = int(self.queryLengthValue.GetValue()))
00785         UserSettings.Set(group = 'vdigit', key = "queryDangle", subkey = 'than-selection',
00786                          value = self.queryDangleSL.GetSelection())
00787         UserSettings.Set(group = 'vdigit', key = "queryDangle", subkey = 'thresh',
00788                          value = int(self.queryDangleValue.GetValue()))
00789 
00790         # select features
00791         for feature in ('point', 'line',
00792                         'centroid', 'boundary'):
00793             UserSettings.Set(group = 'vdigit', key = 'selectType',
00794                              subkey = [feature, 'enabled'],
00795                              value = self.FindWindowById(self.selectFeature[feature]).IsChecked())
00796         UserSettings.Set(group = 'vdigit', key = "selectThresh", subkey = 'value',
00797                          value = int(self.selectThreshValue.GetValue()))
00798         UserSettings.Set(group = 'vdigit', key = "checkForDupl", subkey = 'enabled',
00799                          value = self.checkForDupl.IsChecked())
00800         UserSettings.Set(group = 'vdigit', key = "selectInside", subkey = 'enabled',
00801                          value = self.selectIn.IsChecked())
00802 
00803         # on-exit
00804         UserSettings.Set(group = 'vdigit', key = "saveOnExit", subkey = 'enabled',
00805                          value = self.save.IsChecked())
00806 
00807         # break lines
00808         UserSettings.Set(group = 'vdigit', key = "breakLines", subkey = 'enabled',
00809                          value = self.intersect.IsChecked())
00810         
00811         self.digit.UpdateSettings()
00812         
00813         # redraw map if auto-rendering is enabled
00814         if self.parent.statusbarWin['render'].GetValue(): 
00815             self.parent.OnRender(None)
00816 
00817 class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
00818     def __init__(self, parent, title,
00819                  vectorName, query = None, cats = None,
00820                  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
00821         """!Dialog used to display/modify categories of vector objects
00822         
00823         @param parent
00824         @param title dialog title
00825         @param query {coordinates, qdist} - used by v.edit/v.what
00826         @param cats  directory of lines (layer/categories) - used by vdigit
00827         @param style dialog style
00828         """
00829         self.parent = parent       # mapdisplay.BufferedWindow class instance
00830         self.digit = parent.digit
00831         
00832         # map name
00833         self.vectorName = vectorName
00834         
00835         # line : {layer: [categories]}
00836         self.cats = {}
00837         
00838         # do not display dialog if no line is found (-> self.cats)
00839         if cats is None:
00840             if self._getCategories(query[0], query[1]) == 0 or not self.line:
00841                 Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
00842         else:
00843             self.cats = cats
00844             for line in cats.keys():
00845                 for layer in cats[line].keys():
00846                     self.cats[line][layer] = list(cats[line][layer])
00847             
00848             layers = []
00849             for layer in self.digit.GetLayers():
00850                 layers.append(str(layer))
00851         
00852         # make copy of cats (used for 'reload')
00853         self.cats_orig = copy.deepcopy(self.cats)
00854         
00855         wx.Dialog.__init__(self, parent = self.parent, id = wx.ID_ANY, title = title,
00856                            style = style, **kwargs)
00857         
00858         # list of categories
00859         box = wx.StaticBox(parent = self, id = wx.ID_ANY,
00860                            label = " %s " % _("List of categories - right-click to delete"))
00861         listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00862         self.list = CategoryListCtrl(parent = self, id = wx.ID_ANY,
00863                                      style = wx.LC_REPORT |
00864                                      wx.BORDER_NONE |
00865                                      wx.LC_SORT_ASCENDING |
00866                                      wx.LC_HRULES |
00867                                      wx.LC_VRULES)
00868         # sorter
00869         self.fid = self.cats.keys()[0]
00870         self.itemDataMap = self.list.Populate(self.cats[self.fid])
00871         listmix.ColumnSorterMixin.__init__(self, 2)
00872         self.fidMulti = wx.Choice(parent = self, id = wx.ID_ANY,
00873                                   size = (150, -1))
00874         self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
00875         self.fidText = wx.StaticText(parent = self, id = wx.ID_ANY)
00876         if len(self.cats.keys()) == 1:
00877             self.fidMulti.Show(False)
00878             self.fidText.SetLabel(str(self.fid))
00879         else:
00880             self.fidText.Show(False)
00881             choices = []
00882             for fid in self.cats.keys():
00883                 choices.append(str(fid))
00884             self.fidMulti.SetItems(choices)
00885             self.fidMulti.SetSelection(0)
00886         
00887         listSizer.Add(item = self.list, proportion = 1, flag = wx.EXPAND)
00888 
00889         # add new category
00890         box = wx.StaticBox(parent = self, id = wx.ID_ANY,
00891                            label = " %s " % _("Add new category"))
00892         addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
00893         flexSizer = wx.FlexGridSizer (cols = 5, hgap = 5, vgap = 5)
00894         flexSizer.AddGrowableCol(3)
00895 
00896         layerNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
00897                                  label = "%s:" % _("Layer"))
00898         self.layerNew = wx.Choice(parent = self, id = wx.ID_ANY, size = (75, -1),
00899                                   choices = layers)
00900         if len(layers) > 0:
00901             self.layerNew.SetSelection(0)
00902         
00903         catNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
00904                                label = "%s:" % _("Category"))
00905 
00906         try:
00907             newCat = max(self.cats[self.fid][1]) + 1
00908         except KeyError:
00909             newCat = 1
00910         self.catNew = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (75, -1),
00911                                   initial = newCat, min = 0, max = 1e9)
00912         btnAddCat = wx.Button(self, wx.ID_ADD)
00913         flexSizer.Add(item = layerNewTxt, proportion = 0,
00914                       flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
00915         flexSizer.Add(item = self.layerNew, proportion = 0,
00916                       flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
00917         flexSizer.Add(item = catNewTxt, proportion = 0,
00918                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
00919                       border = 10)
00920         flexSizer.Add(item = self.catNew, proportion = 0,
00921                       flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
00922         flexSizer.Add(item = btnAddCat, proportion = 0,
00923                       flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
00924         addSizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
00925 
00926         # buttons
00927         btnApply = wx.Button(self, wx.ID_APPLY)
00928         btnApply.SetToolTipString(_("Apply changes"))
00929         btnCancel = wx.Button(self, wx.ID_CANCEL)
00930         btnCancel.SetToolTipString(_("Ignore changes and close dialog"))
00931         btnOk = wx.Button(self, wx.ID_OK)
00932         btnOk.SetToolTipString(_("Apply changes and close dialog"))
00933         btnOk.SetDefault()
00934 
00935         # sizers
00936         btnSizer = wx.StdDialogButtonSizer()
00937         btnSizer.AddButton(btnCancel)
00938         #btnSizer.AddButton(btnReload)
00939         #btnSizer.SetNegativeButton(btnReload)
00940         btnSizer.AddButton(btnApply)
00941         btnSizer.AddButton(btnOk)
00942         btnSizer.Realize()
00943         
00944         mainSizer = wx.BoxSizer(wx.VERTICAL)
00945         mainSizer.Add(item = listSizer, proportion = 1,
00946                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
00947         mainSizer.Add(item = addSizer, proportion = 0,
00948                       flag = wx.EXPAND | wx.ALIGN_CENTER |
00949                       wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
00950         fidSizer = wx.BoxSizer(wx.HORIZONTAL)
00951         fidSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
00952                                         label = _("Feature id:")),
00953                      proportion = 0, border = 5,
00954                      flag = wx.ALIGN_CENTER_VERTICAL)
00955         fidSizer.Add(item = self.fidMulti, proportion = 0,
00956                      flag = wx.EXPAND | wx.ALL,  border = 5)
00957         fidSizer.Add(item = self.fidText, proportion = 0,
00958                      flag = wx.EXPAND | wx.ALL,  border = 5)
00959         mainSizer.Add(item = fidSizer, proportion = 0,
00960                       flag = wx.EXPAND | wx.ALL, border = 5)
00961         mainSizer.Add(item = btnSizer, proportion = 0,
00962                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
00963         
00964         self.SetSizer(mainSizer)
00965         mainSizer.Fit(self)
00966         self.SetAutoLayout(True)
00967 
00968         # set min size for dialog
00969         self.SetMinSize(self.GetBestSize())
00970 
00971         # bindings
00972         btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
00973         btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
00974         btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
00975         btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
00976                                      
00977         # list
00978         self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
00979         self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
00980         self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
00981         self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
00982         self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
00983 
00984     def GetListCtrl(self):
00985         """!Used by ColumnSorterMixin
00986         """
00987         return self.list
00988 
00989     def OnColClick(self, event):
00990         """!Click on column header (order by)
00991         """
00992         event.Skip()
00993         
00994     def OnBeginEdit(self, event):
00995         """!Editing of item started
00996         """
00997         event.Allow()
00998 
00999     def OnEndEdit(self, event):
01000         """!Finish editing of item
01001         """
01002         itemIndex = event.GetIndex()
01003         layerOld = int (self.list.GetItem(itemIndex, 0).GetText())
01004         catOld = int (self.list.GetItem(itemIndex, 1).GetText())
01005 
01006         if event.GetColumn() == 0:
01007             layerNew = int(event.GetLabel())
01008             catNew = catOld
01009         else:
01010             layerNew = layerOld
01011             catNew = int(event.GetLabel())
01012         
01013         try:
01014             if layerNew not in self.cats[self.fid].keys():
01015                 self.cats[self.fid][layerNew] = []
01016             self.cats[self.fid][layerNew].append(catNew)
01017             self.cats[self.fid][layerOld].remove(catOld)
01018         except:
01019             event.Veto()
01020             self.list.SetStringItem(itemIndex, 0, str(layerNew))
01021             self.list.SetStringItem(itemIndex, 1, str(catNew))
01022             dlg = wx.MessageDialog(self, _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
01023                                            "Layer and category number must be integer.\n"
01024                                            "Layer number must be greater than zero.") %
01025                                    { 'layer': self.layerNew.GetStringSelection(),
01026                                      'category' : str(self.catNew.GetValue()) },
01027                                    _("Error"), wx.OK | wx.ICON_ERROR)
01028             dlg.ShowModal()
01029             dlg.Destroy()
01030             return False
01031 
01032     def OnRightDown(self, event):
01033         """!Mouse right button down
01034         """
01035         x = event.GetX()
01036         y = event.GetY()
01037         item, flags = self.list.HitTest((x, y))
01038 
01039         if item !=  wx.NOT_FOUND and \
01040                 flags & wx.LIST_HITTEST_ONITEM:
01041             self.list.Select(item)
01042 
01043         event.Skip()
01044 
01045     def OnRightUp(self, event):
01046         """!Mouse right button up
01047         """
01048         if not hasattr(self, "popupID1"):
01049             self.popupID1 = wx.NewId()
01050             self.popupID2 = wx.NewId()
01051             self.popupID3 = wx.NewId()
01052             self.Bind(wx.EVT_MENU, self.OnItemDelete,    id = self.popupID1)
01053             self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, id = self.popupID2)
01054             self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3)
01055 
01056         # generate popup-menu
01057         menu = wx.Menu()
01058         menu.Append(self.popupID1, _("Delete selected"))
01059         if self.list.GetFirstSelected() == -1:
01060             menu.Enable(self.popupID1, False)
01061 
01062         menu.Append(self.popupID2, _("Delete all"))
01063         menu.AppendSeparator()
01064         menu.Append(self.popupID3, _("Reload"))
01065 
01066         self.PopupMenu(menu)
01067         menu.Destroy()
01068 
01069     def OnItemSelected(self, event):
01070         """!Item selected
01071         """
01072         event.Skip()
01073 
01074     def OnItemDelete(self, event):
01075         """!Delete selected item(s) from the list (layer/category pair)
01076         """
01077         item = self.list.GetFirstSelected()
01078         while item != -1:
01079             layer = int (self.list.GetItem(item, 0).GetText())
01080             cat = int (self.list.GetItem(item, 1).GetText())
01081             self.list.DeleteItem(item)
01082             self.cats[self.fid][layer].remove(cat)
01083 
01084             item = self.list.GetFirstSelected()
01085             
01086         event.Skip()
01087         
01088     def OnItemDeleteAll(self, event):
01089         """!Delete all items from the list
01090         """
01091         self.list.DeleteAllItems()
01092         self.cats[self.fid] = {}
01093 
01094         event.Skip()
01095 
01096     def OnFeature(self, event):
01097         """!Feature id changed (on duplicates)
01098         """
01099         self.fid = int(event.GetString())
01100         
01101         self.itemDataMap = self.list.Populate(self.cats[self.fid],
01102                                               update = True)
01103 
01104         try:
01105             newCat = max(self.cats[self.fid][1]) + 1
01106         except KeyError:
01107             newCat = 1
01108             
01109         self.catNew.SetValue(newCat)
01110         
01111         event.Skip()
01112         
01113     def _getCategories(self, coords, qdist):
01114         """!Get layer/category pairs for all available
01115         layers
01116 
01117         Return True line found or False if not found
01118         """
01119         ret = gcmd.RunCommand('v.what',
01120                               parent = self,
01121                               quiet = True,
01122                               map = self.vectorName,
01123                               east_north = '%f,%f' % \
01124                                   (float(coords[0]), float(coords[1])),
01125                               distance = qdist)
01126 
01127         if not ret:
01128             return False
01129 
01130         for item in ret.splitlines():
01131             litem = item.lower()
01132             if "id:" in litem: # get line id
01133                 self.line = int(item.split(':')[1].strip())
01134             elif "layer:" in litem: # add layer
01135                 layer = int(item.split(':')[1].strip())
01136                 if layer not in self.cats.keys():
01137                     self.cats[layer] = []
01138             elif "category:" in litem: # add category
01139                 self.cats[layer].append(int(item.split(':')[1].strip()))
01140 
01141         return True
01142 
01143     def OnReload(self, event):
01144         """!Reload button pressed
01145         """
01146         # restore original list
01147         self.cats = copy.deepcopy(self.cats_orig)
01148 
01149         # polulate list
01150         self.itemDataMap = self.list.Populate(self.cats[self.fid],
01151                                               update = True)
01152 
01153         event.Skip()
01154 
01155     def OnCancel(self, event):
01156         """!Cancel button pressed
01157         """
01158         self.parent.parent.dialogs['category'] = None
01159         if self.digit:
01160             self.digit.GetDisplay().SetSelected([])
01161             self.parent.UpdateMap(render = False)
01162         else:
01163             self.parent.parent.OnRender(None)
01164             
01165         self.Close()
01166 
01167     def OnApply(self, event):
01168         """!Apply button pressed
01169         """
01170         for fid in self.cats.keys():
01171             newfid = self.ApplyChanges(fid)
01172             if fid == self.fid and newfid > 0:
01173                 self.fid = newfid
01174             
01175     def ApplyChanges(self, fid):
01176         """!Apply changes 
01177 
01178         @param fid feature id
01179         """
01180         cats = self.cats[fid]
01181         cats_orig = self.cats_orig[fid]
01182 
01183         # action : (catsFrom, catsTo)
01184         check = {'catadd': (cats,      cats_orig),
01185                  'catdel': (cats_orig, cats)}
01186 
01187         newfid = -1
01188         
01189         # add/delete new category
01190         for action, catsCurr in check.iteritems():
01191             for layer in catsCurr[0].keys():
01192                 catList = []
01193                 for cat in catsCurr[0][layer]:
01194                     if layer not in catsCurr[1].keys() or \
01195                             cat not in catsCurr[1][layer]:
01196                         catList.append(cat)
01197                 if catList != []:
01198                     if action == 'catadd':
01199                         add = True
01200                     else:
01201                         add = False
01202                         
01203                     newfid = self.digit.SetLineCats(fid, layer,
01204                                                     catList, add)
01205                     if len(self.cats.keys()) == 1:
01206                         self.fidText.SetLabel("%d" % newfid)
01207                     else:
01208                         choices = self.fidMulti.GetItems()
01209                         choices[choices.index(str(fid))] = str(newfid)
01210                         self.fidMulti.SetItems(choices)
01211                         self.fidMulti.SetStringSelection(str(newfid))
01212                     
01213                     self.cats[newfid] = self.cats[fid]
01214                     del self.cats[fid]
01215                     
01216                     fid = newfid
01217                     if self.fid < 0:
01218                         wx.MessageBox(parent = self, message = _("Unable to update vector map."),
01219                                       caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
01220         
01221         self.cats_orig[fid] = copy.deepcopy(cats)
01222         
01223         return newfid
01224 
01225     def OnOK(self, event):
01226         """!OK button pressed
01227         """
01228         self.OnApply(event)
01229         self.OnCancel(event)
01230 
01231     def OnAddCat(self, event):
01232         """!Button 'Add' new category pressed
01233         """
01234         try:
01235             layer = int(self.layerNew.GetStringSelection())
01236             cat   = int(self.catNew.GetValue())
01237             if layer <= 0:
01238                 raise ValueError
01239         except ValueError:
01240             gcmd.GError(parent = self,
01241                         message = _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
01242                                     "Layer and category number must be integer.\n"
01243                                     "Layer number must be greater then zero.") %
01244                         {'layer' : str(self.layerNew.GetValue()),
01245                          'category' : str(self.catNew.GetValue())})
01246             return False
01247         
01248         if layer not in self.cats[self.fid].keys():
01249             self.cats[self.fid][layer] = []
01250         
01251         self.cats[self.fid][layer].append(cat)
01252         
01253         # reload list
01254         self.itemDataMap = self.list.Populate(self.cats[self.fid],
01255                                               update = True)
01256         
01257         # update category number for add
01258         self.catNew.SetValue(cat + 1)
01259         
01260         event.Skip()
01261 
01262         return True
01263 
01264     def GetLine(self):
01265         """!Get id of selected line of 'None' if no line is selected
01266         """
01267         return self.cats.keys()
01268 
01269     def UpdateDialog(self, query = None, cats = None):
01270         """!Update dialog
01271         
01272         @param query {coordinates, distance} - v.what
01273         @param cats  directory layer/cats    - vdigit
01274         Return True if updated otherwise False
01275         """
01276         # line: {layer: [categories]}
01277         self.cats = {}
01278         # do not display dialog if no line is found (-> self.cats)
01279         if cats is None:
01280             ret = self._getCategories(query[0], query[1])
01281         else:
01282             self.cats = cats
01283             for line in cats.keys():
01284                 for layer in cats[line].keys():
01285                     self.cats[line][layer] = list(cats[line][layer])
01286             ret = 1
01287         if ret == 0 or len(self.cats.keys()) < 1:
01288             Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
01289             return False
01290         
01291         # make copy of cats (used for 'reload')
01292         self.cats_orig = copy.deepcopy(self.cats)
01293 
01294         # polulate list
01295         self.fid = self.cats.keys()[0]
01296         self.itemDataMap = self.list.Populate(self.cats[self.fid],
01297                                               update = True)
01298 
01299         try:
01300             newCat = max(self.cats[self.fid][1]) + 1
01301         except KeyError:
01302             newCat = 1
01303         self.catNew.SetValue(newCat)
01304         
01305         if len(self.cats.keys()) == 1:
01306             self.fidText.Show(True)
01307             self.fidMulti.Show(False)
01308             self.fidText.SetLabel("%d" % self.fid)
01309         else:
01310             self.fidText.Show(False)
01311             self.fidMulti.Show(True)
01312             choices = []
01313             for fid in self.cats.keys():
01314                 choices.append(str(fid))
01315             self.fidMulti.SetItems(choices)
01316             self.fidMulti.SetSelection(0)
01317 
01318         self.Layout()
01319         
01320         return True
01321 
01322 class CategoryListCtrl(wx.ListCtrl,
01323                        listmix.ListCtrlAutoWidthMixin,
01324                        listmix.TextEditMixin):
01325     def __init__(self, parent, id, pos = wx.DefaultPosition,
01326                  size = wx.DefaultSize, style = 0):
01327         """!List of layers/categories"""
01328         self.parent = parent
01329         
01330         wx.ListCtrl.__init__(self, parent, id, pos, size, style)
01331 
01332         listmix.ListCtrlAutoWidthMixin.__init__(self)
01333         listmix.TextEditMixin.__init__(self)
01334 
01335     def Populate(self, cats, update = False):
01336         """!Populate the list
01337         """
01338         itemData = {} # requested by sorter
01339 
01340         if not update:
01341             self.InsertColumn(0, _("Layer"))
01342             self.InsertColumn(1, _("Category"))
01343         else:
01344             self.DeleteAllItems()
01345 
01346         i = 1
01347         for layer in cats.keys():
01348             catsList = cats[layer]
01349             for cat in catsList:
01350                 index = self.InsertStringItem(sys.maxint, str(catsList[0]))
01351                 self.SetStringItem(index, 0, str(layer))
01352                 self.SetStringItem(index, 1, str(cat))
01353                 self.SetItemData(index, i)
01354                 itemData[i] = (str(layer), str(cat))
01355                 i = i + 1
01356 
01357         if not update:
01358             self.SetColumnWidth(0, 100)
01359             self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
01360 
01361         self.currentItem = 0
01362 
01363         return itemData
01364 
01365 class VDigitZBulkDialog(wx.Dialog):
01366     def __init__(self, parent, title, nselected, style = wx.DEFAULT_DIALOG_STYLE):
01367         """!Dialog used for Z bulk-labeling tool
01368         """
01369         wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
01370 
01371         self.parent = parent # mapdisplay.BufferedWindow class instance
01372 
01373         # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
01374 
01375         border = wx.BoxSizer(wx.VERTICAL)
01376         
01377         txt = wx.StaticText(parent = self,
01378                             label = _("%d lines selected for z bulk-labeling") % nselected);
01379         border.Add(item = txt, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
01380 
01381         box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Set value"))
01382         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
01383         flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
01384         flexSizer.AddGrowableCol(0)
01385 
01386         # starting value
01387         txt = wx.StaticText(parent = self,
01388                             label = _("Starting value"));
01389         self.value = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
01390                                  initial = 0,
01391                                  min = -1e6, max = 1e6)
01392         flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
01393         flexSizer.Add(self.value, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
01394 
01395         # step
01396         txt = wx.StaticText(parent = self,
01397                             label = _("Step"))
01398         self.step  = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
01399                                  initial = 0,
01400                                  min = 0, max = 1e6)
01401         flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
01402         flexSizer.Add(self.step, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
01403 
01404         sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
01405         border.Add(item = sizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 0)
01406 
01407         # buttons
01408         btnCancel = wx.Button(self, wx.ID_CANCEL)
01409         btnOk = wx.Button(self, wx.ID_OK)
01410         btnOk.SetDefault()
01411 
01412         # sizers
01413         btnSizer = wx.StdDialogButtonSizer()
01414         btnSizer.AddButton(btnCancel)
01415         btnSizer.AddButton(btnOk)
01416         btnSizer.Realize()
01417         
01418         mainSizer = wx.BoxSizer(wx.VERTICAL)
01419         mainSizer.Add(item = border, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
01420         mainSizer.Add(item = btnSizer, proportion = 0,
01421                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
01422 
01423         self.SetSizer(mainSizer)
01424         mainSizer.Fit(self)
01425 
01426 class VDigitDuplicatesDialog(wx.Dialog):
01427     def __init__(self, parent, data, title = _("List of duplicates"),
01428                  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
01429                  pos = wx.DefaultPosition):
01430         """!Show duplicated feature ids
01431         """
01432         wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style,
01433                            pos = pos)
01434         
01435         self.parent = parent # BufferedWindow
01436         self.data = data
01437         self.winList = []
01438 
01439         # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
01440 
01441         # notebook
01442         self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
01443 
01444         id = 1
01445         for key in self.data.keys():
01446             panel = wx.Panel(parent = self.notebook, id = wx.ID_ANY)
01447             self.notebook.AddPage(page = panel, text = " %d " % (id))
01448             
01449             # notebook body
01450             border = wx.BoxSizer(wx.VERTICAL)
01451 
01452             win = CheckListFeature(parent = panel, data = list(self.data[key]))
01453             self.winList.append(win.GetId())
01454 
01455             border.Add(item = win, proportion = 1,
01456                        flag = wx.ALL | wx.EXPAND, border = 5)
01457 
01458             panel.SetSizer(border)
01459 
01460             id += 1
01461 
01462         # buttons
01463         btnCancel = wx.Button(self, wx.ID_CANCEL)
01464         btnOk = wx.Button(self, wx.ID_OK)
01465         btnOk.SetDefault()
01466 
01467         # sizers
01468         btnSizer = wx.StdDialogButtonSizer()
01469         btnSizer.AddButton(btnCancel)
01470         btnSizer.AddButton(btnOk)
01471         btnSizer.Realize()
01472         
01473         mainSizer = wx.BoxSizer(wx.VERTICAL)
01474         mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
01475         mainSizer.Add(item = btnSizer, proportion = 0,
01476                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
01477 
01478         self.SetSizer(mainSizer)
01479         mainSizer.Fit(self)
01480         self.SetAutoLayout(True)
01481 
01482         # set min size for dialog
01483         self.SetMinSize((250, 180))
01484 
01485     def GetUnSelected(self):
01486         """!Get unselected items (feature id)
01487 
01488         @return list of ids
01489         """
01490         ids = []
01491         for id in self.winList:
01492             wlist = self.FindWindowById(id)
01493 
01494             for item in range(wlist.GetItemCount()):
01495                 if not wlist.IsChecked(item):
01496                     ids.append(int(wlist.GetItem(item, 0).GetText()))
01497                     
01498         return ids
01499 
01500 class CheckListFeature(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
01501     def __init__(self, parent, data,
01502                  pos = wx.DefaultPosition, log = None):
01503         """!List of mapset/owner/group
01504         """
01505         self.parent = parent
01506         self.data = data
01507 
01508         wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
01509                              style = wx.LC_REPORT)
01510 
01511         listmix.CheckListCtrlMixin.__init__(self)
01512 
01513         self.log = log
01514 
01515         # setup mixins
01516         listmix.ListCtrlAutoWidthMixin.__init__(self)
01517 
01518         self.LoadData(self.data)
01519 
01520     def LoadData(self, data):
01521         """!Load data into list
01522         """
01523         self.InsertColumn(0, _('Feature id'))
01524         self.InsertColumn(1, _('Layer (Categories)'))
01525 
01526         for item in data:
01527             index = self.InsertStringItem(sys.maxint, str(item[0]))
01528             self.SetStringItem(index, 1, str(item[1]))
01529 
01530         # enable all items by default
01531         for item in range(self.GetItemCount()):
01532             self.CheckItem(item, True)
01533 
01534         self.SetColumnWidth(col = 0, width = wx.LIST_AUTOSIZE_USEHEADER)
01535         self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE_USEHEADER)
01536                 
01537     def OnCheckItem(self, index, flag):
01538         """!Mapset checked/unchecked
01539         """
01540         pass
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines