GRASS Programmer's Manual  6.4.2(2012)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
vdigit.py
Go to the documentation of this file.
1 """!
2 @package vdigit
3 
4 @brief Dialogs for wxGUI vector digitizer
5 
6 Classes:
7  - VDigit
8  - VDigitSettingsDialog
9  - VDigitCategoryDialog
10  - CategoryListCtrl
11  - VDigitZBulkDialog
12  - VDigitDuplicatesDialog
13  - CheckListFeature
14 
15 (C) 2007-2011 by the GRASS Development Team
16 This program is free software under the GNU General Public License
17 (>=v2). Read the file COPYING that comes with GRASS for details.
18 
19 @author Martin Landa <landa.martin gmail.com>
20 """
21 
22 import os
23 import sys
24 import string
25 import copy
26 import textwrap
27 import traceback
28 
29 from threading import Thread
30 
31 import wx
32 import wx.lib.colourselect as csel
33 import wx.lib.mixins.listctrl as listmix
34 
35 import gcmd
36 import dbm
37 from debug import Debug as Debug
38 import gselect
39 import globalvar
40 from units import Units
41 from preferences import globalSettings as UserSettings
42 try:
43  from wxvdigit import IVDigit, GV_LINES
44  haveVDigit = True
45  errorMsg = ''
46 except (ImportError, NameError), err:
47  haveVDigit = False
48  errorMsg = err
49  GV_LINES = -1
50  class IVDigit:
51  def __init__(self):
52  pass
53 
54 class VDigit(IVDigit):
55  def __init__(self, mapwindow):
56  """!Base class of vector digitizer
57 
58  @param mapwindow reference to mapwindow (mapdisp_window.BufferedWindow) instance
59  """
60  IVDigit.__init__(self, mapwindow)
61 
62 class VDigitSettingsDialog(wx.Dialog):
63  def __init__(self, parent, title, style = wx.DEFAULT_DIALOG_STYLE):
64  """!Standard settings dialog for digitization purposes
65  """
66  wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
67 
68  self.parent = parent # mapdisplay.MapFrame class instance
69  self.digit = self.parent.MapWindow.digit
70 
71  # notebook
72  notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
73  self._createSymbologyPage(notebook)
74  self.digit.SetCategory()
75  self._createGeneralPage(notebook)
76  self._createAttributesPage(notebook)
77  self._createQueryPage(notebook)
78 
79  # buttons
80  btnApply = wx.Button(self, wx.ID_APPLY)
81  btnCancel = wx.Button(self, wx.ID_CANCEL)
82  btnSave = wx.Button(self, wx.ID_SAVE)
83  btnSave.SetDefault()
84 
85  # bindigs
86  btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
87  btnApply.SetToolTipString(_("Apply changes for this session"))
88  btnApply.SetDefault()
89  btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
90  btnSave.SetToolTipString(_("Close dialog and save changes to user settings file"))
91  btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
92  btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
93 
94  # sizers
95  btnSizer = wx.StdDialogButtonSizer()
96  btnSizer.AddButton(btnCancel)
97  btnSizer.AddButton(btnApply)
98  btnSizer.AddButton(btnSave)
99  btnSizer.Realize()
100 
101  mainSizer = wx.BoxSizer(wx.VERTICAL)
102  mainSizer.Add(item = notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
103  mainSizer.Add(item = btnSizer, proportion = 0,
104  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
105 
106  self.Bind(wx.EVT_CLOSE, self.OnCancel)
107 
108  self.SetSizer(mainSizer)
109  mainSizer.Fit(self)
110 
111  def _createSymbologyPage(self, notebook):
112  """!Create notebook page concerning symbology settings"""
113  panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
114  notebook.AddPage(page = panel, text = _("Symbology"))
115 
116  sizer = wx.BoxSizer(wx.VERTICAL)
117 
118  flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
119  flexSizer.AddGrowableCol(0)
120 
121  self.symbology = {}
122  for label, key in self._symbologyData():
123  textLabel = wx.StaticText(panel, wx.ID_ANY, label)
124  color = csel.ColourSelect(panel, id = wx.ID_ANY,
125  colour = UserSettings.Get(group = 'vdigit', key = 'symbol',
126  subkey = [key, 'color']), size = globalvar.DIALOG_COLOR_SIZE)
127  isEnabled = UserSettings.Get(group = 'vdigit', key = 'symbol',
128  subkey = [key, 'enabled'])
129  if isEnabled is not None:
130  enabled = wx.CheckBox(panel, id = wx.ID_ANY, label = "")
131  enabled.SetValue(isEnabled)
132  self.symbology[key] = (enabled, color)
133  else:
134  enabled = (1, 1)
135  self.symbology[key] = (None, color)
136 
137  flexSizer.Add(textLabel, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
138  flexSizer.Add(enabled, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
139  flexSizer.Add(color, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
140  color.SetName("GetColour")
141 
142  sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 10)
143 
144  panel.SetSizer(sizer)
145 
146  return panel
147 
148  def _createGeneralPage(self, notebook):
149  """!Create notebook page concerning general settings"""
150  panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
151  notebook.AddPage(page = panel, text = _("General"))
152 
153  border = wx.BoxSizer(wx.VERTICAL)
154 
155  #
156  # display section
157  #
158  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Display"))
159  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
160  flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
161  flexSizer.AddGrowableCol(0)
162  # line width
163  text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Line width"))
164  self.lineWidthValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
165  initial = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'value'),
166  min = 1, max = 1e6)
167  units = wx.StaticText(parent = panel, id = wx.ID_ANY, size = (115, -1),
168  label = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'units'),
169  style = wx.ALIGN_LEFT)
170  flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
171  flexSizer.Add(self.lineWidthValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
172  flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
173  border = 10)
174 
175  sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
176  border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
177 
178  #
179  # snapping section
180  #
181  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Snapping"))
182  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
183  flexSizer = wx.FlexGridSizer(cols = 3, hgap = 5, vgap = 5)
184  flexSizer.AddGrowableCol(0)
185 
186  # snapping
187  text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Snapping threshold"))
188  self.snappingValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
189  initial = UserSettings.Get(group = 'vdigit', key = "snapping", subkey = 'value'),
190  min = -1, max = 1e6)
191  self.snappingValue.Bind(wx.EVT_SPINCTRL, self.OnChangeSnappingValue)
192  self.snappingValue.Bind(wx.EVT_TEXT, self.OnChangeSnappingValue)
193  self.snappingUnit = wx.Choice(parent = panel, id = wx.ID_ANY, size = (125, -1),
194  choices = [_("screen pixels"), _("map units")])
195  self.snappingUnit.SetStringSelection(UserSettings.Get(group = 'vdigit', key = "snapping", subkey = 'units'))
196  self.snappingUnit.Bind(wx.EVT_CHOICE, self.OnChangeSnappingUnits)
197  flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
198  flexSizer.Add(self.snappingValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
199  flexSizer.Add(self.snappingUnit, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
200 
201  vertexSizer = wx.BoxSizer(wx.VERTICAL)
202  self.snapVertex = wx.CheckBox(parent = panel, id = wx.ID_ANY,
203  label = _("Snap also to vertex"))
204  self.snapVertex.SetValue(UserSettings.Get(group = 'vdigit', key = "snapToVertex", subkey = 'enabled'))
205  vertexSizer.Add(item = self.snapVertex, proportion = 0, flag = wx.EXPAND)
206  self.mapUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
207  self.snappingInfo = wx.StaticText(parent = panel, id = wx.ID_ANY,
208  label = _("Snapping threshold is %(value).1f %(units)s") % \
209  {'value' : self.digit.GetDisplay().GetThreshold(),
210  'units' : self.mapUnits})
211  vertexSizer.Add(item = self.snappingInfo, proportion = 0,
212  flag = wx.ALL | wx.EXPAND, border = 1)
213 
214  sizer.Add(item = flexSizer, proportion = 1, flag = wx.EXPAND)
215  sizer.Add(item = vertexSizer, proportion = 1, flag = wx.EXPAND)
216  border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
217 
218  #
219  # select box
220  #
221  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Select vector features"))
222  # feature type
223  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
224  inSizer = wx.BoxSizer(wx.HORIZONTAL)
225  self.selectFeature = {}
226  for feature in ('point', 'line',
227  'centroid', 'boundary'):
228  chkbox = wx.CheckBox(parent = panel, label = feature)
229  self.selectFeature[feature] = chkbox.GetId()
230  chkbox.SetValue(UserSettings.Get(group = 'vdigit', key = 'selectType',
231  subkey = [feature, 'enabled']))
232  inSizer.Add(item = chkbox, proportion = 0,
233  flag = wx.EXPAND | wx.ALL, border = 5)
234  sizer.Add(item = inSizer, proportion = 0, flag = wx.EXPAND)
235  # threshold
236  flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
237  flexSizer.AddGrowableCol(0)
238  text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select threshold"))
239  self.selectThreshValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
240  initial = UserSettings.Get(group = 'vdigit', key = "selectThresh", subkey = 'value'),
241  min = 1, max = 1e6)
242  units = wx.StaticText(parent = panel, id = wx.ID_ANY, size = (115, -1),
243  label = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'units'),
244  style = wx.ALIGN_LEFT)
245  flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
246  flexSizer.Add(self.selectThreshValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
247  flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
248  border = 10)
249 
250  self.selectIn = wx.CheckBox(parent = panel, id = wx.ID_ANY,
251  label = _("Select only features inside of selection bounding box"))
252  self.selectIn.SetValue(UserSettings.Get(group = 'vdigit', key = "selectInside", subkey = 'enabled'))
253  self.selectIn.SetToolTipString(_("By default are selected all features overlapping selection bounding box "))
254 
255  self.checkForDupl = wx.CheckBox(parent = panel, id = wx.ID_ANY,
256  label = _("Check for duplicates"))
257  self.checkForDupl.SetValue(UserSettings.Get(group = 'vdigit', key = "checkForDupl", subkey = 'enabled'))
258 
259 
260  sizer.Add(item = flexSizer, proportion = 0, flag = wx.EXPAND)
261  sizer.Add(item = self.selectIn, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1)
262  sizer.Add(item = self.checkForDupl, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1)
263  border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
264 
265  #
266  # digitize lines box
267  #
268  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Digitize line features"))
269  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
270 
271  self.intersect = wx.CheckBox(parent = panel, label = _("Break lines at intersection"))
272  self.intersect.SetValue(UserSettings.Get(group = 'vdigit', key = 'breakLines', subkey = 'enabled'))
273 
274  sizer.Add(item = self.intersect, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
275 
276  border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
277 
278  #
279  # save-on-exit box
280  #
281  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Save changes"))
282  # save changes on exit?
283  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
284  self.save = wx.CheckBox(parent = panel, label = _("Save changes on exit"))
285  self.save.SetValue(UserSettings.Get(group = 'vdigit', key = 'saveOnExit', subkey = 'enabled'))
286  sizer.Add(item = self.save, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
287  border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
288 
289  panel.SetSizer(border)
290 
291  return panel
292 
293  def _createQueryPage(self, notebook):
294  """!Create notebook page for query tool"""
295  panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
296  notebook.AddPage(page = panel, text = _("Query tool"))
297 
298  border = wx.BoxSizer(wx.VERTICAL)
299 
300  #
301  # query tool box
302  #
303  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Choose query tool"))
304  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
305 
306  LocUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
307 
308  self.queryBox = wx.CheckBox(parent = panel, id = wx.ID_ANY, label = _("Select by box"))
309  self.queryBox.SetValue(UserSettings.Get(group = 'vdigit', key = "query", subkey = 'box'))
310 
311  sizer.Add(item = self.queryBox, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
312  sizer.Add((0, 5))
313 
314  #
315  # length
316  #
317  self.queryLength = wx.RadioButton(parent = panel, id = wx.ID_ANY, label = _("length"))
318  self.queryLength.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
319  sizer.Add(item = self.queryLength, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
320  flexSizer = wx.FlexGridSizer (cols = 4, hgap = 5, vgap = 5)
321  flexSizer.AddGrowableCol(0)
322  txt = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select lines"))
323  self.queryLengthSL = wx.Choice (parent = panel, id = wx.ID_ANY,
324  choices = [_("shorter than"), _("longer than")])
325  self.queryLengthSL.SetSelection(UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'than-selection'))
326  self.queryLengthValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
327  initial = 1,
328  min = 0, max = 1e6)
329  self.queryLengthValue.SetValue(UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'thresh'))
330  units = wx.StaticText(parent = panel, id = wx.ID_ANY, label = "%s" % LocUnits)
331  flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
332  flexSizer.Add(self.queryLengthSL, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
333  flexSizer.Add(self.queryLengthValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
334  flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
335  sizer.Add(item = flexSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
336 
337  #
338  # dangle
339  #
340  self.queryDangle = wx.RadioButton(parent = panel, id = wx.ID_ANY, label = _("dangle"))
341  self.queryDangle.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
342  sizer.Add(item = self.queryDangle, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
343  flexSizer = wx.FlexGridSizer (cols = 4, hgap = 5, vgap = 5)
344  flexSizer.AddGrowableCol(0)
345  txt = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select dangles"))
346  self.queryDangleSL = wx.Choice (parent = panel, id = wx.ID_ANY,
347  choices = [_("shorter than"), _("longer than")])
348  self.queryDangleSL.SetSelection(UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'than-selection'))
349  self.queryDangleValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
350  initial = 1,
351  min = 0, max = 1e6)
352  self.queryDangleValue.SetValue(UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'thresh'))
353  units = wx.StaticText(parent = panel, id = wx.ID_ANY, label = "%s" % LocUnits)
354  flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
355  flexSizer.Add(self.queryDangleSL, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
356  flexSizer.Add(self.queryDangleValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
357  flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
358  sizer.Add(item = flexSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
359 
360  if UserSettings.Get(group = 'vdigit', key = "query", subkey = 'selection') == 0:
361  self.queryLength.SetValue(True)
362  else:
363  self.queryDangle.SetValue(True)
364 
365  # enable & disable items
366  self.OnChangeQuery(None)
367 
368  border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
369 
370  panel.SetSizer(border)
371 
372  return panel
373 
374  def _createAttributesPage(self, notebook):
375  """!Create notebook page for attributes"""
376  panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
377  notebook.AddPage(page = panel, text = _("Attributes"))
378 
379  border = wx.BoxSizer(wx.VERTICAL)
380 
381  #
382  # add new record
383  #
384  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Digitize new feature"))
385  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
386 
387  # checkbox
388  self.addRecord = wx.CheckBox(parent = panel, id = wx.ID_ANY,
389  label = _("Add new record into table"))
390  self.addRecord.SetValue(UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled'))
391  sizer.Add(item = self.addRecord, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
392  # settings
393  flexSizer = wx.FlexGridSizer(cols = 2, hgap = 3, vgap = 3)
394  flexSizer.AddGrowableCol(0)
395  settings = ((_("Layer"), 1), (_("Category"), 1), (_("Mode"), _("Next to use")))
396  # layer
397  text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Layer"))
398  self.layer = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (125, -1),
399  min = 1, max = 1e3)
400  self.layer.SetValue(int(UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')))
401  flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
402  flexSizer.Add(item = self.layer, proportion = 0,
403  flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
404  # category number
405  text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Category number"))
406  self.category = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (125, -1),
407  initial = UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'),
408  min = -1e9, max = 1e9)
409  if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') != 1:
410  self.category.Enable(False)
411  flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
412  flexSizer.Add(item = self.category, proportion = 0,
413  flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
414  # category mode
415  text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Category mode"))
416  self.categoryMode = wx.Choice(parent = panel, id = wx.ID_ANY, size = (125, -1),
417  choices = [_("Next to use"), _("Manual entry"), _("No category")])
418  self.categoryMode.SetSelection(UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection'))
419  flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
420  flexSizer.Add(item = self.categoryMode, proportion = 0,
421  flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
422 
423  sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
424  border.Add(item = sizer, proportion = 0,
425  flag = wx.ALL | wx.EXPAND, border = 5)
426 
427  #
428  # delete existing record
429  #
430  box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Delete existing feature(s)"))
431  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
432 
433  # checkbox
434  self.deleteRecord = wx.CheckBox(parent = panel, id = wx.ID_ANY,
435  label = _("Delete record from table"))
436  self.deleteRecord.SetValue(UserSettings.Get(group = 'vdigit', key = "delRecord", subkey = 'enabled'))
437  sizer.Add(item = self.deleteRecord, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
438  border.Add(item = sizer, proportion = 0,
439  flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
440 
441  #
442  # geometry attributes (currently only length and area are supported)
443  #
444  box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
445  label = " %s " % _("Geometry attributes"))
446  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
447  gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
448  gridSizer.AddGrowableCol(0)
449  self.geomAttrb = { 'length' : { 'label' : _('length') },
450  'area' : { 'label' : _('area') },
451  'perimeter' : { 'label' : _('perimeter') } }
452 
453  digitToolbar = self.parent.toolbars['vdigit']
454  try:
455  vectorName = digitToolbar.GetLayer().GetName()
456  except AttributeError:
457  vectorName = None # no vector selected for editing
458  layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
459  mapLayer = self.parent.toolbars['vdigit'].GetLayer()
460  tree = self.parent.tree
461  item = tree.FindItemByData('maplayer', mapLayer)
462  row = 0
463  for attrb in ['length', 'area', 'perimeter']:
464  # checkbox
465  check = wx.CheckBox(parent = panel, id = wx.ID_ANY,
466  label = self.geomAttrb[attrb]['label'])
467  ### self.deleteRecord.SetValue(UserSettings.Get(group='vdigit', key="delRecord", subkey='enabled'))
468  check.Bind(wx.EVT_CHECKBOX, self.OnGeomAttrb)
469  # column (only numeric)
470  column = gselect.ColumnSelect(parent = panel, size = (200, -1))
471  column.InsertColumns(vector = vectorName,
472  layer = layer, excludeKey = True,
473  type = ['integer', 'double precision'])
474  # units
475  if attrb == 'area':
476  choices = Units.GetUnitsList('area')
477  else:
478  choices = Units.GetUnitsList('length')
479  win_units = wx.Choice(parent = panel, id = wx.ID_ANY,
480  choices = choices, size = (120, -1))
481 
482  # default values
483  check.SetValue(False)
484  if item and tree.GetPyData(item)[0]['vdigit'] and \
485  'geomAttr' in tree.GetPyData(item)[0]['vdigit'] and \
486  attrb in tree.GetPyData(item)[0]['vdigit']['geomAttr']:
487  check.SetValue(True)
488  column.SetStringSelection(tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['column'])
489  if attrb == 'area':
490  type = 'area'
491  else:
492  type = 'length'
493  unitsIdx = Units.GetUnitsIndex(type,
494  tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['units'])
495  win_units.SetSelection(unitsIdx)
496 
497  if not vectorName:
498  check.Enable(False)
499  column.Enable(False)
500 
501  if not check.IsChecked():
502  column.Enable(False)
503 
504  self.geomAttrb[attrb]['check'] = check.GetId()
505  self.geomAttrb[attrb]['column'] = column.GetId()
506  self.geomAttrb[attrb]['units'] = win_units.GetId()
507 
508  gridSizer.Add(item = check,
509  flag = wx.ALIGN_CENTER_VERTICAL,
510  pos = (row, 0))
511  gridSizer.Add(item = column,
512  pos = (row, 1))
513  gridSizer.Add(item = win_units,
514  pos = (row, 2))
515  row += 1
516 
517  note = '\n'.join(textwrap.wrap(_("Note: These settings are stored "
518  "in the workspace not in the vector digitizer "
519  "preferences."), 55))
520  gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
521  label = note),
522  pos = (3, 0), span = (1, 3))
523 
524  sizer.Add(item = gridSizer, proportion = 1,
525  flag = wx.ALL | wx.EXPAND, border = 1)
526  border.Add(item = sizer, proportion = 0,
527  flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
528 
529  # bindings
530  self.Bind(wx.EVT_CHECKBOX, self.OnChangeAddRecord, self.addRecord)
531  self.Bind(wx.EVT_CHOICE, self.OnChangeCategoryMode, self.categoryMode)
532  self.Bind(wx.EVT_SPINCTRL, self.OnChangeLayer, self.layer)
533 
534  panel.SetSizer(border)
535 
536  return panel
537 
538  def _symbologyData(self):
539  """!Data for _createSymbologyPage()
540 
541  label | checkbox | color
542  """
543 
544  return (
545  # ("Background", "symbolBackground"),
546  (_("Highlight"), "highlight"),
547  (_("Highlight (duplicates)"), "highlightDupl"),
548  (_("Point"), "point"),
549  (_("Line"), "line"),
550  (_("Boundary (no area)"), "boundaryNo"),
551  (_("Boundary (one area)"), "boundaryOne"),
552  (_("Boundary (two areas)"), "boundaryTwo"),
553  (_("Centroid (in area)"), "centroidIn"),
554  (_("Centroid (outside area)"), "centroidOut"),
555  (_("Centroid (duplicate in area)"), "centroidDup"),
556  (_("Node (one line)"), "nodeOne"),
557  (_("Node (two lines)"), "nodeTwo"),
558  (_("Vertex"), "vertex"),
559  (_("Area (closed boundary + centroid)"), "area"),
560  (_("Direction"), "direction"),)
561 
562  def OnGeomAttrb(self, event):
563  """!Register geometry attributes (enable/disable)
564  """
565  checked = event.IsChecked()
566  id = event.GetId()
567  key = None
568  for attrb, val in self.geomAttrb.iteritems():
569  if val['check'] == id:
570  key = attrb
571  break
572 
573  column = self.FindWindowById(self.geomAttrb[key]['column'])
574  if checked:
575  column.Enable()
576  else:
577  column.Enable(False)
578 
579  def OnChangeCategoryMode(self, event):
580  """!Change category mode
581  """
582  mode = event.GetSelection()
583  UserSettings.Set(group = 'vdigit', key = "categoryMode", subkey = 'selection', value = mode)
584  if mode == 1: # manual entry
585  self.category.Enable(True)
586  elif self.category.IsEnabled(): # disable
587  self.category.Enable(False)
588 
589  if mode == 2 and self.addRecord.IsChecked(): # no category
590  self.addRecord.SetValue(False)
591 
592  self.digit.SetCategory()
593  self.category.SetValue(UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value'))
594 
595  def OnChangeLayer(self, event):
596  """!Layer changed
597  """
598  layer = event.GetInt()
599  if layer > 0:
600  UserSettings.Set(group = 'vdigit', key = 'layer', subkey = 'value', value = layer)
601  self.digit.SetCategory()
602  self.category.SetValue(UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value'))
603 
604  event.Skip()
605 
606  def OnChangeAddRecord(self, event):
607  """!Checkbox 'Add new record' status changed
608  """
609  self.category.SetValue(self.digit.SetCategory())
610 
611  def OnChangeSnappingValue(self, event):
612  """!Change snapping value - update static text
613  """
614  value = self.snappingValue.GetValue()
615 
616  if value < 0:
617  region = self.parent.MapWindow.Map.GetRegion()
618  res = (region['nsres'] + region['ewres']) / 2.
619  threshold = self.digit.GetDisplay().GetThreshold(value = res)
620  else:
621  if self.snappingUnit.GetStringSelection() == "map units":
622  threshold = value
623  else:
624  threshold = self.digit.GetDisplay().GetThreshold(value = value)
625 
626  if value == 0:
627  self.snappingInfo.SetLabel(_("Snapping disabled"))
628  elif value < 0:
629  self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s "
630  "(based on comp. resolution)") %
631  {'value' : threshold,
632  'units' : self.mapUnits.lower()})
633  else:
634  self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") %
635  {'value' : threshold,
636  'units' : self.mapUnits.lower()})
637 
638  event.Skip()
639 
640  def OnChangeSnappingUnits(self, event):
641  """!Snapping units change -> update static text
642  """
643  value = self.snappingValue.GetValue()
644  units = self.snappingUnit.GetStringSelection()
645  threshold = self.digit.GetDisplay().GetThreshold(value = value, units = units)
646 
647  if units == "map units":
648  self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") %
649  {'value' : value,
650  'units' : self.mapUnits})
651  else:
652  self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") %
653  {'value' : threshold,
654  'units' : self.mapUnits})
655 
656  event.Skip()
657 
658  def OnChangeQuery(self, event):
659  """!Change query
660  """
661  if self.queryLength.GetValue():
662  # length
663  self.queryLengthSL.Enable(True)
664  self.queryLengthValue.Enable(True)
665  self.queryDangleSL.Enable(False)
666  self.queryDangleValue.Enable(False)
667  else:
668  # dangle
669  self.queryLengthSL.Enable(False)
670  self.queryLengthValue.Enable(False)
671  self.queryDangleSL.Enable(True)
672  self.queryDangleValue.Enable(True)
673 
674  def OnSave(self, event):
675  """!Button 'Save' pressed
676  """
677  self.UpdateSettings()
678  self.parent.toolbars['vdigit'].settingsDialog = None
679 
680  fileSettings = {}
681  UserSettings.ReadSettingsFile(settings = fileSettings)
682  fileSettings['vdigit'] = UserSettings.Get(group = 'vdigit')
683 
684  file = UserSettings.SaveToFile(fileSettings)
685  self.parent.GetLayerManager().goutput.WriteLog(_('Vector digitizer settings saved to file <%s>.') % file)
686 
687  self.Destroy()
688 
689  event.Skip()
690 
691  def OnApply(self, event):
692  """!Button 'Apply' pressed
693  """
694  self.UpdateSettings()
695 
696  def OnCancel(self, event):
697  """!Button 'Cancel' pressed
698  """
699  self.parent.toolbars['vdigit'].settingsDialog = None
700  self.Destroy()
701 
702  if event:
703  event.Skip()
704 
705  def UpdateSettings(self):
706  """!Update digitizer settings
707  """
708  self.parent.GetLayerManager().WorkspaceChanged() # geometry attributes
709  # symbology
710  for key, (enabled, color) in self.symbology.iteritems():
711  if enabled:
712  UserSettings.Set(group = 'vdigit', key = 'symbol',
713  subkey = [key, 'enabled'],
714  value = enabled.IsChecked())
715  UserSettings.Set(group = 'vdigit', key = 'symbol',
716  subkey = [key, 'color'],
717  value = tuple(color.GetColour()))
718  else:
719  UserSettings.Set(group = 'vdigit', key = 'symbol',
720  subkey = [key, 'color'],
721  value = tuple(color.GetColour()))
722  # display
723  UserSettings.Set(group = 'vdigit', key = "lineWidth", subkey = 'value',
724  value = int(self.lineWidthValue.GetValue()))
725 
726  # snapping
727  UserSettings.Set(group = 'vdigit', key = "snapping", subkey = 'value',
728  value = int(self.snappingValue.GetValue()))
729  UserSettings.Set(group = 'vdigit', key = "snapping", subkey = 'units',
730  value = self.snappingUnit.GetStringSelection())
731  UserSettings.Set(group = 'vdigit', key = "snapToVertex", subkey = 'enabled',
732  value = self.snapVertex.IsChecked())
733 
734  # digitize new feature
735  UserSettings.Set(group = 'vdigit', key = "addRecord", subkey = 'enabled',
736  value = self.addRecord.IsChecked())
737  UserSettings.Set(group = 'vdigit', key = "layer", subkey = 'value',
738  value = int(self.layer.GetValue()))
739  UserSettings.Set(group = 'vdigit', key = "category", subkey = 'value',
740  value = int(self.category.GetValue()))
741  UserSettings.Set(group = 'vdigit', key = "categoryMode", subkey = 'selection',
742  value = self.categoryMode.GetSelection())
743 
744  # delete existing feature
745  UserSettings.Set(group = 'vdigit', key = "delRecord", subkey = 'enabled',
746  value = self.deleteRecord.IsChecked())
747 
748  # geometry attributes (workspace)
749  mapLayer = self.parent.toolbars['vdigit'].GetLayer()
750  tree = self.parent.tree
751  item = tree.FindItemByData('maplayer', mapLayer)
752  for key, val in self.geomAttrb.iteritems():
753  checked = self.FindWindowById(val['check']).IsChecked()
754  column = self.FindWindowById(val['column']).GetValue()
755  unitsIdx = self.FindWindowById(val['units']).GetSelection()
756  if item and not tree.GetPyData(item)[0]['vdigit']:
757  tree.GetPyData(item)[0]['vdigit'] = { 'geomAttr' : dict() }
758 
759  if checked: # enable
760  if key == 'area':
761  type = key
762  else:
763  type = 'length'
764  unitsKey = Units.GetUnitsKey(type, unitsIdx)
765  tree.GetPyData(item)[0]['vdigit']['geomAttr'][key] = { 'column' : column,
766  'units' : unitsKey }
767  else:
768  if item and tree.GetPyData(item)[0]['vdigit'] and \
769  key in tree.GetPyData(item)[0]['vdigit']['geomAttr']:
770  del tree.GetPyData(item)[0]['vdigit']['geomAttr'][key]
771 
772  # query tool
773  if self.queryLength.GetValue():
774  UserSettings.Set(group = 'vdigit', key = "query", subkey = 'selection',
775  value = 0)
776  else:
777  UserSettings.Set(group = 'vdigit', key = "query", subkey = 'type',
778  value = 1)
779  UserSettings.Set(group = 'vdigit', key = "query", subkey = 'box',
780  value = self.queryBox.IsChecked())
781  UserSettings.Set(group = 'vdigit', key = "queryLength", subkey = 'than-selection',
782  value = self.queryLengthSL.GetSelection())
783  UserSettings.Set(group = 'vdigit', key = "queryLength", subkey = 'thresh',
784  value = int(self.queryLengthValue.GetValue()))
785  UserSettings.Set(group = 'vdigit', key = "queryDangle", subkey = 'than-selection',
786  value = self.queryDangleSL.GetSelection())
787  UserSettings.Set(group = 'vdigit', key = "queryDangle", subkey = 'thresh',
788  value = int(self.queryDangleValue.GetValue()))
789 
790  # select features
791  for feature in ('point', 'line',
792  'centroid', 'boundary'):
793  UserSettings.Set(group = 'vdigit', key = 'selectType',
794  subkey = [feature, 'enabled'],
795  value = self.FindWindowById(self.selectFeature[feature]).IsChecked())
796  UserSettings.Set(group = 'vdigit', key = "selectThresh", subkey = 'value',
797  value = int(self.selectThreshValue.GetValue()))
798  UserSettings.Set(group = 'vdigit', key = "checkForDupl", subkey = 'enabled',
799  value = self.checkForDupl.IsChecked())
800  UserSettings.Set(group = 'vdigit', key = "selectInside", subkey = 'enabled',
801  value = self.selectIn.IsChecked())
802 
803  # on-exit
804  UserSettings.Set(group = 'vdigit', key = "saveOnExit", subkey = 'enabled',
805  value = self.save.IsChecked())
806 
807  # break lines
808  UserSettings.Set(group = 'vdigit', key = "breakLines", subkey = 'enabled',
809  value = self.intersect.IsChecked())
810 
811  self.digit.UpdateSettings()
812 
813  # redraw map if auto-rendering is enabled
814  if self.parent.statusbarWin['render'].GetValue():
815  self.parent.OnRender(None)
816 
817 class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
818  def __init__(self, parent, title,
819  vectorName, query = None, cats = None,
820  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
821  """!Dialog used to display/modify categories of vector objects
822 
823  @param parent
824  @param title dialog title
825  @param query {coordinates, qdist} - used by v.edit/v.what
826  @param cats directory of lines (layer/categories) - used by vdigit
827  @param style dialog style
828  """
829  self.parent = parent # mapdisplay.BufferedWindow class instance
830  self.digit = parent.digit
831 
832  # map name
833  self.vectorName = vectorName
834 
835  # line : {layer: [categories]}
836  self.cats = {}
837 
838  # do not display dialog if no line is found (-> self.cats)
839  if cats is None:
840  if self._getCategories(query[0], query[1]) == 0 or not self.line:
841  Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
842  else:
843  self.cats = cats
844  for line in cats.keys():
845  for layer in cats[line].keys():
846  self.cats[line][layer] = list(cats[line][layer])
847 
848  layers = []
849  for layer in self.digit.GetLayers():
850  layers.append(str(layer))
851 
852  # make copy of cats (used for 'reload')
853  self.cats_orig = copy.deepcopy(self.cats)
854 
855  wx.Dialog.__init__(self, parent = self.parent, id = wx.ID_ANY, title = title,
856  style = style, **kwargs)
857 
858  # list of categories
859  box = wx.StaticBox(parent = self, id = wx.ID_ANY,
860  label = " %s " % _("List of categories - right-click to delete"))
861  listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
862  self.list = CategoryListCtrl(parent = self, id = wx.ID_ANY,
863  style = wx.LC_REPORT |
864  wx.BORDER_NONE |
865  wx.LC_SORT_ASCENDING |
866  wx.LC_HRULES |
867  wx.LC_VRULES)
868  # sorter
869  self.fid = self.cats.keys()[0]
870  self.itemDataMap = self.list.Populate(self.cats[self.fid])
871  listmix.ColumnSorterMixin.__init__(self, 2)
872  self.fidMulti = wx.Choice(parent = self, id = wx.ID_ANY,
873  size = (150, -1))
874  self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
875  self.fidText = wx.StaticText(parent = self, id = wx.ID_ANY)
876  if len(self.cats.keys()) == 1:
877  self.fidMulti.Show(False)
878  self.fidText.SetLabel(str(self.fid))
879  else:
880  self.fidText.Show(False)
881  choices = []
882  for fid in self.cats.keys():
883  choices.append(str(fid))
884  self.fidMulti.SetItems(choices)
885  self.fidMulti.SetSelection(0)
886 
887  listSizer.Add(item = self.list, proportion = 1, flag = wx.EXPAND)
888 
889  # add new category
890  box = wx.StaticBox(parent = self, id = wx.ID_ANY,
891  label = " %s " % _("Add new category"))
892  addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
893  flexSizer = wx.FlexGridSizer (cols = 5, hgap = 5, vgap = 5)
894  flexSizer.AddGrowableCol(3)
895 
896  layerNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
897  label = "%s:" % _("Layer"))
898  self.layerNew = wx.Choice(parent = self, id = wx.ID_ANY, size = (75, -1),
899  choices = layers)
900  if len(layers) > 0:
901  self.layerNew.SetSelection(0)
902 
903  catNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
904  label = "%s:" % _("Category"))
905 
906  try:
907  newCat = max(self.cats[self.fid][1]) + 1
908  except KeyError:
909  newCat = 1
910  self.catNew = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (75, -1),
911  initial = newCat, min = 0, max = 1e9)
912  btnAddCat = wx.Button(self, wx.ID_ADD)
913  flexSizer.Add(item = layerNewTxt, proportion = 0,
914  flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
915  flexSizer.Add(item = self.layerNew, proportion = 0,
916  flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
917  flexSizer.Add(item = catNewTxt, proportion = 0,
918  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
919  border = 10)
920  flexSizer.Add(item = self.catNew, proportion = 0,
921  flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
922  flexSizer.Add(item = btnAddCat, proportion = 0,
923  flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
924  addSizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
925 
926  # buttons
927  btnApply = wx.Button(self, wx.ID_APPLY)
928  btnApply.SetToolTipString(_("Apply changes"))
929  btnCancel = wx.Button(self, wx.ID_CANCEL)
930  btnCancel.SetToolTipString(_("Ignore changes and close dialog"))
931  btnOk = wx.Button(self, wx.ID_OK)
932  btnOk.SetToolTipString(_("Apply changes and close dialog"))
933  btnOk.SetDefault()
934 
935  # sizers
936  btnSizer = wx.StdDialogButtonSizer()
937  btnSizer.AddButton(btnCancel)
938  #btnSizer.AddButton(btnReload)
939  #btnSizer.SetNegativeButton(btnReload)
940  btnSizer.AddButton(btnApply)
941  btnSizer.AddButton(btnOk)
942  btnSizer.Realize()
943 
944  mainSizer = wx.BoxSizer(wx.VERTICAL)
945  mainSizer.Add(item = listSizer, proportion = 1,
946  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
947  mainSizer.Add(item = addSizer, proportion = 0,
948  flag = wx.EXPAND | wx.ALIGN_CENTER |
949  wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
950  fidSizer = wx.BoxSizer(wx.HORIZONTAL)
951  fidSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
952  label = _("Feature id:")),
953  proportion = 0, border = 5,
954  flag = wx.ALIGN_CENTER_VERTICAL)
955  fidSizer.Add(item = self.fidMulti, proportion = 0,
956  flag = wx.EXPAND | wx.ALL, border = 5)
957  fidSizer.Add(item = self.fidText, proportion = 0,
958  flag = wx.EXPAND | wx.ALL, border = 5)
959  mainSizer.Add(item = fidSizer, proportion = 0,
960  flag = wx.EXPAND | wx.ALL, border = 5)
961  mainSizer.Add(item = btnSizer, proportion = 0,
962  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
963 
964  self.SetSizer(mainSizer)
965  mainSizer.Fit(self)
966  self.SetAutoLayout(True)
967 
968  # set min size for dialog
969  self.SetMinSize(self.GetBestSize())
970 
971  # bindings
972  btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
973  btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
974  btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
975  btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
976 
977  # list
978  self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
979  self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
980  self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
981  self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
982  self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
983 
984  def GetListCtrl(self):
985  """!Used by ColumnSorterMixin
986  """
987  return self.list
988 
989  def OnColClick(self, event):
990  """!Click on column header (order by)
991  """
992  event.Skip()
993 
994  def OnBeginEdit(self, event):
995  """!Editing of item started
996  """
997  event.Allow()
998 
999  def OnEndEdit(self, event):
1000  """!Finish editing of item
1001  """
1002  itemIndex = event.GetIndex()
1003  layerOld = int (self.list.GetItem(itemIndex, 0).GetText())
1004  catOld = int (self.list.GetItem(itemIndex, 1).GetText())
1005 
1006  if event.GetColumn() == 0:
1007  layerNew = int(event.GetLabel())
1008  catNew = catOld
1009  else:
1010  layerNew = layerOld
1011  catNew = int(event.GetLabel())
1012 
1013  try:
1014  if layerNew not in self.cats[self.fid].keys():
1015  self.cats[self.fid][layerNew] = []
1016  self.cats[self.fid][layerNew].append(catNew)
1017  self.cats[self.fid][layerOld].remove(catOld)
1018  except:
1019  event.Veto()
1020  self.list.SetStringItem(itemIndex, 0, str(layerNew))
1021  self.list.SetStringItem(itemIndex, 1, str(catNew))
1022  dlg = wx.MessageDialog(self, _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
1023  "Layer and category number must be integer.\n"
1024  "Layer number must be greater than zero.") %
1025  { 'layer': self.layerNew.GetStringSelection(),
1026  'category' : str(self.catNew.GetValue()) },
1027  _("Error"), wx.OK | wx.ICON_ERROR)
1028  dlg.ShowModal()
1029  dlg.Destroy()
1030  return False
1031 
1032  def OnRightDown(self, event):
1033  """!Mouse right button down
1034  """
1035  x = event.GetX()
1036  y = event.GetY()
1037  item, flags = self.list.HitTest((x, y))
1038 
1039  if item != wx.NOT_FOUND and \
1040  flags & wx.LIST_HITTEST_ONITEM:
1041  self.list.Select(item)
1042 
1043  event.Skip()
1044 
1045  def OnRightUp(self, event):
1046  """!Mouse right button up
1047  """
1048  if not hasattr(self, "popupID1"):
1049  self.popupID1 = wx.NewId()
1050  self.popupID2 = wx.NewId()
1051  self.popupID3 = wx.NewId()
1052  self.Bind(wx.EVT_MENU, self.OnItemDelete, id = self.popupID1)
1053  self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, id = self.popupID2)
1054  self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3)
1055 
1056  # generate popup-menu
1057  menu = wx.Menu()
1058  menu.Append(self.popupID1, _("Delete selected"))
1059  if self.list.GetFirstSelected() == -1:
1060  menu.Enable(self.popupID1, False)
1061 
1062  menu.Append(self.popupID2, _("Delete all"))
1063  menu.AppendSeparator()
1064  menu.Append(self.popupID3, _("Reload"))
1065 
1066  self.PopupMenu(menu)
1067  menu.Destroy()
1068 
1069  def OnItemSelected(self, event):
1070  """!Item selected
1071  """
1072  event.Skip()
1073 
1074  def OnItemDelete(self, event):
1075  """!Delete selected item(s) from the list (layer/category pair)
1076  """
1077  item = self.list.GetFirstSelected()
1078  while item != -1:
1079  layer = int (self.list.GetItem(item, 0).GetText())
1080  cat = int (self.list.GetItem(item, 1).GetText())
1081  self.list.DeleteItem(item)
1082  self.cats[self.fid][layer].remove(cat)
1083 
1084  item = self.list.GetFirstSelected()
1085 
1086  event.Skip()
1087 
1088  def OnItemDeleteAll(self, event):
1089  """!Delete all items from the list
1090  """
1091  self.list.DeleteAllItems()
1092  self.cats[self.fid] = {}
1093 
1094  event.Skip()
1095 
1096  def OnFeature(self, event):
1097  """!Feature id changed (on duplicates)
1098  """
1099  self.fid = int(event.GetString())
1100 
1101  self.itemDataMap = self.list.Populate(self.cats[self.fid],
1102  update = True)
1103 
1104  try:
1105  newCat = max(self.cats[self.fid][1]) + 1
1106  except KeyError:
1107  newCat = 1
1108 
1109  self.catNew.SetValue(newCat)
1110 
1111  event.Skip()
1112 
1113  def _getCategories(self, coords, qdist):
1114  """!Get layer/category pairs for all available
1115  layers
1116 
1117  Return True line found or False if not found
1118  """
1119  ret = gcmd.RunCommand('v.what',
1120  parent = self,
1121  quiet = True,
1122  map = self.vectorName,
1123  east_north = '%f,%f' % \
1124  (float(coords[0]), float(coords[1])),
1125  distance = qdist)
1126 
1127  if not ret:
1128  return False
1129 
1130  for item in ret.splitlines():
1131  litem = item.lower()
1132  if "id:" in litem: # get line id
1133  self.line = int(item.split(':')[1].strip())
1134  elif "layer:" in litem: # add layer
1135  layer = int(item.split(':')[1].strip())
1136  if layer not in self.cats.keys():
1137  self.cats[layer] = []
1138  elif "category:" in litem: # add category
1139  self.cats[layer].append(int(item.split(':')[1].strip()))
1140 
1141  return True
1142 
1143  def OnReload(self, event):
1144  """!Reload button pressed
1145  """
1146  # restore original list
1147  self.cats = copy.deepcopy(self.cats_orig)
1148 
1149  # polulate list
1150  self.itemDataMap = self.list.Populate(self.cats[self.fid],
1151  update = True)
1152 
1153  event.Skip()
1154 
1155  def OnCancel(self, event):
1156  """!Cancel button pressed
1157  """
1158  self.parent.parent.dialogs['category'] = None
1159  if self.digit:
1160  self.digit.GetDisplay().SetSelected([])
1161  self.parent.UpdateMap(render = False)
1162  else:
1163  self.parent.parent.OnRender(None)
1164 
1165  self.Close()
1166 
1167  def OnApply(self, event):
1168  """!Apply button pressed
1169  """
1170  for fid in self.cats.keys():
1171  newfid = self.ApplyChanges(fid)
1172  if fid == self.fid and newfid > 0:
1173  self.fid = newfid
1174 
1175  def ApplyChanges(self, fid):
1176  """!Apply changes
1177 
1178  @param fid feature id
1179  """
1180  cats = self.cats[fid]
1181  cats_orig = self.cats_orig[fid]
1182 
1183  # action : (catsFrom, catsTo)
1184  check = {'catadd': (cats, cats_orig),
1185  'catdel': (cats_orig, cats)}
1186 
1187  newfid = -1
1188 
1189  # add/delete new category
1190  for action, catsCurr in check.iteritems():
1191  for layer in catsCurr[0].keys():
1192  catList = []
1193  for cat in catsCurr[0][layer]:
1194  if layer not in catsCurr[1].keys() or \
1195  cat not in catsCurr[1][layer]:
1196  catList.append(cat)
1197  if catList != []:
1198  if action == 'catadd':
1199  add = True
1200  else:
1201  add = False
1202 
1203  newfid = self.digit.SetLineCats(fid, layer,
1204  catList, add)
1205  if len(self.cats.keys()) == 1:
1206  self.fidText.SetLabel("%d" % newfid)
1207  else:
1208  choices = self.fidMulti.GetItems()
1209  choices[choices.index(str(fid))] = str(newfid)
1210  self.fidMulti.SetItems(choices)
1211  self.fidMulti.SetStringSelection(str(newfid))
1212 
1213  self.cats[newfid] = self.cats[fid]
1214  del self.cats[fid]
1215 
1216  fid = newfid
1217  if self.fid < 0:
1218  wx.MessageBox(parent = self, message = _("Unable to update vector map."),
1219  caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
1220 
1221  self.cats_orig[fid] = copy.deepcopy(cats)
1222 
1223  return newfid
1224 
1225  def OnOK(self, event):
1226  """!OK button pressed
1227  """
1228  self.OnApply(event)
1229  self.OnCancel(event)
1230 
1231  def OnAddCat(self, event):
1232  """!Button 'Add' new category pressed
1233  """
1234  try:
1235  layer = int(self.layerNew.GetStringSelection())
1236  cat = int(self.catNew.GetValue())
1237  if layer <= 0:
1238  raise ValueError
1239  except ValueError:
1240  gcmd.GError(parent = self,
1241  message = _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
1242  "Layer and category number must be integer.\n"
1243  "Layer number must be greater then zero.") %
1244  {'layer' : str(self.layerNew.GetValue()),
1245  'category' : str(self.catNew.GetValue())})
1246  return False
1247 
1248  if layer not in self.cats[self.fid].keys():
1249  self.cats[self.fid][layer] = []
1250 
1251  self.cats[self.fid][layer].append(cat)
1252 
1253  # reload list
1254  self.itemDataMap = self.list.Populate(self.cats[self.fid],
1255  update = True)
1256 
1257  # update category number for add
1258  self.catNew.SetValue(cat + 1)
1259 
1260  event.Skip()
1261 
1262  return True
1263 
1264  def GetLine(self):
1265  """!Get id of selected line of 'None' if no line is selected
1266  """
1267  return self.cats.keys()
1268 
1269  def UpdateDialog(self, query = None, cats = None):
1270  """!Update dialog
1271 
1272  @param query {coordinates, distance} - v.what
1273  @param cats directory layer/cats - vdigit
1274  Return True if updated otherwise False
1275  """
1276  # line: {layer: [categories]}
1277  self.cats = {}
1278  # do not display dialog if no line is found (-> self.cats)
1279  if cats is None:
1280  ret = self._getCategories(query[0], query[1])
1281  else:
1282  self.cats = cats
1283  for line in cats.keys():
1284  for layer in cats[line].keys():
1285  self.cats[line][layer] = list(cats[line][layer])
1286  ret = 1
1287  if ret == 0 or len(self.cats.keys()) < 1:
1288  Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
1289  return False
1290 
1291  # make copy of cats (used for 'reload')
1292  self.cats_orig = copy.deepcopy(self.cats)
1293 
1294  # polulate list
1295  self.fid = self.cats.keys()[0]
1296  self.itemDataMap = self.list.Populate(self.cats[self.fid],
1297  update = True)
1298 
1299  try:
1300  newCat = max(self.cats[self.fid][1]) + 1
1301  except KeyError:
1302  newCat = 1
1303  self.catNew.SetValue(newCat)
1304 
1305  if len(self.cats.keys()) == 1:
1306  self.fidText.Show(True)
1307  self.fidMulti.Show(False)
1308  self.fidText.SetLabel("%d" % self.fid)
1309  else:
1310  self.fidText.Show(False)
1311  self.fidMulti.Show(True)
1312  choices = []
1313  for fid in self.cats.keys():
1314  choices.append(str(fid))
1315  self.fidMulti.SetItems(choices)
1316  self.fidMulti.SetSelection(0)
1317 
1318  self.Layout()
1319 
1320  return True
1321 
1322 class CategoryListCtrl(wx.ListCtrl,
1323  listmix.ListCtrlAutoWidthMixin,
1324  listmix.TextEditMixin):
1325  def __init__(self, parent, id, pos = wx.DefaultPosition,
1326  size = wx.DefaultSize, style = 0):
1327  """!List of layers/categories"""
1328  self.parent = parent
1329 
1330  wx.ListCtrl.__init__(self, parent, id, pos, size, style)
1331 
1332  listmix.ListCtrlAutoWidthMixin.__init__(self)
1333  listmix.TextEditMixin.__init__(self)
1334 
1335  def Populate(self, cats, update = False):
1336  """!Populate the list
1337  """
1338  itemData = {} # requested by sorter
1339 
1340  if not update:
1341  self.InsertColumn(0, _("Layer"))
1342  self.InsertColumn(1, _("Category"))
1343  else:
1344  self.DeleteAllItems()
1345 
1346  i = 1
1347  for layer in cats.keys():
1348  catsList = cats[layer]
1349  for cat in catsList:
1350  index = self.InsertStringItem(sys.maxint, str(catsList[0]))
1351  self.SetStringItem(index, 0, str(layer))
1352  self.SetStringItem(index, 1, str(cat))
1353  self.SetItemData(index, i)
1354  itemData[i] = (str(layer), str(cat))
1355  i = i + 1
1356 
1357  if not update:
1358  self.SetColumnWidth(0, 100)
1359  self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
1360 
1361  self.currentItem = 0
1362 
1363  return itemData
1364 
1365 class VDigitZBulkDialog(wx.Dialog):
1366  def __init__(self, parent, title, nselected, style = wx.DEFAULT_DIALOG_STYLE):
1367  """!Dialog used for Z bulk-labeling tool
1368  """
1369  wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
1370 
1371  self.parent = parent # mapdisplay.BufferedWindow class instance
1372 
1373  # panel = wx.Panel(parent=self, id=wx.ID_ANY)
1374 
1375  border = wx.BoxSizer(wx.VERTICAL)
1376 
1377  txt = wx.StaticText(parent = self,
1378  label = _("%d lines selected for z bulk-labeling") % nselected);
1379  border.Add(item = txt, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
1380 
1381  box = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Set value"))
1382  sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
1383  flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
1384  flexSizer.AddGrowableCol(0)
1385 
1386  # starting value
1387  txt = wx.StaticText(parent = self,
1388  label = _("Starting value"));
1389  self.value = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
1390  initial = 0,
1391  min = -1e6, max = 1e6)
1392  flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
1393  flexSizer.Add(self.value, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
1394 
1395  # step
1396  txt = wx.StaticText(parent = self,
1397  label = _("Step"))
1398  self.step = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
1399  initial = 0,
1400  min = 0, max = 1e6)
1401  flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
1402  flexSizer.Add(self.step, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
1403 
1404  sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
1405  border.Add(item = sizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 0)
1406 
1407  # buttons
1408  btnCancel = wx.Button(self, wx.ID_CANCEL)
1409  btnOk = wx.Button(self, wx.ID_OK)
1410  btnOk.SetDefault()
1411 
1412  # sizers
1413  btnSizer = wx.StdDialogButtonSizer()
1414  btnSizer.AddButton(btnCancel)
1415  btnSizer.AddButton(btnOk)
1416  btnSizer.Realize()
1417 
1418  mainSizer = wx.BoxSizer(wx.VERTICAL)
1419  mainSizer.Add(item = border, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
1420  mainSizer.Add(item = btnSizer, proportion = 0,
1421  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1422 
1423  self.SetSizer(mainSizer)
1424  mainSizer.Fit(self)
1425 
1426 class VDigitDuplicatesDialog(wx.Dialog):
1427  def __init__(self, parent, data, title = _("List of duplicates"),
1428  style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
1429  pos = wx.DefaultPosition):
1430  """!Show duplicated feature ids
1431  """
1432  wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style,
1433  pos = pos)
1434 
1435  self.parent = parent # BufferedWindow
1436  self.data = data
1437  self.winList = []
1438 
1439  # panel = wx.Panel(parent=self, id=wx.ID_ANY)
1440 
1441  # notebook
1442  self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
1443 
1444  id = 1
1445  for key in self.data.keys():
1446  panel = wx.Panel(parent = self.notebook, id = wx.ID_ANY)
1447  self.notebook.AddPage(page = panel, text = " %d " % (id))
1448 
1449  # notebook body
1450  border = wx.BoxSizer(wx.VERTICAL)
1451 
1452  win = CheckListFeature(parent = panel, data = list(self.data[key]))
1453  self.winList.append(win.GetId())
1454 
1455  border.Add(item = win, proportion = 1,
1456  flag = wx.ALL | wx.EXPAND, border = 5)
1457 
1458  panel.SetSizer(border)
1459 
1460  id += 1
1461 
1462  # buttons
1463  btnCancel = wx.Button(self, wx.ID_CANCEL)
1464  btnOk = wx.Button(self, wx.ID_OK)
1465  btnOk.SetDefault()
1466 
1467  # sizers
1468  btnSizer = wx.StdDialogButtonSizer()
1469  btnSizer.AddButton(btnCancel)
1470  btnSizer.AddButton(btnOk)
1471  btnSizer.Realize()
1472 
1473  mainSizer = wx.BoxSizer(wx.VERTICAL)
1474  mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
1475  mainSizer.Add(item = btnSizer, proportion = 0,
1476  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1477 
1478  self.SetSizer(mainSizer)
1479  mainSizer.Fit(self)
1480  self.SetAutoLayout(True)
1481 
1482  # set min size for dialog
1483  self.SetMinSize((250, 180))
1484 
1485  def GetUnSelected(self):
1486  """!Get unselected items (feature id)
1487 
1488  @return list of ids
1489  """
1490  ids = []
1491  for id in self.winList:
1492  wlist = self.FindWindowById(id)
1493 
1494  for item in range(wlist.GetItemCount()):
1495  if not wlist.IsChecked(item):
1496  ids.append(int(wlist.GetItem(item, 0).GetText()))
1497 
1498  return ids
1499 
1500 class CheckListFeature(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
1501  def __init__(self, parent, data,
1502  pos = wx.DefaultPosition, log = None):
1503  """!List of mapset/owner/group
1504  """
1505  self.parent = parent
1506  self.data = data
1507 
1508  wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
1509  style = wx.LC_REPORT)
1510 
1511  listmix.CheckListCtrlMixin.__init__(self)
1512 
1513  self.log = log
1514 
1515  # setup mixins
1516  listmix.ListCtrlAutoWidthMixin.__init__(self)
1517 
1518  self.LoadData(self.data)
1519 
1520  def LoadData(self, data):
1521  """!Load data into list
1522  """
1523  self.InsertColumn(0, _('Feature id'))
1524  self.InsertColumn(1, _('Layer (Categories)'))
1525 
1526  for item in data:
1527  index = self.InsertStringItem(sys.maxint, str(item[0]))
1528  self.SetStringItem(index, 1, str(item[1]))
1529 
1530  # enable all items by default
1531  for item in range(self.GetItemCount()):
1532  self.CheckItem(item, True)
1533 
1534  self.SetColumnWidth(col = 0, width = wx.LIST_AUTOSIZE_USEHEADER)
1535  self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE_USEHEADER)
1536 
1537  def OnCheckItem(self, index, flag):
1538  """!Mapset checked/unchecked
1539  """
1540  pass