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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26