GRASS Programmer's Manual  6.4.2(2012)
colorrules.py
Go to the documentation of this file.
00001 """
00002 @package colorrules.py
00003 
00004 @brief Dialog for interactive management of raster color tables and
00005 vector rgb_column attributes.
00006 
00007 Classes:
00008  - ColorTable
00009  - BuferedWindow
00010 
00011 (C) 2008, 2010 by the GRASS Development Team
00012 This program is free software under the GNU General Public License
00013 (>=v2). Read the file COPYING that comes with GRASS for details.
00014 
00015 @author Michael Barton (Arizona State University)
00016 @author Martin Landa <landa.martin gmail.com> (various updates)
00017 @author Anna Kratochvilova (load/save raster color tables)
00018 """
00019 
00020 import os
00021 import sys
00022 import shutil
00023 
00024 import wx
00025 import wx.lib.colourselect as csel
00026 import wx.lib.scrolledpanel as scrolled
00027 
00028 import grass.script as grass
00029 
00030 import dbm
00031 import gcmd
00032 import globalvar
00033 import gselect
00034 import render
00035 import utils
00036 from debug import Debug as Debug
00037 from preferences import globalSettings as UserSettings
00038 
00039 class ColorTable(wx.Frame):
00040     def __init__(self, parent, raster, id=wx.ID_ANY, title = _("Set color table"),
00041                  style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
00042                  **kwargs):
00043         """!Dialog for interactively entering rules for map management
00044         commands
00045 
00046         @param raster True to raster otherwise vector
00047         """
00048         self.parent = parent # GMFrame
00049         self.raster = raster
00050         
00051         wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
00052         
00053         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
00054         
00055         # input map to change
00056         self.inmap = ''
00057 
00058         if self.raster:
00059             # raster properties
00060             self.properties = {
00061                 # min cat in raster map
00062                 'min' : None,
00063                 # max cat in raster map
00064                 'max' : None,
00065                 }
00066         else:
00067             # vector properties
00068             self.properties = {
00069                 # list of database layers for vector (minimum of 1)
00070                 'layers' : ['1'],
00071                 # list of database columns for vector
00072                 'columns' : [],
00073                 # vector layer for attribute table to use for setting color
00074                 'layer' : 1, 
00075                 # vector attribute table used for setting color         
00076                 'table' : '',
00077                 # vector attribute column for assigning colors
00078                 'column' : '', 
00079                 # vector attribute column to use for storing colors
00080                 'rgb' : '',
00081                 }
00082 
00083         # rules for creating colortable
00084         self.ruleslines = {}
00085 
00086         # instance of render.Map to be associated with display
00087         self.Map   = render.Map()  
00088 
00089         # reference to layer with preview
00090         self.layer = None          
00091         
00092         if self.raster:
00093             self.SetTitle(_('Create new color table for raster map'))
00094             crlabel = _('Enter raster category values or percents')
00095         else:
00096             self.SetTitle(_('Create new color table for vector map'))
00097             crlabel = _('Enter vector attribute values or ranges (n or n1 to n2)')
00098         
00099         # top controls
00100         if self.raster:
00101             maplabel = _('Select raster map:')
00102         else:
00103             maplabel = _('Select vector map:')
00104         inputBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
00105                                 label=" %s " % maplabel)
00106         self.inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
00107         if self.raster:
00108             elem = 'cell'
00109         else:
00110             elem = 'vector'
00111         self.selectionInput = gselect.Select(parent=self, id=wx.ID_ANY,
00112                                              size=globalvar.DIALOG_GSELECT_SIZE,
00113                                              type=elem)
00114         
00115         self.ovrwrtcheck = wx.CheckBox(parent=self, id=wx.ID_ANY,
00116                                        label=_('replace existing color table'))
00117         self.ovrwrtcheck.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
00118         
00119         if self.raster:
00120             self.btnSave = wx.Button(parent=self, id=wx.ID_SAVE)
00121             self.btnSave.SetToolTipString(_('Save color table to file'))
00122         
00123         if not self.raster:
00124             self.cb_vl_label = wx.StaticText(parent=self, id=wx.ID_ANY,
00125                                              label=_('Layer:'))
00126             self.cb_vc_label = wx.StaticText(parent=self, id=wx.ID_ANY,
00127                                              label=_('Attribute column:'))
00128             self.cb_vrgb_label = wx.StaticText(parent=self, id=wx.ID_ANY,
00129                                                label=_('RGB color column:'))
00130             self.cb_vlayer = gselect.LayerSelect(self)
00131             self.cb_vcol = gselect.ColumnSelect(self)
00132             self.cb_vrgb = gselect.ColumnSelect(self)
00133         
00134         # color table and preview window
00135         self.cr_label = wx.StaticText(parent=self, id=wx.ID_ANY,
00136                                       label=crlabel)
00137         self.cr_panel = self._colorRulesPanel()
00138         # add two rules as default
00139         self.AddRules(2)
00140         
00141         self.numRules = wx.SpinCtrl(parent=self, id=wx.ID_ANY,
00142                                     min=1, max=1e6)
00143         
00144         # initialize preview display
00145         self.InitDisplay()
00146         self.preview = BufferedWindow(self, id=wx.ID_ANY, size=(400, 300),
00147                                       Map=self.Map)
00148         self.preview.EraseMap()
00149         
00150         self.btnCancel = wx.Button(parent=self, id=wx.ID_CANCEL)
00151         self.btnApply = wx.Button(parent=self, id=wx.ID_APPLY) 
00152         self.btnOK = wx.Button(parent=self, id=wx.ID_OK)
00153         self.btnOK.SetDefault()
00154         self.btnOK.Enable(False)
00155         self.btnApply.Enable(False)
00156         
00157         self.btnPreview = wx.Button(parent=self, id=wx.ID_ANY,
00158                                     label=_("Preview"))
00159         self.btnPreview.Enable(False)
00160         self.btnAdd = wx.Button(parent=self, id=wx.ID_ADD)
00161         self.helpbtn = wx.Button(parent=self, id=wx.ID_HELP)
00162             
00163         
00164         # bindings
00165         self.Bind(wx.EVT_BUTTON, self.OnHelp, self.helpbtn)
00166         self.selectionInput.Bind(wx.EVT_TEXT, self.OnSelectionInput)
00167         self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
00168         self.Bind(wx.EVT_BUTTON, self.OnApply, self.btnApply)
00169         self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
00170         self.Bind(wx.EVT_BUTTON, self.OnPreview, self.btnPreview)
00171         self.Bind(wx.EVT_BUTTON, self.OnAddRules, self.btnAdd)
00172         self.Bind(wx.EVT_CLOSE,  self.OnCloseWindow)
00173         
00174         # additional bindings for raster/vector color management
00175         if self.raster:
00176             self.Bind(wx.EVT_BUTTON, self.OnSaveTable, self.btnSave)
00177         else:
00178             self.Bind(wx.EVT_COMBOBOX, self.OnLayerSelection, self.cb_vlayer)
00179             self.Bind(wx.EVT_COMBOBOX, self.OnColumnSelection, self.cb_vcol)
00180             self.Bind(wx.EVT_COMBOBOX, self.OnRGBColSelection, self.cb_vrgb)
00181 
00182         # set map layer from layer tree
00183         try:
00184             layer = self.parent.curr_page.maptree.layer_selected
00185         except:
00186             layer = None
00187         if layer:
00188             mapLayer = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer']
00189             name = mapLayer.GetName()
00190             type = mapLayer.GetType()
00191             self.selectionInput.SetValue(name)
00192             self.inmap = name
00193             self.OnSelectionInput(None)
00194         
00195         # layout
00196         self.__doLayout()
00197         self.SetMinSize(self.GetSize())
00198         
00199         self.CentreOnScreen()
00200         self.Show()
00201         
00202     def __doLayout(self):
00203         sizer = wx.BoxSizer(wx.VERTICAL)
00204         
00205         #
00206         # input
00207         #
00208         self.inputSizer.Add(item=self.selectionInput,
00209                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=5)
00210         replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
00211         replaceSizer.Add(item=self.ovrwrtcheck, proportion=1,
00212                          flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=1)
00213         if self.raster:
00214             replaceSizer.Add(item=self.btnSave, proportion=0,
00215                             flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
00216         
00217         self.inputSizer.Add(item=replaceSizer, proportion=1,
00218                        flag=wx.ALL | wx.EXPAND, border=0)
00219 
00220         #
00221         # body & preview
00222         #
00223         bodySizer =  wx.GridBagSizer(hgap=5, vgap=5)
00224         row = 0
00225         bodySizer.Add(item=self.cr_label, pos=(row, 0), span=(1, 3),
00226                       flag=wx.ALL, border=5)
00227         
00228         if not self.raster:
00229             vSizer = wx.GridBagSizer(hgap=5, vgap=5)
00230             vSizer.Add(self.cb_vl_label, pos=(0, 0),
00231                        flag=wx.ALIGN_CENTER_VERTICAL)
00232             vSizer.Add(self.cb_vlayer,  pos=(0, 1),
00233                        flag=wx.ALIGN_CENTER_VERTICAL)
00234             vSizer.Add(self.cb_vc_label, pos=(0, 2),
00235                        flag=wx.ALIGN_CENTER_VERTICAL)
00236             vSizer.Add(self.cb_vcol, pos=(0, 3),
00237                        flag=wx.ALIGN_CENTER_VERTICAL)
00238             vSizer.Add(self.cb_vrgb_label, pos=(1, 2),
00239                       flag=wx.ALIGN_CENTER_VERTICAL)
00240             vSizer.Add(self.cb_vrgb, pos=(1, 3),
00241                        flag=wx.ALIGN_CENTER_VERTICAL)
00242             row += 1
00243             bodySizer.Add(item=vSizer, pos=(row, 0), span=(1, 3))
00244         
00245         row += 1
00246         bodySizer.Add(item=self.cr_panel, pos=(row, 0), span=(1, 2))
00247         
00248         bodySizer.Add(item=self.preview, pos=(row, 2),
00249                       flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10)
00250         bodySizer.AddGrowableRow(row)
00251         bodySizer.AddGrowableCol(2)
00252         
00253         row += 1
00254         bodySizer.Add(item=self.numRules, pos=(row, 0),
00255                       flag=wx.ALIGN_CENTER_VERTICAL)
00256         
00257         bodySizer.Add(item=self.btnAdd, pos=(row, 1))
00258         bodySizer.Add(item=self.btnPreview, pos=(row, 2),
00259                       flag=wx.ALIGN_RIGHT)
00260         
00261         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00262         btnSizer.Add(self.helpbtn,
00263                      flag=wx.LEFT | wx.RIGHT, border=5)
00264         btnSizer.Add(self.btnCancel,
00265                      flag=wx.LEFT | wx.RIGHT, border=5)
00266         btnSizer.Add(self.btnApply,
00267                      flag=wx.LEFT | wx.RIGHT, border=5)
00268         btnSizer.Add(self.btnOK,
00269                      flag=wx.LEFT | wx.RIGHT, border=5)
00270         
00271         sizer.Add(item=self.inputSizer, proportion=0,
00272                   flag=wx.ALL | wx.EXPAND, border=5)
00273         
00274         sizer.Add(item=bodySizer, proportion=1,
00275                   flag=wx.ALL | wx.EXPAND, border=5)
00276         
00277         sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY,
00278                                      style=wx.LI_HORIZONTAL),
00279                   proportion=0,
00280                   flag=wx.EXPAND | wx.ALL, border=5) 
00281         
00282         sizer.Add(item=btnSizer, proportion=0,
00283                   flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
00284         
00285         self.SetSizer(sizer)
00286         sizer.Fit(self)
00287         self.Layout()
00288         
00289     def _colorRulesPanel(self):
00290         """!Create rules panel"""
00291         cr_panel = scrolled.ScrolledPanel(parent=self, id=wx.ID_ANY,
00292                                           size=(180, 300),
00293                                           style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
00294         cr_panel.SetupScrolling(scroll_x = False)
00295         self.cr_sizer = wx.GridBagSizer(vgap=2, hgap=4)
00296         
00297         cr_panel.SetSizer(self.cr_sizer)
00298         cr_panel.SetAutoLayout(True)
00299         
00300         return cr_panel        
00301 
00302     def OnAddRules(self, event):
00303         """!Add rules button pressed"""
00304         nrules = self.numRules.GetValue()
00305         self.AddRules(nrules)
00306         
00307     def AddRules(self, nrules):
00308         """!Add rules"""
00309         snum = len(self.ruleslines.keys())
00310         for num in range(snum, snum + nrules):
00311             # enable
00312             enable = wx.CheckBox(parent=self.cr_panel, id=num)
00313             enable.SetValue(True)
00314             self.Bind(wx.EVT_CHECKBOX, self.OnRuleEnable, enable)
00315             # value
00316             txt_ctrl = wx.TextCtrl(parent=self.cr_panel, id=1000 + num,
00317                                    size=(90, -1),
00318                                    style=wx.TE_NOHIDESEL)
00319             self.Bind(wx.EVT_TEXT, self.OnRuleValue, txt_ctrl)
00320             # color
00321             color_ctrl = csel.ColourSelect(self.cr_panel, id=2000 + num,
00322                                            size = globalvar.DIALOG_COLOR_SIZE)
00323             self.Bind(csel.EVT_COLOURSELECT, self.OnRuleColor, color_ctrl)
00324             self.ruleslines[enable.GetId()] = { 'value' : '',
00325                                                 'color': "0:0:0" }
00326             
00327             self.cr_sizer.Add(item=enable, pos=(num, 0),
00328                               flag=wx.ALIGN_CENTER_VERTICAL)
00329             self.cr_sizer.Add(item=txt_ctrl, pos=(num, 1),
00330                               flag=wx.ALIGN_CENTER | wx.RIGHT, border=5)
00331             self.cr_sizer.Add(item=color_ctrl, pos=(num, 2),
00332                               flag=wx.ALIGN_CENTER | wx.RIGHT, border=10)
00333         
00334         self.cr_panel.Layout()
00335         self.cr_panel.SetupScrolling(scroll_x = False)
00336         
00337     def InitDisplay(self):
00338         """!Initialize preview display, set dimensions and region
00339         """
00340         self.width = self.Map.width = 400
00341         self.height = self.Map.height = 300
00342         self.Map.geom = self.width, self.height
00343 
00344     def OnErase(self, event):
00345         """!Erase the histogram display
00346         """
00347         self.PreviewWindow.Draw(self.HistWindow.pdc, pdctype='clear')
00348 
00349     def OnCloseWindow(self, event):
00350         """!Window closed
00351         Also remove associated rendered images
00352         """
00353         self.Map.Clean()
00354         self.Destroy()
00355         
00356     def OnSelectionInput(self, event):
00357         """!Raster/vector map selected"""
00358         if event:
00359             self.inmap = event.GetString()
00360 
00361         if self.inmap:
00362             if self.raster:
00363                 mapType = 'cell'
00364             else:
00365                 mapType = 'vector'
00366             if not grass.find_file(name = self.inmap, element = mapType)['file']:
00367                 self.inmap = None
00368         
00369         if not self.inmap:
00370             self.btnPreview.Enable(False)
00371             self.btnOK.Enable(False)
00372             self.btnApply.Enable(False)
00373             self.OnLoadTable(event)
00374             return
00375         
00376         if self.raster:
00377             info = grass.raster_info(map = self.inmap)
00378             
00379             if info:
00380                 self.properties['min'] = info['min']
00381                 self.properties['max'] = info['max']
00382                 self.OnLoadTable(event)
00383             else:
00384                 self.inmap = ''
00385                 self.properties['min'] = self.properties['max'] = None
00386                 self.btnPreview.Enable(False)
00387                 self.btnOK.Enable(False)
00388                 self.btnApply.Enable(False)
00389                 self.preview.EraseMap()
00390                 self.cr_label.SetLabel(_('Enter raster category values or percents'))
00391                 return
00392             
00393             if info['datatype'] == 'CELL':
00394                 mapRange = _('range')
00395             else:
00396                 mapRange = _('fp range')
00397             self.cr_label.SetLabel(_('Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') %
00398                                      { 'range' : mapRange,
00399                                        'min' : self.properties['min'],
00400                                        'max' : self.properties['max'] })
00401         
00402         else:
00403             # initialize layer selection combobox
00404             self.cb_vlayer.InsertLayers(self.inmap)
00405             # initialize attribute table for layer=1
00406             layer = int(self.properties['layer'])
00407             self.properties['table'] = gselect.VectorDBInfo(self.inmap).layers[layer]['table']
00408             # initialize column selection comboboxes 
00409             self.cb_vcol.InsertColumns(vector=self.inmap, layer=layer)
00410             self.cb_vrgb.InsertColumns(vector=self.inmap, layer=layer)
00411             self.Update()
00412     
00413         self.btnPreview.Enable(True)
00414         self.btnOK.Enable(True)
00415         self.btnApply.Enable(True)
00416         
00417     def OnLayerSelection(self, event):
00418         # reset choices in column selection comboboxes if layer changes
00419         self.vlayer = int(event.GetString())
00420         self.vtable = gselect.VectorDBInfo(self.inmap).layers[str(self.vlayer)]
00421         self.cb_vcol.InsertColumns(vector=self.inmap, layer=self.vlayer)
00422         self.cb_vrgb.InsertColumns(vector=self.inmap, layer=self.vlayer)
00423         self.Update()
00424         
00425     def OnColumnSelection(self, event):
00426         self.properties['column'] = event.GetString()
00427     
00428     def OnRGBColSelection(self, event):
00429         self.properties['rgb'] = event.GetString()
00430         
00431     def OnRuleEnable(self, event):
00432         """!Rule enabled/disabled"""
00433         id = event.GetId()
00434         
00435         if event.IsChecked():
00436             value = self.FindWindowById(id+1000).GetValue()
00437             color = self.FindWindowById(id+2000).GetValue()
00438             color_str = str(color[0]) + ':' \
00439                 + str(color[1]) + ':' + \
00440                 str(color[2])
00441             
00442             self.ruleslines[id] = {
00443                 'value' : value,
00444                 'color' : color_str }
00445         else:
00446             del self.ruleslines[id]
00447         
00448     def OnRuleValue(self, event):
00449         """!Rule value changed"""
00450         num = event.GetId()
00451         vals = event.GetString().strip()
00452         
00453         if vals == '':
00454             return
00455 
00456         tc = self.FindWindowById(num)
00457         
00458         if self.raster:
00459             self.ruleslines[num-1000]['value'] = vals
00460         
00461         else:
00462             if self.properties['column'] == '' or self.properties['rgb'] == '':
00463                 tc.SetValue('')
00464                 gcmd.GMessage(parent=self,
00465                               message=_("Please select attribute column "
00466                                         "and RGB color column first"))
00467             else:
00468                 try:
00469                     self.ruleslines[num-1000]['value'] = self.SQLConvert(vals)
00470                 except ValueError:
00471                     tc.SetValue('')
00472                     self.ruleslines[num-1000]['value'] = ''
00473                     return
00474         
00475     def OnRuleColor(self, event):
00476         """!Rule color changed"""
00477         num = event.GetId()
00478         
00479         rgba_color = event.GetValue()
00480         
00481         rgb_string = str(rgba_color[0]) + ':' \
00482             + str(rgba_color[1]) + ':' + \
00483             str(rgba_color[2])
00484         
00485         self.ruleslines[num-2000]['color'] = rgb_string
00486         
00487     def SQLConvert(self, vals):
00488         valslist = []
00489         valslist = vals.split('to')
00490         if len(valslist) == 1:
00491             sqlrule = '%s=%s' % (self.properties['column'], valslist[0])
00492         elif len(valslist) > 1:
00493             sqlrule = '%s>=%s AND %s<=%s' % (self.properties['column'], valslist[0],
00494                                              self.properties['column'], valslist[1])
00495         else:
00496             return None
00497         
00498         return sqlrule
00499         
00500     def OnLoadTable(self, event):
00501         """!Load current color table (using `r.colors.out`)"""
00502         self.ruleslines.clear()
00503         self.cr_panel.DestroyChildren()
00504         if self.inmap:
00505             ctable = gcmd.RunCommand('r.colors.out',
00506                                      parent = self,
00507                                      read = True,
00508                                      map = self.inmap,
00509                                      rules = '-')
00510         else:
00511             self.OnPreview(event)
00512             return
00513         
00514         rulesNumber = len(ctable.splitlines())
00515         self.AddRules(rulesNumber)
00516         
00517         count = 0
00518         for line in ctable.splitlines():
00519             value, color = map(lambda x: x.strip(), line.split(' '))
00520             self.ruleslines[count]['value'] = value
00521             self.ruleslines[count]['color'] = color
00522             self.FindWindowById(count + 1000).SetValue(value)
00523             rgb = list()
00524             for c in color.split(':'):
00525                 rgb.append(int(c))
00526             self.FindWindowById(count + 2000).SetColour(rgb)
00527             count += 1
00528         
00529         self.OnPreview(tmp = False)
00530         
00531     def OnSaveTable(self, event):
00532         """!Save color table to file"""
00533         rulestxt = ''   
00534         for rule in self.ruleslines.itervalues():
00535             if not rule['value']:
00536                 continue
00537             rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
00538         if not rulestxt:
00539             gcmd.GMessage(message = _("Nothing to save."),
00540                           parent = self)
00541             return
00542         
00543         dlg = wx.FileDialog(parent = self,
00544                             message = _("Save color table to file"),
00545                             defaultDir = os.getcwd(), style = wx.SAVE | wx.OVERWRITE_PROMPT)
00546         if dlg.ShowModal() == wx.ID_OK:
00547             path = dlg.GetPath()
00548             fd = open(path, 'w')
00549             fd.write(rulestxt)
00550             fd.close()            
00551         dlg.Destroy()
00552           
00553     def OnApply(self, event):
00554         """!Apply selected color table
00555 
00556         @return True on success otherwise False
00557         """
00558         ret = self.CreateColorTable()
00559         display = self.parent.GetLayerTree().GetMapDisplay()
00560         if display and display.IsAutoRendered():
00561             display.GetWindow().UpdateMap(render = True)
00562         
00563         return ret
00564 
00565     def OnOK(self, event):
00566         """!Apply selected color table and close the dialog"""
00567         if self.OnApply(event):
00568             self.Destroy()
00569     
00570     def OnCancel(self, event):
00571         """!Do not apply any changes and close the dialog"""
00572         self.Destroy()
00573         
00574     def OnPreview(self, event = None, tmp = True):
00575         """!Update preview (based on computational region)"""
00576         if not self.inmap:
00577             self.preview.EraseMap()
00578             return
00579         
00580         # raster
00581         if self.raster:
00582             cmdlist = ['d.rast',
00583                        'map=%s' % self.inmap]
00584             ltype = 'raster'
00585             
00586             # find existing color table and copy to temp file
00587             try:
00588                 name, mapset = self.inmap.split('@')
00589             except ValueError:
00590                 name = self.inmap
00591                 mapset = grass.find_file(self.inmap, element = 'cell')['mapset']
00592                 if not mapset:
00593                     return
00594             old_colrtable = None
00595             if mapset == grass.gisenv()['MAPSET']:
00596                 old_colrtable = grass.find_file(name=name, element='colr')['file']
00597             else:
00598                 old_colrtable = grass.find_file(name=name, element='colr2/' + mapset)['file']
00599             
00600             if old_colrtable:
00601                 colrtemp = utils.GetTempfile()
00602                 shutil.copyfile(old_colrtable, colrtemp)
00603         # vector
00604         else:
00605             cmdlist = ['d.vect',
00606                         '-a',
00607                        'map=%s' % self.inmap,
00608                        'rgb_column=%s' % self.properties["rgb"],
00609                        'type=point,line,boundary,area']
00610             ltype = 'vector'
00611         
00612         if not self.layer:
00613             self.layer = self.Map.AddLayer(type=ltype, name='preview', command=cmdlist,
00614                                            l_active=True, l_hidden=False, l_opacity=1.0,
00615                                            l_render=False) 
00616         else:
00617             self.layer.SetCmd(cmdlist)
00618         
00619         # apply new color table and display preview
00620         self.CreateColorTable(force = True)
00621         self.preview.UpdatePreview()
00622         
00623         # restore previous color table
00624         if self.raster and tmp:
00625             if old_colrtable:
00626                 shutil.copyfile(colrtemp, old_colrtable)
00627                 os.remove(colrtemp)
00628             else:
00629                 gcmd.RunCommand('r.colors',
00630                                 parent = self,
00631                                 flags = 'r',
00632                                 map = self.inmap)
00633         
00634     def OnHelp(self, event):
00635         """!Show GRASS manual page"""
00636         if self.raster:
00637             cmd = 'r.colors'
00638         else:
00639             cmd = 'vcolors'
00640         gcmd.RunCommand('g.manual',
00641                         quiet = True,
00642                         parent = self,
00643                         entry = cmd)
00644         
00645     def _IsNumber(self, s):
00646         """!Check if 's' is a number"""
00647         try:
00648             float(s)
00649             return True
00650         except ValueError:
00651             return False
00652         
00653     def CreateColorTable(self, force = False):
00654         """!Creates color table
00655 
00656         @return True on success
00657         @return False on failure
00658         """
00659         rulestxt = ''
00660         
00661         for rule in self.ruleslines.itervalues():
00662             if not rule['value']: # skip empty rules
00663                 continue
00664             
00665             if self.raster:
00666                 if rule['value'] not in ('nv', 'default') and \
00667                         rule['value'][-1] != '%' and \
00668                         not self._IsNumber(rule['value']):
00669                     gcmd.GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
00670                                 parent = self)
00671                     return False
00672                 
00673                 rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
00674             else:
00675                 rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
00676                                                                     self.properties['rgb'],
00677                                                                     rule['color'],
00678                                                                     rule['value'])
00679         if not rulestxt:
00680             return False
00681         
00682         gtemp = utils.GetTempfile()
00683         output = open(gtemp, "w")
00684         try:
00685             output.write(rulestxt)
00686         finally:
00687             output.close()
00688         
00689         if self.raster:
00690             if not force and \
00691                     not self.ovrwrtcheck.IsChecked():
00692                 flags = 'w'
00693             else:
00694                 flags = ''
00695             
00696             ret = gcmd.RunCommand('r.colors',
00697                                   flags = flags,
00698                                   map = self.inmap,
00699                                   rules = gtemp)
00700             if ret != 0:
00701                 gcmd.GMessage(_("Color table already exists. "
00702                                 "Check out 'replace existing color table' to "
00703                                 "overwrite it."),
00704                               parent = self)
00705                 return False
00706         
00707         else:
00708             gcmd.RunCommand('db.execute',
00709                             parent = self,
00710                             input = gtemp)
00711         
00712         return True
00713     
00714 class BufferedWindow(wx.Window):
00715     """!A Buffered window class"""
00716     def __init__(self, parent, id,
00717                  style=wx.NO_FULL_REPAINT_ON_RESIZE,
00718                  Map=None, **kwargs):
00719         
00720         wx.Window.__init__(self, parent, id, style = style, **kwargs)
00721 
00722         self.parent = parent
00723         self.Map = Map
00724         
00725         # re-render the map from GRASS or just redraw image
00726         self.render = True
00727         # indicates whether or not a resize event has taken place
00728         self.resize = False 
00729 
00730         #
00731         # event bindings
00732         #
00733         self.Bind(wx.EVT_PAINT,        self.OnPaint)
00734         self.Bind(wx.EVT_IDLE,         self.OnIdle)
00735         self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
00736 
00737         #
00738         # render output objects
00739         #
00740         # image file to be rendered
00741         self.mapfile = None 
00742         # wx.Image object (self.mapfile)
00743         self.img = None
00744 
00745         self.pdc = wx.PseudoDC()
00746         # will store an off screen empty bitmap for saving to file
00747         self._Buffer = None 
00748 
00749         # make sure that extents are updated at init
00750         self.Map.region = self.Map.GetRegion()
00751         self.Map.SetRegion()
00752 
00753     def Draw(self, pdc, img=None, pdctype='image'):
00754         """!Draws preview or clears window"""
00755         pdc.BeginDrawing()
00756 
00757         Debug.msg (3, "BufferedWindow.Draw(): pdctype=%s" % (pdctype))
00758 
00759         if pdctype == 'clear': # erase the display
00760             bg = wx.WHITE_BRUSH
00761             pdc.SetBackground(bg)
00762             pdc.Clear()
00763             self.Refresh()
00764             pdc.EndDrawing()
00765             return
00766 
00767         if pdctype == 'image' and img:
00768             bg = wx.TRANSPARENT_BRUSH
00769             pdc.SetBackground(bg)
00770             bitmap = wx.BitmapFromImage(img)
00771             w, h = bitmap.GetSize()
00772             pdc.DrawBitmap(bitmap, 0, 0, True) # draw the composite map
00773             
00774         pdc.EndDrawing()
00775         self.Refresh()
00776 
00777     def OnPaint(self, event):
00778         """!Draw pseudo DC to buffer"""
00779         self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
00780         dc = wx.BufferedPaintDC(self, self._Buffer)
00781         
00782         # use PrepareDC to set position correctly
00783         self.PrepareDC(dc)
00784         
00785         # we need to clear the dc BEFORE calling PrepareDC
00786         bg = wx.Brush(self.GetBackgroundColour())
00787         dc.SetBackground(bg)
00788         dc.Clear()
00789         
00790         # create a clipping rect from our position and size
00791         # and the Update Region
00792         rgn = self.GetUpdateRegion()
00793         r = rgn.GetBox()
00794         
00795         # draw to the dc using the calculated clipping rect
00796         self.pdc.DrawToDCClipped(dc, r)
00797         
00798     def OnSize(self, event):
00799         """!Init image size to match window size"""
00800         # set size of the input image
00801         self.Map.width, self.Map.height = self.GetClientSize()
00802 
00803         # Make new off screen bitmap: this bitmap will always have the
00804         # current drawing in it, so it can be used to save the image to
00805         # a file, or whatever.
00806         self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
00807 
00808         # get the image to be rendered
00809         self.img = self.GetImage()
00810 
00811         # update map display
00812         if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
00813             self.img = self.img.Scale(self.Map.width, self.Map.height)
00814             self.render = False
00815             self.UpdatePreview()
00816 
00817         # re-render image on idle
00818         self.resize = True
00819 
00820     def OnIdle(self, event):
00821         """!Only re-render a preview image from GRASS during
00822         idle time instead of multiple times during resizing.
00823         """
00824         if self.resize:
00825             self.render = True
00826             self.UpdatePreview()
00827         event.Skip()
00828 
00829     def GetImage(self):
00830         """!Converts files to wx.Image"""
00831         if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
00832                 os.path.getsize(self.Map.mapfile):
00833             img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
00834         else:
00835             img = None
00836         
00837         return img
00838     
00839     def UpdatePreview(self, img=None):
00840         """!Update canvas if window changes geometry"""
00841         Debug.msg (2, "BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.render))
00842         oldfont = ""
00843         oldencoding = ""
00844         
00845         if self.render:
00846             # make sure that extents are updated
00847             self.Map.region = self.Map.GetRegion()
00848             self.Map.SetRegion()
00849             
00850             # render new map images
00851             self.mapfile = self.Map.Render(force=self.render)
00852             self.img = self.GetImage()
00853             self.resize = False
00854         
00855         if not self.img:
00856             return
00857         
00858         # paint images to PseudoDC
00859         self.pdc.Clear()
00860         self.pdc.RemoveAll()
00861         # draw map image background
00862         self.Draw(self.pdc, self.img, pdctype='image')
00863         
00864         self.resize = False
00865         
00866     def EraseMap(self):
00867         """!Erase preview"""
00868         self.Draw(self.pdc, pdctype='clear')
00869     
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines