4 @brief Dialog for interactive management of raster color tables and
5 vector rgb_column attributes.
11 (C) 2008, 2010 by the GRASS Development Team
12 This program is free software under the GNU General Public License
13 (>=v2). Read the file COPYING that comes with GRASS for details.
15 @author Michael Barton (Arizona State University)
16 @author Martin Landa <landa.martin gmail.com> (various updates)
17 @author Anna Kratochvilova (load/save raster color tables)
25 import wx.lib.colourselect
as csel
26 import wx.lib.scrolledpanel
as scrolled
36 from debug
import Debug
as Debug
37 from preferences
import globalSettings
as UserSettings
40 def __init__(self, parent, raster, id=wx.ID_ANY, title = _(
"Set color table"),
41 style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
43 """!Dialog for interactively entering rules for map management
46 @param raster True to raster otherwise vector
51 wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
53 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR,
'grass.ico'), wx.BITMAP_TYPE_ICO))
93 self.SetTitle(_(
'Create new color table for raster map'))
94 crlabel = _(
'Enter raster category values or percents')
96 self.SetTitle(_(
'Create new color table for vector map'))
97 crlabel = _(
'Enter vector attribute values or ranges (n or n1 to n2)')
101 maplabel = _(
'Select raster map:')
103 maplabel = _(
'Select vector map:')
104 inputBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
105 label=
" %s " % maplabel)
112 size=globalvar.DIALOG_GSELECT_SIZE,
116 label=_(
'replace existing color table'))
117 self.ovrwrtcheck.SetValue(UserSettings.Get(group=
'cmd', key=
'overwrite', subkey=
'enabled'))
120 self.
btnSave = wx.Button(parent=self, id=wx.ID_SAVE)
121 self.btnSave.SetToolTipString(_(
'Save color table to file'))
127 label=_(
'Attribute column:'))
129 label=_(
'RGB color column:'))
135 self.
cr_label = wx.StaticText(parent=self, id=wx.ID_ANY,
141 self.
numRules = wx.SpinCtrl(parent=self, id=wx.ID_ANY,
148 self.preview.EraseMap()
150 self.
btnCancel = wx.Button(parent=self, id=wx.ID_CANCEL)
151 self.
btnApply = wx.Button(parent=self, id=wx.ID_APPLY)
152 self.
btnOK = wx.Button(parent=self, id=wx.ID_OK)
153 self.btnOK.SetDefault()
154 self.btnOK.Enable(
False)
155 self.btnApply.Enable(
False)
159 self.btnPreview.Enable(
False)
160 self.
btnAdd = wx.Button(parent=self, id=wx.ID_ADD)
161 self.
helpbtn = wx.Button(parent=self, id=wx.ID_HELP)
169 self.Bind(wx.EVT_BUTTON, self.
OnOK, self.
btnOK)
184 layer = self.parent.curr_page.maptree.layer_selected
188 mapLayer = self.parent.curr_page.maptree.GetPyData(layer)[0][
'maplayer']
189 name = mapLayer.GetName()
190 type = mapLayer.GetType()
191 self.selectionInput.SetValue(name)
197 self.SetMinSize(self.GetSize())
199 self.CentreOnScreen()
202 def __doLayout(self):
203 sizer = wx.BoxSizer(wx.VERTICAL)
209 flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=5)
210 replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
211 replaceSizer.Add(item=self.
ovrwrtcheck, proportion=1,
212 flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=1)
214 replaceSizer.Add(item=self.
btnSave, proportion=0,
215 flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
217 self.inputSizer.Add(item=replaceSizer, proportion=1,
218 flag=wx.ALL | wx.EXPAND, border=0)
223 bodySizer = wx.GridBagSizer(hgap=5, vgap=5)
225 bodySizer.Add(item=self.
cr_label, pos=(row, 0), span=(1, 3),
226 flag=wx.ALL, border=5)
229 vSizer = wx.GridBagSizer(hgap=5, vgap=5)
231 flag=wx.ALIGN_CENTER_VERTICAL)
233 flag=wx.ALIGN_CENTER_VERTICAL)
235 flag=wx.ALIGN_CENTER_VERTICAL)
236 vSizer.Add(self.
cb_vcol, pos=(0, 3),
237 flag=wx.ALIGN_CENTER_VERTICAL)
239 flag=wx.ALIGN_CENTER_VERTICAL)
240 vSizer.Add(self.
cb_vrgb, pos=(1, 3),
241 flag=wx.ALIGN_CENTER_VERTICAL)
243 bodySizer.Add(item=vSizer, pos=(row, 0), span=(1, 3))
246 bodySizer.Add(item=self.
cr_panel, pos=(row, 0), span=(1, 2))
248 bodySizer.Add(item=self.
preview, pos=(row, 2),
249 flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10)
250 bodySizer.AddGrowableRow(row)
251 bodySizer.AddGrowableCol(2)
254 bodySizer.Add(item=self.
numRules, pos=(row, 0),
255 flag=wx.ALIGN_CENTER_VERTICAL)
257 bodySizer.Add(item=self.
btnAdd, pos=(row, 1))
258 bodySizer.Add(item=self.
btnPreview, pos=(row, 2),
261 btnSizer = wx.BoxSizer(wx.HORIZONTAL)
263 flag=wx.LEFT | wx.RIGHT, border=5)
265 flag=wx.LEFT | wx.RIGHT, border=5)
267 flag=wx.LEFT | wx.RIGHT, border=5)
268 btnSizer.Add(self.
btnOK,
269 flag=wx.LEFT | wx.RIGHT, border=5)
272 flag=wx.ALL | wx.EXPAND, border=5)
274 sizer.Add(item=bodySizer, proportion=1,
275 flag=wx.ALL | wx.EXPAND, border=5)
277 sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY,
278 style=wx.LI_HORIZONTAL),
280 flag=wx.EXPAND | wx.ALL, border=5)
282 sizer.Add(item=btnSizer, proportion=0,
283 flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
289 def _colorRulesPanel(self):
290 """!Create rules panel"""
291 cr_panel = scrolled.ScrolledPanel(parent=self, id=wx.ID_ANY,
293 style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
294 cr_panel.SetupScrolling(scroll_x =
False)
298 cr_panel.SetAutoLayout(
True)
303 """!Add rules button pressed"""
304 nrules = self.numRules.GetValue()
309 snum = len(self.ruleslines.keys())
310 for num
in range(snum, snum + nrules):
312 enable = wx.CheckBox(parent=self.
cr_panel, id=num)
313 enable.SetValue(
True)
316 txt_ctrl = wx.TextCtrl(parent=self.
cr_panel, id=1000 + num,
318 style=wx.TE_NOHIDESEL)
321 color_ctrl = csel.ColourSelect(self.
cr_panel, id=2000 + num,
322 size = globalvar.DIALOG_COLOR_SIZE)
323 self.Bind(csel.EVT_COLOURSELECT, self.
OnRuleColor, color_ctrl)
324 self.
ruleslines[enable.GetId()] = {
'value' :
'',
327 self.cr_sizer.Add(item=enable, pos=(num, 0),
328 flag=wx.ALIGN_CENTER_VERTICAL)
329 self.cr_sizer.Add(item=txt_ctrl, pos=(num, 1),
330 flag=wx.ALIGN_CENTER | wx.RIGHT, border=5)
331 self.cr_sizer.Add(item=color_ctrl, pos=(num, 2),
332 flag=wx.ALIGN_CENTER | wx.RIGHT, border=10)
334 self.cr_panel.Layout()
335 self.cr_panel.SetupScrolling(scroll_x =
False)
338 """!Initialize preview display, set dimensions and region
345 """!Erase the histogram display
347 self.PreviewWindow.Draw(self.HistWindow.pdc, pdctype=
'clear')
351 Also remove associated rendered images
357 """!Raster/vector map selected"""
359 self.
inmap = event.GetString()
366 if not grass.find_file(name = self.
inmap, element = mapType)[
'file']:
370 self.btnPreview.Enable(
False)
371 self.btnOK.Enable(
False)
372 self.btnApply.Enable(
False)
377 info = grass.raster_info(map = self.
inmap)
386 self.btnPreview.Enable(
False)
387 self.btnOK.Enable(
False)
388 self.btnApply.Enable(
False)
389 self.preview.EraseMap()
390 self.cr_label.SetLabel(_(
'Enter raster category values or percents'))
393 if info[
'datatype'] ==
'CELL':
394 mapRange = _(
'range')
396 mapRange = _(
'fp range')
397 self.cr_label.SetLabel(_(
'Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') %
398 {
'range' : mapRange,
404 self.cb_vlayer.InsertLayers(self.
inmap)
409 self.cb_vcol.InsertColumns(vector=self.
inmap, layer=layer)
410 self.cb_vrgb.InsertColumns(vector=self.
inmap, layer=layer)
413 self.btnPreview.Enable(
True)
414 self.btnOK.Enable(
True)
415 self.btnApply.Enable(
True)
421 self.cb_vcol.InsertColumns(vector=self.
inmap, layer=self.
vlayer)
422 self.cb_vrgb.InsertColumns(vector=self.
inmap, layer=self.
vlayer)
432 """!Rule enabled/disabled"""
435 if event.IsChecked():
436 value = self.FindWindowById(id+1000).GetValue()
437 color = self.FindWindowById(id+2000).GetValue()
438 color_str = str(color[0]) +
':' \
439 + str(color[1]) +
':' + \
444 'color' : color_str }
449 """!Rule value changed"""
451 vals = event.GetString().strip()
456 tc = self.FindWindowById(num)
465 message=_(
"Please select attribute column "
466 "and RGB color column first"))
476 """!Rule color changed"""
479 rgba_color = event.GetValue()
481 rgb_string = str(rgba_color[0]) +
':' \
482 + str(rgba_color[1]) +
':' + \
485 self.
ruleslines[num-2000][
'color'] = rgb_string
489 valslist = vals.split(
'to')
490 if len(valslist) == 1:
491 sqlrule =
'%s=%s' % (self.
properties[
'column'], valslist[0])
492 elif len(valslist) > 1:
493 sqlrule =
'%s>=%s AND %s<=%s' % (self.
properties[
'column'], valslist[0],
501 """!Load current color table (using `r.colors.out`)"""
502 self.ruleslines.clear()
503 self.cr_panel.DestroyChildren()
505 ctable = gcmd.RunCommand(
'r.colors.out',
514 rulesNumber = len(ctable.splitlines())
518 for line
in ctable.splitlines():
519 value, color =
map(
lambda x: x.strip(), line.split(
' '))
522 self.FindWindowById(count + 1000).SetValue(value)
524 for c
in color.split(
':'):
526 self.FindWindowById(count + 2000).SetColour(rgb)
532 """!Save color table to file"""
534 for rule
in self.ruleslines.itervalues():
535 if not rule[
'value']:
537 rulestxt += rule[
'value'] +
' ' + rule[
'color'] +
'\n'
543 dlg = wx.FileDialog(parent = self,
544 message = _(
"Save color table to file"),
545 defaultDir = os.getcwd(), style = wx.SAVE | wx.OVERWRITE_PROMPT)
546 if dlg.ShowModal() == wx.ID_OK:
554 """!Apply selected color table
556 @return True on success otherwise False
559 display = self.parent.GetLayerTree().GetMapDisplay()
560 if display
and display.IsAutoRendered():
561 display.GetWindow().UpdateMap(render =
True)
566 """!Apply selected color table and close the dialog"""
571 """!Do not apply any changes and close the dialog"""
575 """!Update preview (based on computational region)"""
577 self.preview.EraseMap()
583 'map=%s' % self.
inmap]
588 name, mapset = self.inmap.split(
'@')
591 mapset = grass.find_file(self.
inmap, element =
'cell')[
'mapset']
595 if mapset == grass.gisenv()[
'MAPSET']:
596 old_colrtable = grass.find_file(name=name, element=
'colr')[
'file']
598 old_colrtable = grass.find_file(name=name, element=
'colr2/' + mapset)[
'file']
601 colrtemp = utils.GetTempfile()
602 shutil.copyfile(old_colrtable, colrtemp)
607 'map=%s' % self.
inmap,
609 'type=point,line,boundary,area']
613 self.
layer = self.Map.AddLayer(type=ltype, name=
'preview', command=cmdlist,
614 l_active=
True, l_hidden=
False, l_opacity=1.0,
617 self.layer.SetCmd(cmdlist)
621 self.preview.UpdatePreview()
626 shutil.copyfile(colrtemp, old_colrtable)
629 gcmd.RunCommand(
'r.colors',
635 """!Show GRASS manual page"""
640 gcmd.RunCommand(
'g.manual',
645 def _IsNumber(self, s):
646 """!Check if 's' is a number"""
654 """!Creates color table
656 @return True on success
657 @return False on failure
661 for rule
in self.ruleslines.itervalues():
662 if not rule[
'value']:
666 if rule[
'value']
not in (
'nv',
'default')
and \
667 rule[
'value'][-1] !=
'%' and \
669 gcmd.GError(_(
"Invalid rule value '%s'. Unable to apply color table.") % rule[
'value'],
673 rulestxt += rule[
'value'] +
' ' + rule[
'color'] +
'\n'
675 rulestxt +=
"UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.
properties[
'table'],
682 gtemp = utils.GetTempfile()
683 output = open(gtemp,
"w")
685 output.write(rulestxt)
691 not self.ovrwrtcheck.IsChecked():
696 ret = gcmd.RunCommand(
'r.colors',
702 "Check out 'replace existing color table' to "
708 gcmd.RunCommand(
'db.execute',
715 """!A Buffered window class"""
717 style=wx.NO_FULL_REPAINT_ON_RESIZE,
720 wx.Window.__init__(self, parent, id, style = style, **kwargs)
733 self.Bind(wx.EVT_PAINT, self.
OnPaint)
734 self.Bind(wx.EVT_IDLE, self.
OnIdle)
735 self.Bind(wx.EVT_ERASE_BACKGROUND,
lambda x:
None)
750 self.Map.region = self.Map.GetRegion()
753 def Draw(self, pdc, img=None, pdctype='image'):
754 """!Draws preview or clears window"""
757 Debug.msg (3,
"BufferedWindow.Draw(): pdctype=%s" % (pdctype))
759 if pdctype ==
'clear':
761 pdc.SetBackground(bg)
767 if pdctype ==
'image' and img:
768 bg = wx.TRANSPARENT_BRUSH
769 pdc.SetBackground(bg)
770 bitmap = wx.BitmapFromImage(img)
771 w, h = bitmap.GetSize()
772 pdc.DrawBitmap(bitmap, 0, 0,
True)
778 """!Draw pseudo DC to buffer"""
779 self.
_Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
780 dc = wx.BufferedPaintDC(self, self.
_Buffer)
786 bg = wx.Brush(self.GetBackgroundColour())
792 rgn = self.GetUpdateRegion()
796 self.pdc.DrawToDCClipped(dc, r)
799 """!Init image size to match window size"""
801 self.Map.width, self.Map.height = self.GetClientSize()
806 self.
_Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
812 if self.
img and self.Map.width + self.Map.height > 0:
813 self.
img = self.img.Scale(self.Map.width, self.Map.height)
821 """!Only re-render a preview image from GRASS during
822 idle time instead of multiple times during resizing.
830 """!Converts files to wx.Image"""
831 if self.Map.mapfile
and os.path.isfile(self.Map.mapfile)
and \
832 os.path.getsize(self.Map.mapfile):
833 img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
840 """!Update canvas if window changes geometry"""
841 Debug.msg (2,
"BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.
render))
847 self.Map.region = self.Map.GetRegion()
862 self.
Draw(self.
pdc, self.
img, pdctype=
'image')
868 self.
Draw(self.
pdc, pdctype=
'clear')