/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/classgen.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/classgen.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 620 by jonathan, Mon Apr 7 10:14:25 2003 UTC revision 1274 by jonathan, Fri Jun 20 17:46:04 2003 UTC
# Line 5  Line 5 
5  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
6  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
7    
8    import sys
9    
10  from Thuban import _  from Thuban import _
11    
12  from wxPython.wx import *  from wxPython.wx import *
13    
14  from Thuban.Model.classification import Classification, ClassGroupRange, \  from Thuban.Model.classification import ClassGroupProperties
     ClassGroupSingleton, ClassGroupProperties  
15    
16  from Thuban.Model.table import Table, FIELDTYPE_INT, FIELDTYPE_DOUBLE, \  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
17       FIELDTYPE_STRING       FIELDTYPE_STRING
18    
19  from Thuban.Model.color import Color  from Thuban.Model.range import Range
20    from Thuban.UI.common import ThubanBeginBusyCursor, ThubanEndBusyCursor
21    
22    import classifier, resource
23    
24    from Thuban.Model.classgen import \
25        generate_uniform_distribution, generate_singletons, generate_quantiles, \
26        CustomRamp, GreyRamp, RedRamp, GreenRamp, BlueRamp, GreenToRedRamp, \
27        HotToColdRamp
28    
29    USEALL_BMP  = "group_use_all"
30    USE_BMP     = "group_use"
31    USENOT_BMP  = "group_use_not"
32    USENONE_BMP = "group_use_none"
33    
34    GENCOMBOSTR_UNIFORM = _("Uniform Distribution")
35    GENCOMBOSTR_UNIQUE = _("Unique Values")
36    GENCOMBOSTR_QUANTILES = _("Quantiles from Table")
37    
38    PROPCOMBOSTR_CUSTOM     = _("Custom Ramp")
39    PROPCOMBOSTR_GREY       = _("Grey Ramp")
40    PROPCOMBOSTR_RED        = _("Red Ramp")
41    PROPCOMBOSTR_GREEN      = _("Green Ramp")
42    PROPCOMBOSTR_BLUE       = _("Blue Ramp")
43    PROPCOMBOSTR_GREEN2RED  = _("Green-to-Red Ramp")
44    PROPCOMBOSTR_HOT2COLD   = _("Hot-to-Cold Ramp")
45    
46  ID_CLASSGEN_GEN = 4001  ID_CLASSGEN_GENCOMBO = 4007
47  ID_CLASSGEN_CANCEL = 4002  ID_CLASSGEN_PROPCOMBO = 4008
48    
49  class ClassGenDialog(wxDialog):  class ClassGenDialog(wxDialog):
50                                                                                    
51      def __init__(self, parent, table, fieldName):      def __init__(self, parent, layer, fieldName):
52            """Inialize the class generating dialog.
53    
54            parent -- this must be an instance of the Classifier class
55            """
56    
57          wxDialog.__init__(self, parent, -1, _("Generate Classification"),          wxDialog.__init__(self, parent, -1, _("Generate Classification"),
58                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)                            style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
59                                                                                    
60            self.parent = parent
61            self.layer = layer
62          self.clazz = None          self.clazz = None
63    
64          type, name, width, prec = table.field_info_by_name(fieldName)          col = layer.ShapeStore().Table().Column(fieldName)
65            self.type = col.type
66    
67            self.fieldName = fieldName
68            self.fieldType = self.type
69    
70            self.curGenPanel = None
71    
72            self.genpanels = []
73    
74            #############
75            # we need to create genButton first because when we create the
76            # panels they will call AllowGenerate() which uses genButton.
77            #
78            self.genButton = wxButton(self, wxID_OK, _("Generate"))
79            self.genButton.SetDefault()
80            self.cancelButton = wxButton(self, wxID_CANCEL, _("Close"))
81    
82            self.genChoice = wxChoice(self, ID_CLASSGEN_GENCOMBO)
83    
84            self.genpanels.append((GENCOMBOSTR_UNIQUE, GenUniquePanel))
85            if self.type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):
86                self.genpanels.append((GENCOMBOSTR_UNIFORM, GenUniformPanel))
87                self.genpanels.append((GENCOMBOSTR_QUANTILES, GenQuantilesPanel))
88    
89            for name, clazz in self.genpanels:
90                self.genChoice.Append(name, [clazz, None])
91    
92            self.genChoice.SetSelection(0)
93    
94            for i in range(self.genChoice.GetCount()):
95                clazz, obj = self.genChoice.GetClientData(i)
96    
97                if obj is None:
98                    obj = clazz(self, self.layer, self.fieldName, self.fieldType)
99                    obj.Hide()
100                    self.genChoice.SetClientData(i, [clazz, obj])
101    
102    
103            #############
104    
105          sizer = wxBoxSizer(wxVERTICAL)          sizer = wxBoxSizer(wxVERTICAL)
106    
107          buttonSizer = wxBoxSizer(wxHORIZONTAL)          sizer.Add(wxStaticText(self, -1, _("Field: %s") % fieldName),
108          self.genButton = wxButton(self, ID_CLASSGEN_GEN, _("Generate"))                    0, wxALL, 4)
109          buttonSizer.Add(self.genButton, 0, wxALL, 4)          sizer.Add(wxStaticText(
110          buttonSizer.Add(60, 20, 0, wxALL, 4)              self, -1,
111          buttonSizer.Add(wxButton(self, ID_CLASSGEN_CANCEL, _("Cancel")),              _("Data Type: %s") % classifier.Classifier.type2string[self.type]),
112                          0, wxALL, 4)              0, wxALL, 4)
113    
114          if type in (FIELDTYPE_INT, FIELDTYPE_DOUBLE):          psizer = wxBoxSizer(wxHORIZONTAL)
115              self.panel = GenRangePanel(self, table, fieldName, type)          psizer.Add(wxStaticText(self, -1, _("Generate:")),
116          elif type == FIELDTYPE_STRING:              0, wxALIGN_CENTER_VERTICAL, 0)
117              print "Select a field that is an int/decimal"          psizer.Add(self.genChoice, 1, wxALL | wxGROW, 4)
118              #panel = GenSingletonPanel(self, table, fieldName)  
119          else:          sizer.Add(psizer, 0, wxALL | wxGROW, 4)
120              assert False, "Shouldn't be here."  
121              pass          self.sizer_genPanel = wxBoxSizer(wxVERTICAL)
122            sizer.Add(self.sizer_genPanel, 1, wxGROW | wxALL, 4)
123    
124            psizer = wxBoxSizer(wxHORIZONTAL)
125            psizer.Add(wxStaticText(self, -1, _("Color Scheme:")),
126                0, wxALIGN_CENTER_VERTICAL, 0)
127    
128            # Properties (Ramp) ComboBox
129            self.propCombo = wxChoice(self, ID_CLASSGEN_PROPCOMBO)
130    
131            self.propPanel = None
132            custom_ramp_panel = CustomRampPanel(self, layer.ShapeType())
133    
134            self.propCombo.Append(PROPCOMBOSTR_GREY,  GreyRamp())
135            self.propCombo.Append(PROPCOMBOSTR_RED,   RedRamp())
136            self.propCombo.Append(PROPCOMBOSTR_GREEN, GreenRamp())
137            self.propCombo.Append(PROPCOMBOSTR_BLUE,  BlueRamp())
138            self.propCombo.Append(PROPCOMBOSTR_GREEN2RED, GreenToRedRamp())
139            self.propCombo.Append(PROPCOMBOSTR_HOT2COLD,  HotToColdRamp())
140            self.propCombo.Append(PROPCOMBOSTR_CUSTOM, custom_ramp_panel)
141    
142            self.propCombo.SetSelection(0)
143    
144            psizer.Add(self.propCombo, 1, wxALL | wxGROW, 4)
145            sizer.Add(psizer, 0, wxALL | wxGROW, 4)
146    
147          sizer.Add(self.panel, 1, wxGROW | wxALL, 4)          sizer.Add(custom_ramp_panel, 1, wxGROW | wxALL, 4)
148          sizer.Add(buttonSizer, 0,          sizer.Show(custom_ramp_panel, False)
149                    wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL, 4)  
150            # Finally place the main buttons
151            buttonSizer = wxBoxSizer(wxHORIZONTAL)
152            buttonSizer.Add(self.genButton, 0, wxRIGHT|wxEXPAND, 10)
153            buttonSizer.Add(self.cancelButton, 0, wxRIGHT|wxEXPAND, 10)
154            sizer.Add(buttonSizer, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
155    
156          self.SetSizer(sizer)          self.SetSizer(sizer)
157          self.SetAutoLayout(True)          self.SetAutoLayout(True)
158          sizer.SetSizeHints(self)          sizer.SetSizeHints(self)
159    
160          EVT_BUTTON(self, ID_CLASSGEN_GEN, self._OnGenerate)          self.topBox = sizer
161          EVT_BUTTON(self, ID_CLASSGEN_CANCEL, self._OnCancel)  
162            self.__DoOnGenTypeSelect()
163    
164            EVT_CHOICE(self, ID_CLASSGEN_GENCOMBO, self._OnGenTypeSelect)
165            EVT_CHOICE(self, ID_CLASSGEN_PROPCOMBO, self._OnPropTypeSelect)
166            EVT_BUTTON(self, wxID_OK, self.OnOK)
167            EVT_BUTTON(self, wxID_CANCEL, self.OnCancel)
168    
169            self.__DoOnGenTypeSelect()
170    
171            self.genChoice.SetFocus()
172    
173      def GetClassification(self):      def GetClassification(self):
174          return self.clazz          return self.clazz
175    
176      def AllowGenerate(self, on):      def AllowGenerate(self, on):
177          self.genButton.Enable(on)          pass #self.genButton.Enable(on)
178    
179      def _OnGenerate(self, event):      def OnOK(self, event):
180          min = self.panel.GetMin()          """This is really the generate button, but we want to override
181          max = self.panel.GetMax()          the wxDialog class.
182          numGroups = self.panel.GetNumGroups()          """
         cgp = ClassGroupProperties()  
         cgp2 = ClassGroupProperties()  
                                                                                   
         cgp.SetLineColor(Color(.5, 0, 0))  
         cgp2.SetLineColor(Color(1, 0, 1))  
         cgp2.SetLineWidth(10)  
183    
184          if min is not None \          index = self.genChoice.GetSelection()
185              and max is not None \  
186              and numGroups is not None:          assert index != -1, "button should be disabled!"
187              self.clazz = ClassGenerator().GenerateRanges(min, max,  
188                                                           numGroups, cgp, cgp2)          genSel = self.genChoice.GetString(index)
189          else:            clazz, genPanel = self.genChoice.GetClientData(index)
190              self.clazz = None # for now  
191            propPanel = self.propPanel
192          self.EndModal(wxID_OK)  
193                                                                                            if genSel in (GENCOMBOSTR_UNIFORM,          \
194      def _OnCancel(self, event):                        GENCOMBOSTR_UNIQUE,           \
195          self.EndModal(wxID_CANCEL)                        GENCOMBOSTR_QUANTILES):
196    
197  ID_RANGE_MIN = 4001              numGroups = genPanel.GetNumGroups()
198  ID_RANGE_MAX = 4002  
199  ID_RANGE_NGROUPS = 4003              index = self.propCombo.GetSelection()
200  ID_RANGE_STEP = 4004  
201                propSel = self.propCombo.GetString(index)
202                propPanel = self.propCombo.GetClientData(index)
203    
204                ramp = propPanel.GetRamp()
205    
206                if genSel == GENCOMBOSTR_UNIFORM:
207    
208                    min = genPanel.GetMin()
209                    max = genPanel.GetMax()
210    
211  class GenRangePanel(wxPanel):                  if min is not None \
212                        and max is not None \
213                        and numGroups is not None:
214    
215      def __init__(self, parent, table, fieldName, fieldType):                      self.clazz = generate_uniform_distribution(
216                                    min, max, numGroups, ramp,
217                                    self.type == FIELDTYPE_INT)
218    
219                        self.parent._SetClassification(self.clazz)
220    
221                elif genSel == GENCOMBOSTR_UNIQUE:
222    
223                    list = genPanel.GetValueList()
224    
225                    if len(list) > 0 \
226                        and numGroups is not None:
227    
228                        self.clazz = generate_singletons(
229                                        list, numGroups, ramp)
230    
231                        self.parent._SetClassification(self.clazz)
232    
233                elif genSel == GENCOMBOSTR_QUANTILES:
234    
235                    _range = genPanel.GetRange()
236                    _list = genPanel.GetList()
237                    _list.sort()
238    
239                    delta = 1 / float(numGroups)
240                    percents = [delta * i for i in range(1, numGroups + 1)]
241                    adjusted, self.clazz = \
242                        generate_quantiles(_list, percents, ramp, _range)
243    
244                    if adjusted:
245                        dlg = wxMessageDialog(self,
246                            _("Based on the data from the table and the input\n" +
247                              "values, the exact quantiles could not be generated.\n\n" +
248                              "Accept a close estimate?"),
249                            _("Problem with Quantiles"),
250    
251                            wxYES_NO|wxYES_DEFAULT|wxICON_QUESTION)
252                        if dlg.ShowModal() == wxID_YES:
253                            self.parent._SetClassification(self.clazz)
254                    else:
255                        self.parent._SetClassification(self.clazz)
256    
257        def OnCancel(self, event):
258            self.Close()
259    
260        def _OnGenTypeSelect(self, event):
261            self.__DoOnGenTypeSelect()
262            return
263    
264            combo = event.GetEventObject()
265    
266            selIndex = combo.GetSelection()
267    
268            if self.genPanel is not None:
269                self.topBox.Show(self.genPanel, False)
270    
271            self.genPanel = combo.GetClientData(selIndex)
272            if self.genPanel is not None:
273                self.topBox.Show(self.genPanel, True)
274    
275            self.topBox.SetSizeHints(self)
276            self.topBox.Layout()
277    
278        def _OnPropTypeSelect(self, event):
279            combo = event.GetEventObject()
280    
281            selIndex = combo.GetSelection()
282            sel = combo.GetString(selIndex)
283    
284            if isinstance(self.propPanel, wxPanel):
285                self.topBox.Show(self.propPanel, False)
286    
287            self.propPanel = combo.GetClientData(selIndex)
288    
289            if isinstance(self.propPanel, wxPanel):
290                self.topBox.Show(self.propPanel, True)
291    
292            self.topBox.SetSizeHints(self)
293            self.topBox.Layout()
294    
295        def __DoOnGenTypeSelect(self):
296            choice = self.genChoice
297    
298            sel = choice.GetSelection()
299            if sel == -1: return
300    
301            clazz, obj = choice.GetClientData(sel)
302    
303            if self.curGenPanel is not None:
304                self.curGenPanel.Hide()
305                self.sizer_genPanel.Remove(self.curGenPanel)
306    
307            self.curGenPanel = obj
308            self.curGenPanel.Show()
309    
310            self.sizer_genPanel.Add(self.curGenPanel, 1,
311                wxALL|wxEXPAND|wxADJUST_MINSIZE, 3)
312            self.sizer_genPanel.Layout()
313            self.Layout()
314            self.topBox.SetSizeHints(self)
315    
316    ID_UNIFORM_MIN = 4001
317    ID_UNIFORM_MAX = 4002
318    ID_UNIFORM_NGROUPS = 4003
319    ID_UNIFORM_STEP = 4004
320    ID_UNIFORM_RETRIEVE = 4005
321    
322    class GenUniformPanel(wxPanel):
323    
324        def __init__(self, parent, layer, fieldName, fieldType):
325          wxPanel.__init__(self, parent, -1)          wxPanel.__init__(self, parent, -1)
326    
327          self.parent = parent          self.parent = parent
328            self.layer = layer
329            self.fieldName = fieldName
330          self.fieldType = fieldType          self.fieldType = fieldType
331    
332          topSizer = wxStaticBoxSizer(wxStaticBox(self, -1, "Ranges"),          topSizer = wxStaticBoxSizer(wxStaticBox(self, -1, ""),
333                                      wxVERTICAL)                                      wxVERTICAL)
334    
335            #############
336    
337          sizer = wxBoxSizer(wxHORIZONTAL)          sizer = wxBoxSizer(wxHORIZONTAL)
338    
339          sizer.Add(wxStaticText(self, -1, _("Min:")), 0, wxALL, 4)          sizer.Add(wxStaticText(self, -1, _("Min:")), 0, wxALL, 4)
340          self.minCtrl = wxTextCtrl(self, ID_RANGE_MIN, style=wxTE_RIGHT)          self.minCtrl = wxTextCtrl(self, ID_UNIFORM_MIN, style=wxTE_RIGHT)
341          sizer.Add(self.minCtrl, 0, wxALL, 4)          sizer.Add(self.minCtrl, 1, wxALL, 4)
342          EVT_TEXT(self, ID_RANGE_MIN, self._OnRangeChanged)          EVT_TEXT(self, ID_UNIFORM_MIN, self._OnRangeChanged)
         self.goodTextColour = self.minCtrl.GetForegroundColour()  
         self.badTextColour = wxRED  
343    
344          sizer.Add(wxStaticText(self, -1, _("Max:")), 0, wxALL, 4)          sizer.Add(wxStaticText(self, -1, _("Max:")), 0, wxALL, 4)
345          self.maxCtrl = wxTextCtrl(self, ID_RANGE_MAX, style=wxTE_RIGHT)          self.maxCtrl = wxTextCtrl(self, ID_UNIFORM_MAX, style=wxTE_RIGHT)
346          sizer.Add(self.maxCtrl, 0, wxALL, 4)          sizer.Add(self.maxCtrl, 1, wxALL, 4)
347          EVT_TEXT(self, ID_RANGE_MAX, self._OnRangeChanged)          EVT_TEXT(self, ID_UNIFORM_MAX, self._OnRangeChanged)
348          topSizer.Add(sizer, 0, wxALL, 4)  
349            sizer.Add(wxButton(self, ID_UNIFORM_RETRIEVE, _("Retrieve From Table")),
350                0, wxALL, 4)
351            EVT_BUTTON(self, ID_UNIFORM_RETRIEVE, self._OnRetrieve)
352    
353            topSizer.Add(sizer, 1, wxGROW, 0)
354    
355            #############
356    
357          sizer = wxBoxSizer(wxHORIZONTAL)          sizer = wxBoxSizer(wxHORIZONTAL)
358    
359          sizer.Add(wxStaticText(self, -1, _("Number of Groups:")), 0, wxALL, 4)          sizer.Add(wxStaticText(self, -1, _("Number of Groups:")), 0, wxALL, 4)
360          self.numGroupsCtrl = wxSpinCtrl(self, ID_RANGE_NGROUPS, style=wxTE_RIGHT)          self.numGroupsCtrl = wxSpinCtrl(self, ID_UNIFORM_NGROUPS,
361          EVT_TEXT(self, ID_RANGE_NGROUPS, self._OnNumGroupsChanged)                                          style=wxTE_RIGHT)
362          EVT_SPINCTRL(self, ID_RANGE_NGROUPS, self._OnNumGroupsChanged)          EVT_TEXT(self, ID_UNIFORM_NGROUPS, self._OnNumGroupsChanged)
363          sizer.Add(self.numGroupsCtrl, 0, wxALL, 4)          EVT_SPINCTRL(self, ID_UNIFORM_NGROUPS, self._OnNumGroupsChanged)
364            sizer.Add(self.numGroupsCtrl, 1, wxALL, 4)
365    
366          sizer.Add(wxStaticText(self, -1, _("Stepping:")), 0, wxALL, 4)          sizer.Add(wxStaticText(self, -1, _("Stepping:")), 0, wxALL, 4)
367          self.stepCtrl = wxTextCtrl(self, ID_RANGE_STEP, style=wxTE_RIGHT)          self.stepCtrl = wxTextCtrl(self, ID_UNIFORM_STEP, style=wxTE_RIGHT)
368          EVT_TEXT(self, ID_RANGE_STEP, self._OnSteppingChanged)          EVT_TEXT(self, ID_UNIFORM_STEP, self._OnSteppingChanged)
369          sizer.Add(self.stepCtrl , 0, wxALL, 4)          sizer.Add(self.stepCtrl , 1, wxALL, 4)
370          topSizer.Add(sizer, 0, wxALL, 4)  
371            topSizer.Add(sizer, 1, wxGROW, 0)
372    
373            #############
374    
375          self.SetSizer(topSizer)          self.SetSizer(topSizer)
376          self.SetAutoLayout(True)          self.SetAutoLayout(True)
# Line 141  class GenRangePanel(wxPanel): Line 379  class GenRangePanel(wxPanel):
379          self.numGroupsChanging = False          self.numGroupsChanging = False
380          self.steppingChanging = False          self.steppingChanging = False
381    
382          self.numGroupsCtrl.SetRange(1, 100)          self.numGroupsCtrl.SetRange(1, sys.maxint)
383    
384          self.numGroupsCtrl.SetValue(1)          self.numGroupsCtrl.SetValue(1)
385          self.stepCtrl.SetValue("1")          self.stepCtrl.SetValue("1")
# Line 156  class GenRangePanel(wxPanel): Line 394  class GenRangePanel(wxPanel):
394                                              FIELDTYPE_INT,                                              FIELDTYPE_INT,
395                                              None)                                              None)
396    
         ##if self.__ValidateEntry(self.numGroupsCtrl, value, int):  
             #return value  
   
         #return None  
   
397      def GetStepping(self):      def GetStepping(self):
398          step = self.stepCtrl.GetValue()          step = self.stepCtrl.GetValue()
399          return self.__GetValidatedTypeEntry(self.stepCtrl,          return self.__GetValidatedTypeEntry(self.stepCtrl,
# Line 196  class GenRangePanel(wxPanel): Line 429  class GenRangePanel(wxPanel):
429          self.numGroupsCtrl.Enable(on)          self.numGroupsCtrl.Enable(on)
430          self.stepCtrl.Enable(on)          self.stepCtrl.Enable(on)
431    
         if on:  
             self.numGroupsCtrl.SetRange(1, abs(max - min) / 0.001)  
   
432          ngroups = self.GetNumGroups()          ngroups = self.GetNumGroups()
433    
434          if ngroups is not None  \          if ngroups is not None  \
# Line 206  class GenRangePanel(wxPanel): Line 436  class GenRangePanel(wxPanel):
436              and max is not None \              and max is not None \
437              and ngroups != 0:              and ngroups != 0:
438    
439              self.stepCtrl.SetValue(str((max - min) / ngroups))              #self.stepCtrl.SetValue(str((max - min) / ngroups))
440                self.stepCtrl.SetValue(str(self.__CalcStepping(min, max, ngroups)))
441                #self.numGroupsCtrl.SetValue(ngroups)
442    
443              self.parent.AllowGenerate(self.GetStepping() is not None)              self.parent.AllowGenerate(self.GetStepping() is not None)
444          else:          else:
# Line 227  class GenRangePanel(wxPanel): Line 459  class GenRangePanel(wxPanel):
459          min = self.GetMin()          min = self.GetMin()
460          max = self.GetMax()          max = self.GetMax()
461    
         if ngroups >= self.numGroupsCtrl.GetMax():  
             self.numGroupsCtrl.SetRange(1, ngroups + 1)  
   
462          if ngroups is not None  \          if ngroups is not None  \
463              and min is not None \              and min is not None \
464              and max is not None \              and max is not None \
# Line 243  class GenRangePanel(wxPanel): Line 472  class GenRangePanel(wxPanel):
472              # called steppingChanging tries to prevent the recursion.              # called steppingChanging tries to prevent the recursion.
473              #              #
474              self.numGroupsChanging = True              self.numGroupsChanging = True
475              self.stepCtrl.SetValue(str((max - min) / ngroups))  
476                self.stepCtrl.SetValue(str(self.__CalcStepping(min, max, ngroups)))
477    
478              self.parent.AllowGenerate(self.GetStepping() is not None)              self.parent.AllowGenerate(self.GetStepping() is not None)
479          else:          else:
# Line 268  class GenRangePanel(wxPanel): Line 498  class GenRangePanel(wxPanel):
498              # see note in _OnNumGroupsChanged              # see note in _OnNumGroupsChanged
499              #              #
500              self.steppingChanging = True              self.steppingChanging = True
501              n = int((max - min) / step)              self.numGroupsCtrl.SetValue(self.__CalcNumGroups(min, max, step))
             if n == 0:  
                 n = 1  
   
             self.numGroupsCtrl.SetValue(n)  
502    
503              self.parent.AllowGenerate(self.GetNumGroups() is not None)              self.parent.AllowGenerate(self.GetNumGroups() is not None)
504          else:          else:
505              self.parent.AllowGenerate(False)              self.parent.AllowGenerate(False)
506    
507        def _OnRetrieve(self, event):
508            table = self.layer.ShapeStore().Table()
509            if table is not None:
510                ThubanBeginBusyCursor()
511                try:
512                    min, max = table.ValueRange(self.fieldName)
513                    self.minCtrl.SetValue(str(min))
514                    self.maxCtrl.SetValue(str(max))
515                finally:
516                    ThubanEndBusyCursor()
517    
518      def __GetValidatedTypeEntry(self, win, value, type, badValue = None):      def __GetValidatedTypeEntry(self, win, value, type, badValue = None):
519    
520          if type == FIELDTYPE_INT:          if type == FIELDTYPE_INT:
# Line 316  class GenRangePanel(wxPanel): Line 553  class GenRangePanel(wxPanel):
553          win.Refresh()          win.Refresh()
554    
555          return valid          return valid
           
556    
557  class GenSingletonPanel(wxPanel):      def __CalcStepping(self, min, max, ngroups):
558            if self.fieldType == FIELDTYPE_INT:
559                step = int((max - min + 1) / float(ngroups))
560            else:
561                step = (max - min) / float(ngroups)
562    
563            return step
564    
565        def __CalcNumGroups(self, min, max, step):
566            n = int((max - min) / step)
567            if n == 0:
568                n = 1
569    
570            if self.fieldType == FIELDTYPE_INT and step == 1:
571                n += 1
572    
573            return n
574    
575      def __init__(self, parent, table, fieldName, fieldType):  
576    ID_UNIQUE_RETRIEVE = 4001
577    ID_UNIQUE_USEALL = 4002
578    ID_UNIQUE_USE = 4003
579    ID_UNIQUE_DONTUSE = 4004
580    ID_UNIQUE_USENONE = 4005
581    ID_UNIQUE_SORTAVAIL = 4006
582    ID_UNIQUE_SORTUSE = 4007
583    ID_UNIQUE_REVAVAIL = 4008
584    ID_UNIQUE_REVUSE = 4009
585    
586    class GenUniquePanel(wxPanel):
587    
588        def __init__(self, parent, layer, fieldName, fieldType):
589          wxPanel.__init__(self, parent, -1)          wxPanel.__init__(self, parent, -1)
590    
591            self.parent = parent
592            self.layer = layer
593            self.fieldName = fieldName
594            self.fieldType = fieldType
595    
596  class ClassGenerator:          topSizer = wxStaticBoxSizer(wxStaticBox(self, -1, ""),
597                                        wxVERTICAL)
598    
     def GenerateSingletons(self, list, numGroups, prop1, prop2):  
         """Generate a new classification consisting solely of singletons.  
599    
600          The resulting classification will consist of at most 'numGroups'          #bsizer = wxBoxSizer(wxVERTICAL)
601          groups whose group properties ramp between 'prop1' and 'prop2'. There          topSizer.Add(wxButton(self, ID_UNIQUE_RETRIEVE,
602          could be fewer groups if 'list' contains fewer that 'numGroups' items.                              _("Retrieve From Table")),
603                       0, wxALL | wxALIGN_RIGHT, 4)
604    
605          list -- any object that implements the iterator interface          EVT_BUTTON(self, ID_UNIQUE_RETRIEVE, self._OnRetrieve)
606    
607          numGroups -- how many groups to generate. This can not be          #topSizer.Add(bsizer, 0, wxALL, 4)
                      determined while the classification is being  
                      generated because the stepping values must  
                      be precalculated to ramp between prop1 and prop2.  
608    
609          prop1 -- initial group property values          sizer = wxBoxSizer(wxHORIZONTAL)
610    
611          prop2 -- final group property values          self.dataList = []
         """  
612    
613          clazz = Classification()          psizer = wxBoxSizer(wxVERTICAL)
614          if numGroups == 0: return clazz          self.list_avail = wxListCtrl(self, -1,
615                            style=wxLC_REPORT | wxLC_SINGLE_SEL)
616            self.list_avail.InsertColumn(0, "Available")
617            self.list_avail_data = []
618            psizer.Add(self.list_avail, 1, wxGROW, 0)
619    
620            bsizer = wxBoxSizer(wxHORIZONTAL)
621            bsizer.Add(wxButton(self, ID_UNIQUE_SORTAVAIL, _("Sort")))
622            EVT_BUTTON(self, ID_UNIQUE_SORTAVAIL, self._OnSortList)
623    
624          for value, prop in zip(list, PropertyRamp(numGroups, prop1, prop2)):          bsizer.Add(wxButton(self, ID_UNIQUE_REVAVAIL, _("Reverse")))
625              clazz.AppendGroup(ClassGroupSingleton(value, prop))          EVT_BUTTON(self, ID_UNIQUE_REVAVAIL, self._OnReverseList)
626    
627          return clazz          psizer.Add(bsizer, 0, wxGROW, 0)
628            sizer.Add(psizer, 1, wxGROW, 0)
629    
630      def GenerateRanges(self, min, max, numGroups, prop1, prop2):          
631            bsizer = wxBoxSizer(wxVERTICAL)
632    
633          clazz = Classification()          bmp = resource.GetBitmapResource(USEALL_BMP, wxBITMAP_TYPE_XPM)
634          if numGroups == 0: return clazz          bsizer.Add(wxBitmapButton(self, ID_UNIQUE_USEALL, bmp),
635                       0, wxGROW | wxALL, 4)
636            bmp = resource.GetBitmapResource(USE_BMP, wxBITMAP_TYPE_XPM)
637            bsizer.Add(wxBitmapButton(self, ID_UNIQUE_USE, bmp),
638                       0, wxGROW | wxALL, 4)
639            bmp = resource.GetBitmapResource(USENOT_BMP, wxBITMAP_TYPE_XPM)
640            bsizer.Add(wxBitmapButton(self, ID_UNIQUE_DONTUSE, bmp),
641                       0, wxGROW | wxALL, 4)
642            bmp = resource.GetBitmapResource(USENONE_BMP, wxBITMAP_TYPE_XPM)
643            bsizer.Add(wxBitmapButton(self, ID_UNIQUE_USENONE, bmp),
644                       0, wxGROW | wxALL, 4)
645    
646            EVT_BUTTON(self, ID_UNIQUE_USEALL, self._OnUseAll)
647            EVT_BUTTON(self, ID_UNIQUE_USE, self._OnUse)
648            EVT_BUTTON(self, ID_UNIQUE_DONTUSE, self._OnDontUse)
649            EVT_BUTTON(self, ID_UNIQUE_USENONE, self._OnUseNone)
650    
651            sizer.Add(bsizer, 0, wxALL | wxALIGN_CENTER_VERTICAL, 4)
652    
653            psizer = wxBoxSizer(wxVERTICAL)
654            self.list_use = wxListCtrl(self, -1,
655                            style=wxLC_REPORT | wxLC_SINGLE_SEL)
656            self.list_use.InsertColumn(0, "Use")
657            self.list_use_data = []
658            psizer.Add(self.list_use, 1, wxGROW, 0)
659    
660            bsizer = wxBoxSizer(wxHORIZONTAL)
661            bsizer.Add(wxButton(self, ID_UNIQUE_SORTUSE, _("Sort")))
662            EVT_BUTTON(self, ID_UNIQUE_SORTUSE, self._OnSortList)
663    
664            bsizer.Add(wxButton(self, ID_UNIQUE_REVUSE, _("Reverse")))
665            EVT_BUTTON(self, ID_UNIQUE_REVUSE, self._OnReverseList)
666    
667          step = (max - min) / float(numGroups)          psizer.Add(bsizer, 0, wxGROW, 0)
668    
669          cur_min = min          sizer.Add(psizer, 1, wxGROW, 0)
         cur_max = cur_min + step  
670    
         i = 0  
         for prop in PropertyRamp(numGroups, prop1, prop2):  
671    
672              if i == (numGroups - 1):          topSizer.Add(sizer, 1, wxGROW, 0)
                 cur_max = max  
673    
674              # this check guards against rounding issues          self.SetSizer(topSizer)
675              if cur_min != cur_max:          self.SetAutoLayout(True)
676                  clazz.AppendGroup(ClassGroupRange(cur_min, cur_max, prop))          topSizer.SetSizeHints(self)
677    
678              cur_min = cur_max          width, height = self.list_avail.GetSizeTuple()
679              cur_max += step          self.list_avail.SetColumnWidth(0,width)
680              i += 1          width, height = self.list_use.GetSizeTuple()
681            self.list_use.SetColumnWidth(0,width)
682    
683          return clazz          self.parent.AllowGenerate(False)
684    
685  class PropertyRamp:      def GetNumGroups(self):
686            return self.list_use.GetItemCount()
687    
688      def __init__(self, num, prop1, prop2):      def GetValueList(self):
689            list = []
690            for i in range(self.list_use.GetItemCount()):
691                list.append(self.dataList[self.list_use.GetItemData(i)])
692            return list
693    
694          self.count = int(num)      def _OnSortList(self, event):
695          num = float(num)          id = event.GetId()
696    
697            if id == ID_UNIQUE_SORTUSE:
698                list = self.list_use
699            else:
700                list = self.list_avail
701    
702          self.lineColor = prop1.GetLineColor()          list.SortItems(lambda i1, i2: cmp(self.dataList[i1],
703          self.fillColor = prop1.GetFill()                                            self.dataList[i2]))
         self.lineWidth = prop1.GetLineWidth()  
704    
705          lineColor2 = prop2.GetLineColor()      def _OnReverseList(self, event):
706          fillColor2 = prop2.GetFill()          id = event.GetId()
         lineWidth2 = prop2.GetLineWidth()  
707    
708          self.line_redStep   = (lineColor2.red   - self.lineColor.red)   / num          if id == ID_UNIQUE_REVUSE:
709          self.line_greenStep = (lineColor2.green - self.lineColor.green) / num              list = self.list_use
710          self.line_blueStep  = (lineColor2.blue  - self.lineColor.blue)  / num          else:
711                list = self.list_avail
712    
713          self.line_widthStep = (lineWidth2 - self.lineWidth) / num          #
714            # always returning 1 reverses the list
715            #
716            list.SortItems(lambda i1, i2: 1)
717    
718        def _OnRetrieve(self, event):
719            self.list_use.DeleteAllItems()
720            self.list_use_data = []
721            self.list_avail.DeleteAllItems()
722            self.list_avail_data = []
723    
724          self.fill_redStep   = (fillColor2.red   - self.fillColor.red)   / num          ThubanBeginBusyCursor()
725          self.fill_greenStep = (fillColor2.green - self.fillColor.green) / num          try:
726          self.fill_blueStep  = (fillColor2.blue  - self.fillColor.blue)  / num              list = self.layer.ShapeStore().Table().UniqueValues(self.fieldName)
727                index = 0
728                for v in list:
729                    self.dataList.append(v)
730                    i = self.list_avail.InsertStringItem(index, str(v))
731                    self.list_avail.SetItemData(index, i)
732        
733                    self.list_avail_data.append(v)
734                    index += 1
735            finally:
736                ThubanEndBusyCursor()
737    
738      def __iter__(self):      def _OnUseAll(self, event):
739          return self          for i in range(self.list_avail.GetItemCount()):
740                self.__MoveListItem(0, self.list_avail, self.list_use)
741    
742      def next(self):      def _OnUse(self, event):
743          if self.count == 0:          self.__MoveSelectedItems(self.list_avail, self.list_use)
             raise StopIteration  
744    
745          prop = ClassGroupProperties()      def _OnDontUse(self, event):
746          prop.SetFill(self.fillColor)          self.__MoveSelectedItems(self.list_use, self.list_avail)
747          prop.SetLineColor(self.lineColor)  
748          prop.SetLineWidth(int(self.lineWidth))      def _OnUseNone(self, event):
749            
750          self.lineColor.red   += self.line_redStep          for i in range(self.list_use.GetItemCount()):
751          self.lineColor.green += self.line_greenStep              self.__MoveListItem(0, self.list_use, self.list_avail)
752          self.lineColor.blue  += self.line_blueStep  
753        def __MoveSelectedItems(self, list_src, list_dest):
754            while True:
755                index = list_src.GetNextItem(-1,
756                                             wxLIST_NEXT_ALL,
757                                             wxLIST_STATE_SELECTED)
758    
759                if index == -1:
760                    break
761    
762                self.__MoveListItem(index, list_src, list_dest)
763    
764    
765        def __MoveListItem(self, index, list_src, list_dest):
766    
767            item = list_src.GetItem(index)
768    
769            x = list_dest.InsertStringItem(
770                    list_dest.GetItemCount(),
771                    str(self.dataList[item.GetData()]))
772    
773            list_dest.SetItemData(x, item.GetData())
774    
775            list_src.DeleteItem(index)
776    
777    #   def _OnListSize(self, event):
778    #       list = event.GetEventObject()
779    
780    #       list.SetColumnWidth(0, event.GetSize().GetWidth())
781    #      
782    
783    ID_QUANTILES_RANGE = 4001
784    ID_QUANTILES_RETRIEVE = 4002
785    
786    class GenQuantilesPanel(wxPanel):
787    
788        def __init__(self, parent, layer, fieldName, fieldType):
789            wxPanel.__init__(self, parent, -1)
790    
791            self.parent = parent
792            self.layer = layer
793            self.fieldName = fieldName
794            self.fieldType = fieldType
795    
796            topBox = wxStaticBoxSizer(wxStaticBox(self, -1, ""),
797                                        wxVERTICAL)
798    
799            self.text_range = wxTextCtrl(self, ID_QUANTILES_RANGE, "")
800            self.button_retrieve = wxButton(self, ID_QUANTILES_RETRIEVE,
801                                            _("Retrieve from Table"))
802    
803            self.spin_numClasses = wxSpinCtrl(self, -1, style=wxTE_RIGHT)
804            self.spin_numClasses.SetRange(1, sys.maxint)
805            self.spin_numClasses.SetValue(1)
806    
807    
808            sizer = wxBoxSizer(wxHORIZONTAL)
809            sizer.Add(wxStaticText(self, -1, _("Apply to Range")), 0, wxALL, 4)
810            sizer.Add(self.text_range, 1, wxALL, 4)
811            sizer.Add(self.button_retrieve, 0, wxALL, 4)
812    
813            topBox.Add(sizer, 0, wxEXPAND, 0)
814    
815            sizer = wxBoxSizer(wxHORIZONTAL)
816            sizer.Add(wxStaticText(self, -1, _("Number of Classes:")), 0, wxALL, 4)
817            sizer.Add(self.spin_numClasses, 1, wxALL, 4)
818    
819            topBox.Add(sizer, 0, wxEXPAND, 0)
820    
821            self.SetSizer(topBox)
822            self.SetAutoLayout(True)
823            topBox.Fit(self)
824            topBox.SetSizeHints(self)
825    
826            EVT_TEXT(self, ID_QUANTILES_RANGE, self.OnRangeText)
827            EVT_BUTTON(self, ID_QUANTILES_RETRIEVE, self.OnRetrieve)
828    
829            self.__range = None
830    
831        def GetNumGroups(self):
832            return self.spin_numClasses.GetValue()
833    
834        def GetRange(self):
835            assert self.__range is not None
836    
837            return self.__range
838    
839        def GetList(self):
840            _list = []
841            table = self.layer.ShapeStore().Table()
842            if table is not None:
843                ThubanBeginBusyCursor()
844                try:
845                    #
846                    # FIXME: Replace with a call to table when the method
847                    # has been written to get all the values
848                    #
849                    for i in range(table.NumRows()):
850                        _list.append(table.ReadValue(i, self.fieldName))
851                finally:
852                    ThubanEndBusyCursor()
853    
854            return _list
855    
856        def OnRangeText(self, event):
857    
858            try:
859                self.__range = Range(self.text_range.GetValue())
860            except ValueError:
861                self.__range = None
862    
863            if self.__range is not None:
864                self.text_range.SetForegroundColour(wxBLACK)
865            else:
866                self.text_range.SetForegroundColour(wxRED)
867    
868        def OnRetrieve(self, event):
869            table = self.layer.ShapeStore().Table()
870            if table is not None:
871                ThubanBeginBusyCursor()
872                try:
873                    min, max = table.ValueRange(self.fieldName)
874                    self.text_range.SetValue("[" + str(min) + ";" + str(max) + "]")
875                finally:
876                    ThubanEndBusyCursor()
877    
878    ID_CUSTOMRAMP_COPYSTART = 4001
879    ID_CUSTOMRAMP_COPYEND = 4002
880    ID_CUSTOMRAMP_EDITSTART = 4003
881    ID_CUSTOMRAMP_EDITEND = 4004
882    ID_CUSTOMRAMP_SPROP = 4005
883    ID_CUSTOMRAMP_EPROP = 4006
884    
885    class CustomRampPanel(wxPanel):
886    
887        def __init__(self, parent, shapeType):
888            wxPanel.__init__(self, parent, -1)
889    
890            topSizer = wxStaticBoxSizer(wxStaticBox(self, -1, ""), wxHORIZONTAL)
891    
892            bsizer = wxBoxSizer(wxVERTICAL)
893            bsizer.Add(wxStaticText(self, -1, _("Start:")), 0, wxALL | wxCENTER, 4)
894            self.startPropCtrl = classifier.ClassGroupPropertiesCtrl(
895                self, ID_CUSTOMRAMP_SPROP,
896                ClassGroupProperties(), shapeType,
897                style=wxSIMPLE_BORDER, size=(40, 20))
898            bsizer.Add(self.startPropCtrl, 1, wxGROW | wxALL | wxCENTER, 4)
899            bsizer.Add(wxButton(self, ID_CUSTOMRAMP_EDITSTART, _("Change")),
900                       0, wxGROW | wxALL | wxCENTER, 4)
901    
902            topSizer.Add(bsizer,
903                       1, wxALL \
904                          | wxSHAPED \
905                          | wxALIGN_CENTER_HORIZONTAL \
906                          | wxALIGN_CENTER_VERTICAL, \
907                       4)
908    
909            bmp = resource.GetBitmapResource(USE_BMP, wxBITMAP_TYPE_XPM)
910            bsizer = wxBoxSizer(wxVERTICAL)
911            bsizer.Add(wxBitmapButton(self, ID_CUSTOMRAMP_COPYSTART, bmp),
912                       0, wxGROW | wxALL, 4)
913            bmp = resource.GetBitmapResource(USENOT_BMP, wxBITMAP_TYPE_XPM)
914            bsizer.Add(wxBitmapButton(self, ID_CUSTOMRAMP_COPYEND, bmp),
915                       0, wxGROW | wxALL, 4)
916    
917            topSizer.Add(bsizer,
918                       0, wxALL \
919                          | wxALIGN_CENTER_HORIZONTAL \
920                          | wxALIGN_CENTER_VERTICAL,
921                       4)
922    
923            bsizer = wxBoxSizer(wxVERTICAL)
924            bsizer.Add(wxStaticText(self, -1, _("End:")), 0, wxALL | wxCENTER, 4)
925            self.endPropCtrl = classifier.ClassGroupPropertiesCtrl(
926                self, ID_CUSTOMRAMP_EPROP,
927                ClassGroupProperties(), shapeType,
928                style=wxSIMPLE_BORDER, size=(40, 20))
929            bsizer.Add(self.endPropCtrl, 1, wxGROW | wxALL | wxCENTER, 4)
930            bsizer.Add(wxButton(self, ID_CUSTOMRAMP_EDITEND, _("Change")),
931                       0, wxGROW | wxALL | wxCENTER, 4)
932    
933            topSizer.Add(bsizer,
934                       1, wxALL \
935                          | wxSHAPED \
936                          | wxALIGN_RIGHT \
937                          | wxALIGN_CENTER_HORIZONTAL \
938                          | wxALIGN_CENTER_VERTICAL,
939                       4)
940    
941            EVT_BUTTON(self, ID_CUSTOMRAMP_COPYSTART, self._OnCopyStart)
942            EVT_BUTTON(self, ID_CUSTOMRAMP_COPYEND, self._OnCopyEnd)
943            EVT_BUTTON(self, ID_CUSTOMRAMP_EDITSTART, self._OnEditStart)
944            EVT_BUTTON(self, ID_CUSTOMRAMP_EDITEND, self._OnEditEnd)
945    
946            self.SetSizer(topSizer)
947            self.SetAutoLayout(True)
948            topSizer.SetSizeHints(self)
949    
950        def GetRamp(self):
951            return CustomRamp(self.startPropCtrl.GetProperties(),
952                              self.endPropCtrl.GetProperties())
953    
954          self.fillColor.red   += self.fill_redStep      def _OnCopyStart(self, event):
955          self.fillColor.green += self.fill_greenStep          self.endPropCtrl.SetProperties(self.startPropCtrl.GetProperties())
         self.fillColor.blue  += self.fill_blueStep  
956    
957          self.lineWidth       += self.line_widthStep      def _OnCopyEnd(self, event):
958            self.startPropCtrl.SetProperties(self.endPropCtrl.GetProperties())
959    
960          self.count -= 1      def _OnEditStart(self, event):
961            self.startPropCtrl.DoEdit()
962    
963          return prop      def _OnEditEnd(self, event):
964            self.endPropCtrl.DoEdit()
965    
966      

Legend:
Removed from v.620  
changed lines
  Added in v.1274

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26