123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842 |
- """
- @package module.colorrules
- @brief Dialog for interactive management of raster/vector color tables
- and color rules.
- Classes:
- - colorrules::RulesPanel
- - colorrules::ColorTable
- - colorrules::RasterColorTable
- - colorrules::VectorColorTable
- - colorrules::ThematicVectorTable
- - colorrules::BufferedWindow
- (C) 2008, 2010-2011 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- @author Michael Barton (Arizona State University)
- @author Martin Landa <landa.martin gmail.com> (various updates)
- @author Anna Kratochvilova <kratochanna gmail.com> (split to base and derived classes)
- """
- import os
- import shutil
- import copy
- import tempfile
- import wx
- import wx.lib.colourselect as csel
- import wx.lib.scrolledpanel as scrolled
- import wx.lib.filebrowsebutton as filebrowse
- import grass.script as grass
- from core import globalvar
- from core import utils
- from core.gcmd import GMessage, RunCommand
- from gui_core.gselect import Select, LayerSelect, ColumnSelect, VectorDBInfo
- from core.render import Map
- from gui_core.forms import GUI
- from core.debug import Debug as Debug
- from core.settings import UserSettings
- from nviz.mapwindow import wxUpdateProperties
- class RulesPanel:
- def __init__(self, parent, mapType, attributeType, properties, panelWidth = 180):
- """!Create rules panel
-
- @param mapType raster/vector
- @param attributeType color/size for choosing widget type
- @param properties properties of classes derived from ColorTable
- @param panelWidth width of scroll panel"""
-
- self.ruleslines = {}
- self.mapType = mapType
- self.attributeType = attributeType
- self.properties = properties
- self.parent = parent
- self.panelWidth = panelWidth
-
- self.mainSizer = wx.FlexGridSizer(cols = 3, vgap = 6, hgap = 4)
- # put small border at the top of panel
- for i in range(3):
- self.mainSizer.Add(item = wx.Size(3, 3))
-
- self.mainPanel = scrolled.ScrolledPanel(parent, id = wx.ID_ANY,
- size = (self.panelWidth, 300),
- style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
-
- # (un)check all
- self.checkAll = wx.CheckBox(parent, id = wx.ID_ANY, label = _("Check all"))
- self.checkAll.SetValue(True)
- # clear button
- self.clearAll = wx.Button(parent, id = wx.ID_ANY, label = _("Clear all"))
- # determines how many rules should be added
- self.numRules = wx.SpinCtrl(parent, id = wx.ID_ANY,
- min = 1, max = 1e6)
- # add rules
- self.btnAdd = wx.Button(parent, id = wx.ID_ADD)
-
- self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAddRules)
- self.checkAll.Bind(wx.EVT_CHECKBOX, self.OnCheckAll)
- self.clearAll.Bind(wx.EVT_BUTTON, self.OnClearAll)
- self.mainPanel.SetSizer(self.mainSizer)
- self.mainPanel.SetAutoLayout(True)
- self.mainPanel.SetupScrolling()
-
- def Clear(self):
- """!Clear and widgets and delete information"""
- self.ruleslines.clear()
- self.mainSizer.Clear(deleteWindows=True)
-
- def OnCheckAll(self, event):
- """!(Un)check all rules"""
- check = event.GetInt()
- for child in self.mainPanel.GetChildren():
- if child.GetName() == 'enable':
- child.SetValue(check)
- else:
- child.Enable(check)
-
- def OnClearAll(self, event):
- """!Delete all widgets in panel"""
- self.Clear()
-
- def OnAddRules(self, event):
- """!Add rules button pressed"""
- nrules = self.numRules.GetValue()
- self.AddRules(nrules)
-
- def AddRules(self, nrules, start = False):
- """!Add rules
- @param start set widgets (not append)"""
-
- snum = len(self.ruleslines.keys())
- if start:
- snum = 0
- for num in range(snum, snum + nrules):
- # enable
- enable = wx.CheckBox(parent = self.mainPanel, id = num)
- enable.SetValue(True)
- enable.SetName('enable')
- enable.Bind(wx.EVT_CHECKBOX, self.OnRuleEnable)
- # value
- txt_ctrl = wx.TextCtrl(parent = self.mainPanel, id = 1000 + num,
- size = (80, -1),
- style = wx.TE_NOHIDESEL)
- if self.mapType == 'vector':
- txt_ctrl.SetToolTipString(_("Enter vector attribute values"))
- txt_ctrl.Bind(wx.EVT_TEXT, self.OnRuleValue)
- txt_ctrl.SetName('source')
- if self.attributeType == 'color':
- # color
- columnCtrl = csel.ColourSelect(self.mainPanel, id = 2000 + num,
- size = globalvar.DIALOG_COLOR_SIZE)
- columnCtrl.Bind(csel.EVT_COLOURSELECT, self.OnRuleColor)
- columnCtrl.SetName('target')
- if not start:
- self.ruleslines[enable.GetId()] = { 'value' : '',
- 'color': "0:0:0" }
- else:
- # size or width
- init = 2
- if self.attributeType == 'size':
- init = 100
- columnCtrl = wx.SpinCtrl(self.mainPanel, id = 2000 + num,
- size = (50, -1), min = 1, max = 1e4,
- initial = init)
- columnCtrl.Bind(wx.EVT_SPINCTRL, self.OnRuleSize)
- columnCtrl.Bind(wx.EVT_TEXT, self.OnRuleSize)
- columnCtrl.SetName('target')
- if not start:
- self.ruleslines[enable.GetId()] = { 'value' : '',
- self.attributeType: init }
-
- self.mainSizer.Add(item = enable, proportion = 0,
- flag = wx.ALIGN_CENTER_VERTICAL)
- self.mainSizer.Add(item = txt_ctrl, proportion = 0,
- flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
- self.mainSizer.Add(item = columnCtrl, proportion = 0,
- flag = wx.ALIGN_CENTER | wx.RIGHT, border = 10)
-
- self.mainPanel.Layout()
- self.mainPanel.SetupScrolling(scroll_x = False)
-
- def OnRuleEnable(self, event):
- """!Rule enabled/disabled"""
- id = event.GetId()
-
- if event.IsChecked():
- self.mainPanel.FindWindowById(id + 1000).Enable()
- self.mainPanel.FindWindowById(id + 2000).Enable()
- if self.mapType == 'vector' and not self.parent.GetParent().colorTable:
- vals = []
- vals.append(self.mainPanel.FindWindowById(id + 1000).GetValue())
- try:
- vals.append(self.mainPanel.FindWindowById(id + 1 + 1000).GetValue())
- except AttributeError:
- vals.append(None)
- value = self.SQLConvert(vals)
- else:
- value = self.mainPanel.FindWindowById(id + 1000).GetValue()
- color = self.mainPanel.FindWindowById(id + 2000).GetValue()
-
- if self.attributeType == 'color':
- # color
- color_str = str(color[0]) + ':' \
- + str(color[1]) + ':' \
- + str(color[2])
- self.ruleslines[id] = {'value' : value,
- 'color' : color_str }
-
- else:
- # size or width
- self.ruleslines[id] = {'value' : value,
- self.attributeType : float(color) }
-
- else:
- self.mainPanel.FindWindowById(id + 1000).Disable()
- self.mainPanel.FindWindowById(id + 2000).Disable()
- del self.ruleslines[id]
-
- def OnRuleColor(self, event):
- """!Rule color changed"""
- num = event.GetId()
-
- rgba_color = event.GetValue()
-
- rgb_string = str(rgba_color[0]) + ':' \
- + str(rgba_color[1]) + ':' \
- + str(rgba_color[2])
-
- self.ruleslines[num-2000]['color'] = rgb_string
-
- def OnRuleSize(self, event):
- """!Rule size changed"""
- num = event.GetId()
- size = event.GetInt()
-
- self.ruleslines[num - 2000][self.attributeType] = size
-
- def OnRuleValue(self, event):
- """!Rule value changed"""
- num = event.GetId()
- val = event.GetString().strip()
-
- if val == '':
- return
- try:
- table = self.parent.colorTable
- except AttributeError:
- # due to scrollpanel in vector dialog
- table = self.parent.GetParent().colorTable
- if table:
- self.SetRasterRule(num, val)
- else:
- self.SetVectorRule(num, val)
- def SetRasterRule(self, num, val):
- """!Set raster rule"""
- self.ruleslines[num - 1000]['value'] = val
- def SetVectorRule(self, num, val):
- """!Set vector rule"""
- vals = []
- vals.append(val)
- try:
- vals.append(self.mainPanel.FindWindowById(num + 1).GetValue())
- except AttributeError:
- vals.append(None)
- self.ruleslines[num - 1000]['value'] = self.SQLConvert(vals)
-
- def Enable(self, enable = True):
- """!Enable/Disable all widgets"""
- for child in self.mainPanel.GetChildren():
- child.Enable(enable)
- sql = True
- self.LoadRulesline(sql)# todo
- self.btnAdd.Enable(enable)
- self.numRules.Enable(enable)
- self.checkAll.Enable(enable)
- self.clearAll.Enable(enable)
-
-
- def LoadRules(self):
- message = ""
- for item in range(len(self.ruleslines)):
- try:
- self.mainPanel.FindWindowById(item + 1000).SetValue(self.ruleslines[item]['value'])
- r, g, b = (0, 0, 0) # default
- if not self.ruleslines[item][self.attributeType]:
- if self.attributeType == 'color':
- self.ruleslines[item][self.attributeType] = '%d:%d:%d' % (r, g, b)
- elif self.attributeType == 'size':
- self.ruleslines[item][self.attributeType] = 100
- elif self.attributeType == 'width':
- self.ruleslines[item][self.attributeType] = 2
-
- if self.attributeType == 'color':
- try:
- r, g, b = map(int, self.ruleslines[item][self.attributeType].split(':'))
- except ValueError, e:
- message = _("Bad color format. Use color format '0:0:0'")
- self.mainPanel.FindWindowById(item + 2000).SetValue((r, g, b))
- else:
- value = float(self.ruleslines[item][self.attributeType])
- self.mainPanel.FindWindowById(item + 2000).SetValue(value)
- except:
- continue
-
- if message:
- GMessage(parent = self.parent, message = message)
- return False
-
- return True
-
- def SQLConvert(self, vals):
- """!Prepare value for SQL query"""
- if vals[0].isdigit():
- sqlrule = '%s=%s' % (self.properties['sourceColumn'], vals[0])
- if vals[1]:
- sqlrule += ' AND %s<%s' % (self.properties['sourceColumn'], vals[1])
- else:
- sqlrule = '%s=%s' % (self.properties['sourceColumn'], vals[0])
-
- return sqlrule
- class ColorTable(wx.Frame):
- def __init__(self, parent, title, id = wx.ID_ANY,
- style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
- **kwargs):
- """!Dialog for interactively entering rules for map management
- commands
- @param raster True to raster otherwise vector
- @param nviz True if ColorTable is called from nviz thematic mapping
- """
- self.parent = parent # GMFrame
- wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
-
- self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
-
- # instance of render.Map to be associated with display
- self.Map = Map()
-
- # input map to change
- self.inmap = ''
- # reference to layer with preview
- self.layer = None
- # layout
- self._doLayout()
-
- # bindings
- self.Bind(wx.EVT_BUTTON, self.OnHelp, self.btnHelp)
- self.selectionInput.Bind(wx.EVT_TEXT, self.OnSelectionInput)
- self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
- self.Bind(wx.EVT_BUTTON, self.OnApply, self.btnApply)
- self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
- self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
-
- self.Bind(wx.EVT_BUTTON, self.OnPreview, self.btnPreview)
-
- def _initLayer(self):
- """!Set initial layer when opening dialog"""
- # set map layer from layer tree, first selected,
- # if not the right type, than select another
- try:
- sel = self.parent.curr_page.maptree.layer_selected
- if sel and self.parent.curr_page.maptree.GetPyData(sel)[0]['type'] == self.mapType:
- layer = sel
- else:
- layer = self.parent.curr_page.maptree.FindItemByData(key = 'type', value = self.mapType)
- except:
- layer = None
- if layer:
- mapLayer = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer']
- name = mapLayer.GetName()
- type = mapLayer.GetType()
- self.selectionInput.SetValue(name)
- self.inmap = name
-
- def _createMapSelection(self, parent):
- """!Create map selection part of dialog"""
- # top controls
- if self.mapType == 'raster':
- maplabel = _('Select raster map:')
- else:
- maplabel = _('Select vector map:')
- inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
- label = " %s " % maplabel)
- inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
- self.selectionInput = Select(parent, id = wx.ID_ANY,
- size = globalvar.DIALOG_GSELECT_SIZE,
- type = self.mapType)
- # layout
- inputSizer.Add(item = self.selectionInput,
- flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
- return inputSizer
-
- def _createFileSelection(self, parent):
- """!Create file (open/save rules) selection part of dialog"""
- inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
- label = " %s " % _("Import or export color table:"))
- inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
-
- self.loadRules = filebrowse.FileBrowseButton(parent = parent, id = wx.ID_ANY, fileMask = '*',
- size = globalvar.DIALOG_GSELECT_SIZE,
- labelText = _('Load color table from file:'),
- dialogTitle = _('Choose file to load color table'),
- buttonText = _('Load'),
- toolTip = _("Type filename or click to choose "
- "file and load color table"),
- startDirectory = os.getcwd(), fileMode = wx.OPEN,
- changeCallback = self.OnLoadRulesFile)
- self.saveRules = filebrowse.FileBrowseButton(parent = parent, id = wx.ID_ANY, fileMask = '*',
- size = globalvar.DIALOG_GSELECT_SIZE,
- labelText = _('Save color table to file:'),
- dialogTitle = _('Choose file to save color table'),
- toolTip = _("Type filename or click to choose "
- "file and save color table"),
- buttonText = _('Save'),
- startDirectory = os.getcwd(), fileMode = wx.SAVE,
- changeCallback = self.OnSaveRulesFile)
-
- default = wx.Button(parent = parent, id = wx.ID_ANY, label = _("Reload default table"))
- # layout
- sizer = wx.BoxSizer(wx.HORIZONTAL)
- sizer.Add(item = self.loadRules, proportion = 1,
- flag = wx.RIGHT | wx.EXPAND, border = 10)
- sizer.Add(item = default, flag = wx.ALIGN_CENTER_VERTICAL)
- inputSizer.Add(item = sizer,
- flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
- sizer = wx.BoxSizer(wx.HORIZONTAL)
- sizer.Add(item = self.saveRules, proportion = 1,
- flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
- inputSizer.Add(item = sizer, proportion = 1,
- flag = wx.ALL | wx.EXPAND, border = 5)
-
- default.Bind(wx.EVT_BUTTON, self.OnLoadDefaultTable)
-
- if self.mapType == 'vector':
- # parent is collapsible pane
- parent.SetSizer(inputSizer)
-
- return inputSizer
-
- def _createPreview(self, parent):
- """!Create preview"""
- # initialize preview display
- self.InitDisplay()
- self.preview = BufferedWindow(parent, id = wx.ID_ANY, size = (400, 300),
- Map = self.Map)
- self.preview.EraseMap()
-
- def _createButtons(self):
- """!Create buttons for leaving dialog"""
- self.btnHelp = wx.Button(parent = self, id = wx.ID_HELP)
- self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
- self.btnApply = wx.Button(parent = self, id = wx.ID_APPLY)
- self.btnOK = wx.Button(parent = self, id = wx.ID_OK)
-
- self.btnOK.SetDefault()
- self.btnOK.Enable(False)
- self.btnApply.Enable(False)
-
- # layout
- btnSizer = wx.BoxSizer(wx.HORIZONTAL)
- btnSizer.Add(wx.Size(-1, -1), proportion = 1)
- btnSizer.Add(self.btnHelp,
- flag = wx.LEFT | wx.RIGHT, border = 5)
- btnSizer.Add(self.btnCancel,
- flag = wx.LEFT | wx.RIGHT, border = 5)
- btnSizer.Add(self.btnApply,
- flag = wx.LEFT | wx.RIGHT, border = 5)
- btnSizer.Add(self.btnOK,
- flag = wx.LEFT | wx.RIGHT, border = 5)
-
- return btnSizer
-
- def _createBody(self, parent):
- """!Create dialog body consisting of rules and preview"""
- bodySizer = wx.GridBagSizer(hgap = 5, vgap = 5)
- bodySizer.AddGrowableRow(1)
- bodySizer.AddGrowableCol(2)
- row = 0
- # label with range
- self.cr_label = wx.StaticText(parent, id = wx.ID_ANY)
- bodySizer.Add(item = self.cr_label, pos = (row, 0), span = (1, 3),
- flag = wx.ALL, border = 5)
- row += 1
- # color table
- self.rulesPanel = RulesPanel(parent = parent, mapType = self.mapType,
- attributeType = self.attributeType, properties = self.properties)
-
- bodySizer.Add(item = self.rulesPanel.mainPanel, pos = (row, 0),
- span = (1, 2), flag = wx.EXPAND)
- # add two rules as default
- self.rulesPanel.AddRules(2)
-
- # preview window
- self._createPreview(parent = parent)
- bodySizer.Add(item = self.preview, pos = (row, 2), flag = wx.EXPAND)
-
- row += 1
- # add ckeck all and clear all
- bodySizer.Add(item = self.rulesPanel.checkAll, flag = wx.ALIGN_CENTER_VERTICAL,
- pos = (row, 0))
- bodySizer.Add(item = self.rulesPanel.clearAll, pos = (row, 1))
-
- # preview button
- self.btnPreview = wx.Button(parent, id = wx.ID_ANY,
- label = _("Preview"))
- bodySizer.Add(item = self.btnPreview, pos = (row, 2),
- flag = wx.ALIGN_RIGHT)
- self.btnPreview.Enable(False)
- self.btnPreview.SetToolTipString(_("Show preview of map "
- "(current Map Display extent is used)."))
-
- row +=1
- # add rules button and spin to sizer
- bodySizer.Add(item = self.rulesPanel.numRules, pos = (row, 0),
- flag = wx.ALIGN_CENTER_VERTICAL)
- bodySizer.Add(item = self.rulesPanel.btnAdd, pos = (row, 1))
-
- return bodySizer
-
- def InitDisplay(self):
- """!Initialize preview display, set dimensions and region
- """
- self.width = self.Map.width = 400
- self.height = self.Map.height = 300
- self.Map.geom = self.width, self.height
- def OnCloseWindow(self, event):
- """!Window closed
- """
- self.OnCancel(event)
-
- def OnApply(self, event):
- """!Apply selected color table
-
- @return True on success otherwise False
- """
- ret = self.CreateColorTable()
- if not ret:
- GMessage(parent = self, message = _("No valid color rules given."))
-
- if ret:
- display = self.parent.GetLayerTree().GetMapDisplay()
- if display and display.IsAutoRendered():
- display.GetWindow().UpdateMap(render = True)
-
- return ret
- def OnOK(self, event):
- """!Apply selected color table and close the dialog"""
- if self.OnApply(event):
- self.OnCancel(event)
-
- def OnCancel(self, event):
- """!Do not apply any changes, remove associated
- rendered images and close the dialog"""
- self.Map.Clean()
- self.Destroy()
-
- def OnSaveRulesFile(self, event):
- """!Save color table to file"""
- path = event.GetString()
- if not os.path.exists(path):
- return
-
- rulestxt = ''
- for rule in self.rulesPanel.ruleslines.itervalues():
- if 'value' not in rule:
- continue
- rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
- if not rulestxt:
- GMessage(message = _("Nothing to save."),
- parent = self)
- return
-
- fd = open(path, 'w')
- fd.write(rulestxt)
- fd.close()
-
- def OnLoadRulesFile(self, event):
- """!Load color table from file"""
- path = event.GetString()
- if not os.path.exists(path):
- return
-
- self.rulesPanel.Clear()
-
- file = open(path, 'r')
- ctable = file.read()
- self.ReadColorTable(ctable = ctable)
-
- def ReadColorTable(self, ctable):
- """!Read color table
-
- @param table color table in format coming from r.colors.out"""
-
- rulesNumber = len(ctable.splitlines())
- self.rulesPanel.AddRules(rulesNumber)
-
- minim = maxim = count = 0
- for line in ctable.splitlines():
- try:
- value, color = map(lambda x: x.strip(), line.split(' '))
- except ValueError:
- GMessage(parent = self, message = _("Invalid color table format"))
- self.rulesPanel.Clear()
- return
-
- self.rulesPanel.ruleslines[count]['value'] = value
- self.rulesPanel.ruleslines[count]['color'] = color
- self.rulesPanel.mainPanel.FindWindowById(count + 1000).SetValue(value)
- rgb = list()
- for c in color.split(':'):
- rgb.append(int(c))
- self.rulesPanel.mainPanel.FindWindowById(count + 2000).SetColour(rgb)
- # range
- try:
- if float(value) < minim:
- minim = float(value)
- if float(value) > maxim:
- maxim = float(value)
- except ValueError: # nv, default
- pass
- count += 1
-
- if self.mapType == 'vector':
- # raster min, max is known from r.info
- self.properties['min'], self.properties['max'] = minim, maxim
- self.SetRangeLabel()
-
- self.OnPreview(tmp = True)
-
- def OnLoadDefaultTable(self, event):
- """!Load internal color table"""
- self.LoadTable()
-
- def LoadTable(self, mapType = 'raster'):
- """!Load current color table (using `r(v).colors.out`)
-
- @param mapType map type (raster or vector)"""
- self.rulesPanel.Clear()
- if mapType == 'raster':
- cmd = ['r.colors.out',
- 'read=True',
- 'map=%s' % self.inmap,
- 'rules=-']
- else:
- cmd = ['v.colors.out',
- 'read=True',
- 'map=%s' % self.inmap,
- 'rules=-']
-
- if self.properties['sourceColumn'] and self.properties['sourceColumn'] != 'cat':
- cmd.append('column=%s' % self.properties['sourceColumn'])
-
- cmd = utils.CmdToTuple(cmd)
-
- if self.inmap:
- ctable = RunCommand(cmd[0], **cmd[1])
- else:
- self.OnPreview()
- return
-
- self.ReadColorTable(ctable = ctable)
-
- def CreateColorTable(self, tmp = False):
- """!Creates color table
- @return True on success
- @return False on failure
- """
- rulestxt = ''
-
- for rule in self.rulesPanel.ruleslines.itervalues():
- if 'value' not in rule: # skip empty rules
- continue
-
- if rule['value'] not in ('nv', 'default') and \
- rule['value'][-1] != '%' and \
- not self._IsNumber(rule['value']):
- GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
- parent = self)
- return False
-
- rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
-
- if not rulestxt:
- return False
-
- gtemp = utils.GetTempfile()
- output = open(gtemp, "w")
- try:
- output.write(rulestxt)
- finally:
- output.close()
-
- cmd = ['%s.colors' % self.mapType[0],#r.colors/v.colors
- 'map=%s' % self.inmap,
- 'rules=%s' % gtemp]
- if self.mapType == 'vector' and self.properties['sourceColumn']\
- and self.properties['sourceColumn'] != 'cat':
- cmd.append('column=%s' % self.properties['sourceColumn'])
- cmd = utils.CmdToTuple(cmd)
- ret = RunCommand(cmd[0], **cmd[1])
- if ret != 0:
- return False
-
- return True
-
- def DoPreview(self, ltype, cmdlist):
- """!Update preview (based on computational region)"""
-
- if not self.layer:
- self.layer = self.Map.AddLayer(type = ltype, name = 'preview', command = cmdlist,
- l_active = True, l_hidden = False, l_opacity = 1.0,
- l_render = False)
- else:
- self.layer.SetCmd(cmdlist)
-
- # apply new color table and display preview
- self.CreateColorTable(tmp = True)
- self.preview.UpdatePreview()
-
- def RunHelp(self, cmd):
- """!Show GRASS manual page"""
- RunCommand('g.manual',
- quiet = True,
- parent = self,
- entry = cmd)
-
- def _IsNumber(self, s):
- """!Check if 's' is a number"""
- try:
- float(s)
- return True
- except ValueError:
- return False
-
- class RasterColorTable(ColorTable):
- def __init__(self, parent, **kwargs):
- """!Dialog for interactively entering color rules for raster maps"""
- self.mapType = 'raster'
- self.attributeType = 'color'
- self.colorTable = True
- # raster properties
- self.properties = {
- # min cat in raster map
- 'min' : None,
- # max cat in raster map
- 'max' : None,
- }
-
- ColorTable.__init__(self, parent,
- title = _('Create new color table for raster map'), **kwargs)
-
- self._initLayer()
-
- self.SetMinSize(self.GetSize())
- self.CentreOnScreen()
- self.Show()
-
- def _doLayout(self):
- """!Do main layout"""
- sizer = wx.BoxSizer(wx.VERTICAL)
- #
- # map selection
- #
- mapSelection = self._createMapSelection(parent = self)
- sizer.Add(item = mapSelection, proportion = 0,
- flag = wx.ALL | wx.EXPAND, border = 5)
- #
- # manage extern tables
- #
- fileSelection = self._createFileSelection(parent = self)
- sizer.Add(item = fileSelection, proportion = 0,
- flag = wx.ALL | wx.EXPAND, border = 5)
- #
- # body & preview
- #
- bodySizer = self._createBody(parent = self)
- sizer.Add(item = bodySizer, proportion = 1,
- flag = wx.ALL | wx.EXPAND, border = 5)
- #
- # buttons
- #
- btnSizer = self._createButtons()
- sizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
- style = wx.LI_HORIZONTAL), proportion = 0,
- flag = wx.EXPAND | wx.ALL, border = 5)
-
- sizer.Add(item = btnSizer, proportion = 0,
- flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
-
- self.SetSizer(sizer)
- sizer.Layout()
- sizer.Fit(self)
- self.Layout()
-
- def OnSelectionInput(self, event):
- """!Raster map selected"""
- if event:
- self.inmap = event.GetString()
-
- self.loadRules.SetValue('')
- self.saveRules.SetValue('')
- if self.inmap:
- if not grass.find_file(name = self.inmap, element = 'cell')['file']:
- self.inmap = None
-
- if not self.inmap:
- self.btnPreview.Enable(False)
- self.btnOK.Enable(False)
- self.btnApply.Enable(False)
- self.LoadTable()
- return
-
- info = grass.raster_info(map = self.inmap)
-
- if info:
- self.properties['min'] = info['min']
- self.properties['max'] = info['max']
- self.LoadTable()
- else:
- self.inmap = ''
- self.properties['min'] = self.properties['max'] = None
- self.btnPreview.Enable(False)
- self.btnOK.Enable(False)
- self.btnApply.Enable(False)
- self.preview.EraseMap()
- self.cr_label.SetLabel(_('Enter raster category values or percents'))
- return
-
- if info['datatype'] == 'CELL':
- mapRange = _('range')
- else:
- mapRange = _('fp range')
- self.cr_label.SetLabel(_('Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') %
- { 'range' : mapRange,
- 'min' : self.properties['min'],
- 'max' : self.properties['max'] })
-
- self.btnPreview.Enable()
- self.btnOK.Enable()
- self.btnApply.Enable()
-
-
- def OnPreview(self, tmp = True):
- """!Update preview (based on computational region)"""
- if not self.inmap:
- self.preview.EraseMap()
- return
-
- cmdlist = ['d.rast',
- 'map=%s' % self.inmap]
- ltype = 'raster'
-
- # find existing color table and copy to temp file
- try:
- name, mapset = self.inmap.split('@')
- except ValueError:
- name = self.inmap
- mapset = grass.find_file(self.inmap, element = 'cell')['mapset']
- if not mapset:
- return
- old_colrtable = None
- if mapset == grass.gisenv()['MAPSET']:
- old_colrtable = grass.find_file(name = name, element = 'colr')['file']
- else:
- old_colrtable = grass.find_file(name = name, element = 'colr2/' + mapset)['file']
-
- if old_colrtable:
- colrtemp = utils.GetTempfile()
- shutil.copyfile(old_colrtable, colrtemp)
-
- ColorTable.DoPreview(self, ltype, cmdlist)
-
- # restore previous color table
- if tmp:
- if old_colrtable:
- shutil.copyfile(colrtemp, old_colrtable)
- os.remove(colrtemp)
- else:
- RunCommand('r.colors',
- parent = self,
- flags = 'r',
- map = self.inmap)
-
- def OnHelp(self, event):
- """!Show GRASS manual page"""
- cmd = 'r.colors'
- ColorTable.RunHelp(self, cmd = cmd)
-
-
- class VectorColorTable(ColorTable):
- def __init__(self, parent, attributeType, **kwargs):
- """!Dialog for interactively entering color rules for vector maps"""
- # dialog attributes
- self.mapType = 'vector'
- self.attributeType = attributeType # color, size, width
- # in version 7 v.colors used, otherwise color column only
- self.version7 = int(grass.version()['version'].split('.')[0]) >= 7
- self.colorTable = False
- self.updateColumn = True
- # vector properties
- self.properties = {
- # vector layer for attribute table to use for setting color
- 'layer' : 1,
- # vector attribute table used for setting color
- 'table' : '',
- # vector attribute column for assigning colors
- 'sourceColumn' : '',
- # vector attribute column to use for loading colors
- 'loadColumn' : '',
- # vector attribute column to use for storing colors
- 'storeColumn' : '',
- # vector attribute column for temporary storing colors
- 'tmpColumn' : 'tmp_0',
- # min value of attribute column/vector color table
- 'min': None,
- # max value of attribute column/vector color table
- 'max': None
- }
- self.columnsProp = {'color': {'name': 'GRASSRGB', 'type1': 'varchar(11)', 'type2': ['character']},
- 'size' : {'name': 'GRASSSIZE', 'type1': 'integer', 'type2': ['integer']},
- 'width': {'name': 'GRASSWIDTH', 'type1': 'integer', 'type2': ['integer']}}
- ColorTable.__init__(self, parent = parent,
- title = _('Create new color rules for vector map'), **kwargs)
-
- # additional bindings for vector color management
- self.Bind(wx.EVT_COMBOBOX, self.OnLayerSelection, self.layerSelect)
- self.Bind(wx.EVT_COMBOBOX, self.OnSourceColumnSelection, self.sourceColumn)
- self.Bind(wx.EVT_COMBOBOX, self.OnFromColSelection, self.fromColumn)
- self.Bind(wx.EVT_COMBOBOX, self.OnToColSelection, self.toColumn)
- self.Bind(wx.EVT_BUTTON, self.OnAddColumn, self.addColumn)
-
- self._initLayer()
- if self.colorTable:
- self.cr_label.SetLabel(_("Enter vector attribute values or percents:"))
- else:
- self.cr_label.SetLabel(_("Enter vector attribute values:"))
- self.SetMinSize(self.GetSize())
- self.CentreOnScreen()
- self.SetSize((-1, 760))
- self.Show()
-
- def _createVectorAttrb(self, parent):
- """!Create part of dialog with layer/column selection"""
- inputBox = wx.StaticBox(parent = parent, id = wx.ID_ANY,
- label = " %s " % _("Select vector columns"))
- cb_vl_label = wx.StaticText(parent, id = wx.ID_ANY,
- label = _('Layer:'))
- cb_vc_label = wx.StaticText(parent, id = wx.ID_ANY,
- label = _('Attribute column:'))
-
- if self.attributeType == 'color':
- labels = [_("Load color from column:"), _("Save color to column:")]
- elif self.attributeType == 'size':
- labels = [_("Load size from column:"), _("Save size to column:")]
- elif self.attributeType == 'width':
- labels = [_("Load width from column:"), _("Save width to column:")]
-
- if self.version7 and self.attributeType == 'color':
- self.useColumn = wx.CheckBox(parent, id = wx.ID_ANY,
- label = _("Use color column instead of color table:"))
- self.useColumn.Bind(wx.EVT_CHECKBOX, self.OnCheckColumn)
-
- fromColumnLabel = wx.StaticText(parent, id = wx.ID_ANY,
- label = labels[0])
- toColumnLabel = wx.StaticText(parent, id = wx.ID_ANY,
- label = labels[1])
-
- self.rgb_range_label = wx.StaticText(parent, id = wx.ID_ANY)
- self.layerSelect = LayerSelect(parent)
- self.sourceColumn = ColumnSelect(parent)
- self.fromColumn = ColumnSelect(parent)
- self.toColumn = ColumnSelect(parent)
- self.addColumn = wx.Button(parent, id = wx.ID_ANY,
- label = _('Add column'))
- self.addColumn.SetToolTipString(_("Add GRASSRGB column to current attribute table."))
-
- # layout
- inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
- vSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
- row = 0
- vSizer.Add(cb_vl_label, pos = (row, 0),
- flag = wx.ALIGN_CENTER_VERTICAL)
- vSizer.Add(self.layerSelect, pos = (row, 1),
- flag = wx.ALIGN_CENTER_VERTICAL)
- row += 1
- vSizer.Add(cb_vc_label, pos = (row, 0),
- flag = wx.ALIGN_CENTER_VERTICAL)
- vSizer.Add(self.sourceColumn, pos = (row, 1),
- flag = wx.ALIGN_CENTER_VERTICAL)
- vSizer.Add(self.rgb_range_label, pos = (row, 2),
- flag = wx.ALIGN_CENTER_VERTICAL)
- row += 1
- if self.version7 and self.attributeType == 'color':
- vSizer.Add(self.useColumn, pos = (row, 0), span = (1, 2),
- flag = wx.ALIGN_CENTER_VERTICAL)
- row += 1
-
- vSizer.Add(fromColumnLabel, pos = (row, 0),
- flag = wx.ALIGN_CENTER_VERTICAL)
- vSizer.Add(self.fromColumn, pos = (row, 1),
- flag = wx.ALIGN_CENTER_VERTICAL)
- row += 1
- vSizer.Add(toColumnLabel, pos = (row, 0),
- flag = wx.ALIGN_CENTER_VERTICAL)
- vSizer.Add(self.toColumn, pos = (row, 1),
- flag = wx.ALIGN_CENTER_VERTICAL)
- vSizer.Add(self.addColumn, pos = (row, 2),
- flag = wx.ALIGN_CENTER_VERTICAL)
- inputSizer.Add(item = vSizer,
- flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
- self.colorColumnSizer = vSizer
- return inputSizer
-
- def _doLayout(self):
- """!Do main layout"""
- scrollPanel = scrolled.ScrolledPanel(self, id = wx.ID_ANY, size = (650, 500),
- style = wx.TAB_TRAVERSAL)
- scrollPanel.SetupScrolling()
- sizer = wx.BoxSizer(wx.VERTICAL)
- #
- # map selection
- #
- mapSelection = self._createMapSelection(parent = scrollPanel)
- sizer.Add(item = mapSelection, proportion = 0,
- flag = wx.ALL | wx.EXPAND, border = 5)
- #
- # manage extern tables
- #
- if self.version7 and self.attributeType == 'color':
- self.cp = wx.CollapsiblePane(scrollPanel, label = _("Import or export color table"),
- winid = wx.ID_ANY,
- style = wx.CP_DEFAULT_STYLE|wx.CP_NO_TLW_RESIZE)
- self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged, self.cp)
-
- self._createFileSelection(parent = self.cp.GetPane())
- sizer.Add(item = self.cp, proportion = 0,
- flag = wx.ALL | wx.EXPAND, border = 5)
- #
- # set vector attributes
- #
- vectorAttrb = self._createVectorAttrb(parent = scrollPanel)
- sizer.Add(item = vectorAttrb, proportion = 0,
- flag = wx.ALL | wx.EXPAND, border = 5)
- #
- # body & preview
- #
- bodySizer = self._createBody(parent = scrollPanel)
- sizer.Add(item = bodySizer, proportion = 1,
- flag = wx.ALL | wx.EXPAND, border = 5)
-
- scrollPanel.SetSizer(sizer)
- scrollPanel.Fit()
-
- #
- # buttons
- #
- btnSizer = self._createButtons()
-
- mainsizer = wx.BoxSizer(wx.VERTICAL)
- mainsizer.Add(scrollPanel, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
- mainsizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
- style = wx.LI_HORIZONTAL), proportion = 0,
- flag = wx.EXPAND | wx.ALL, border = 5)
- mainsizer.Add(item = btnSizer, proportion = 0,
- flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND, border = 5)
-
- self.SetSizer(mainsizer)
- mainsizer.Layout()
- mainsizer.Fit(self)
-
- def OnPaneChanged(self, event = None):
- # redo the layout
- self.Layout()
- # and also change the labels
- if self.cp.IsExpanded():
- self.cp.SetLabel('')
- else:
- self.cp.SetLabel(_("Import or export color table"))
-
- def CheckMapset(self):
- """!Check if current vector is in current mapset"""
- if grass.find_file(name = self.inmap,
- element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
- return True
- else:
- return False
-
- def NoConnection(self, vectorName):
- dlg = wx.MessageDialog(parent = self,
- message = _("Database connection for vector map <%s> "
- "is not defined in DB file. Do you want to create and "
- "connect new attribute table?") % vectorName,
- caption = _("No database connection defined"),
- style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
- if dlg.ShowModal() == wx.ID_YES:
- dlg.Destroy()
- GUI(parent = self).ParseCommand(['v.db.addtable', 'map=' + self.inmap],
- completed = (self.CreateAttrTable, self.inmap, ''))
- else:
- dlg.Destroy()
-
- def OnCheckColumn(self, event):
- """!Use color column instead of color table"""
- if self.useColumn.GetValue():
- self.properties['loadColumn'] = self.fromColumn.GetStringSelection()
- self.properties['storeColumn'] = self.toColumn.GetStringSelection()
- self.fromColumn.Enable(True)
- self.toColumn.Enable(True)
- self.colorTable = False
-
- if self.properties['loadColumn']:
- self.LoadTable()
- else:
- self.rulesPanel.Clear()
- else:
- self.properties['loadColumn'] = ''
- self.properties['storeColumn'] = ''
- self.fromColumn.Enable(False)
- self.toColumn.Enable(False)
- self.colorTable = True
- self.LoadTable()
-
- def EnableVectorAttributes(self, enable):
- """!Enable/disable part of dialog connected with db"""
- for child in self.colorColumnSizer.GetChildren():
- child.GetWindow().Enable(enable)
-
- def DisableClearAll(self):
- """!Enable, disable the whole dialog"""
- self.rulesPanel.Clear()
- self.EnableVectorAttributes(False)
- self.btnPreview.Enable(False)
- self.btnOK.Enable(False)
- self.btnApply.Enable(False)
- self.preview.EraseMap()
-
- def OnSelectionInput(self, event):
- """!Vector map selected"""
- if event:
- if self.inmap:
- # switch to another map -> delete temporary column
- self.DeleteTemporaryColumn()
- self.inmap = event.GetString()
-
- if self.version7 and self.attributeType == 'color':
- self.loadRules.SetValue('')
- self.saveRules.SetValue('')
-
- if self.inmap:
- if not grass.find_file(name = self.inmap, element = 'vector')['file']:
- self.inmap = None
-
- self.UpdateDialog()
-
- def UpdateDialog(self):
- """!Update dialog after map selection"""
-
- if not self.inmap:
- self.DisableClearAll()
- return
-
- if not self.CheckMapset():
- # v.colors doesn't need the map to be in current mapset
- if not (self.version7 and self.attributeType == 'color'):
- message = _("Selected map <%s> is not in current mapset <%s>. "
- "Attribute table cannot be edited.") % \
- (self.inmap, grass.gisenv()['MAPSET'])
- wx.CallAfter(GMessage, parent = self, message = message)
- self.DisableClearAll()
- return
-
- # check for db connection
- self.dbInfo = VectorDBInfo(self.inmap)
- enable = True
- if not len(self.dbInfo.layers): # no connection
- if not (self.version7 and self.attributeType == 'color'): # otherwise it doesn't matter
- wx.CallAfter(self.NoConnection, self.inmap)
- enable = False
- for combo in (self.layerSelect, self.sourceColumn, self.fromColumn, self.toColumn):
- combo.SetValue("")
- combo.Clear()
- for prop in ('sourceColumn', 'loadColumn', 'storeColumn'):
- self.properties[prop] = ''
- self.EnableVectorAttributes(False)
- else: # db connection exist
- # initialize layer selection combobox
- self.EnableVectorAttributes(True)
- self.layerSelect.InsertLayers(self.inmap)
- # initialize attribute table for layer=1
- self.properties['layer'] = self.layerSelect.GetString(0)
- self.layerSelect.SetStringSelection(self.properties['layer'])
- layer = int(self.properties['layer'])
- self.properties['table'] = self.dbInfo.layers[layer]['table']
-
- if self.attributeType == 'color':
- self.AddTemporaryColumn(type = 'varchar(11)')
- else:
- self.AddTemporaryColumn(type = 'integer')
-
- # initialize column selection comboboxes
-
- self.OnLayerSelection(event = None)
- if self.version7 and self.attributeType == 'color':
- self.useColumn.SetValue(False)
- self.OnCheckColumn(event = None)
- self.useColumn.Enable(self.CheckMapset())
-
- self.LoadTable()
-
- self.btnPreview.Enable(enable)
- self.btnOK.Enable(enable)
- self.btnApply.Enable(enable)
-
- def AddTemporaryColumn(self, type):
- """!Add temporary column to not overwrite the original values,
- need to be deleted when closing dialog and unloading map
-
- @param type type of column (e.g. vachar(11))"""
- if not self.CheckMapset():
- return
- # because more than one dialog with the same map can be opened we must test column name and
- # create another one
- while self.properties['tmpColumn'] in self.dbInfo.GetTableDesc(self.properties['table']).keys():
- name, idx = self.properties['tmpColumn'].split('_')
- idx = int(idx)
- idx += 1
- self.properties['tmpColumn'] = name + '_' + str(idx)
-
- if self.version7:
- modul = 'v.db.addcolumn'
- else:
- modul = 'v.db.addcol'
- ret = RunCommand(modul,
- parent = self,
- map = self.inmap,
- layer = self.properties['layer'],
- column = '%s %s' % (self.properties['tmpColumn'], type))
-
- def DeleteTemporaryColumn(self):
- """!Delete temporary column"""
- if not self.CheckMapset():
- return
-
- if self.inmap:
- if self.version7:
- modul = 'v.db.dropcolumn'
- else:
- modul = 'v.db.dropcol'
- ret = RunCommand(modul,
- map = self.inmap,
- layer = self.properties['layer'],
- column = self.properties['tmpColumn'])
-
- def OnLayerSelection(self, event):
- # reset choices in column selection comboboxes if layer changes
- vlayer = int(self.layerSelect.GetStringSelection())
- self.sourceColumn.InsertColumns(vector = self.inmap, layer = vlayer,
- type = ['integer', 'double precision'], dbInfo = self.dbInfo,
- excludeCols = ['tmpColumn'])
- self.sourceColumn.SetStringSelection('cat')
- self.properties['sourceColumn'] = self.sourceColumn.GetString(0)
-
- if self.attributeType == 'color':
- type = ['character']
- else:
- type = ['integer']
- self.fromColumn.InsertColumns(vector = self.inmap, layer = vlayer, type = type,
- dbInfo = self.dbInfo, excludeCols = ['tmpColumn'])
- self.toColumn.InsertColumns(vector = self.inmap, layer = vlayer, type = type,
- dbInfo = self.dbInfo, excludeCols = ['tmpColumn'])
-
- found = self.fromColumn.FindString(self.columnsProp[self.attributeType]['name'])
- if found != wx.NOT_FOUND:
- self.fromColumn.SetSelection(found)
- self.toColumn.SetSelection(found)
- self.properties['loadColumn'] = self.fromColumn.GetString(found)
- self.properties['storeColumn'] = self.toColumn.GetString(found)
- else:
- self.properties['loadColumn'] = ''
- self.properties['storeColumn'] = ''
-
- if event:
- self.LoadTable()
- self.Update()
-
- def OnSourceColumnSelection(self, event):
- self.properties['sourceColumn'] = event.GetString()
-
- self.LoadTable()
-
- def OnAddColumn(self, event):
- """!Add GRASS(RGB,SIZE,WIDTH) column if it doesn't exist"""
- if self.columnsProp[self.attributeType]['name'] not in self.fromColumn.GetItems():
- if self.version7:
- modul = 'v.db.addcolumn'
- else:
- modul = 'v.db.addcol'
- ret = RunCommand(modul,
- map = self.inmap,
- layer = self.properties['layer'],
- columns = '%s %s' % (self.columnsProp[self.attributeType]['name'],
- self.columnsProp[self.attributeType]['type1']))
- self.toColumn.InsertColumns(self.inmap, self.properties['layer'],
- type = self.columnsProp[self.attributeType]['type2'])
- self.toColumn.SetStringSelection(self.columnsProp[self.attributeType]['name'])
- self.properties['storeColumn'] = self.toColumn.GetStringSelection()
-
- self.LoadTable()
- else:
- GMessage(parent = self,
- message = _("%s column already exists.") % \
- self.columnsProp[self.attributeType]['name'])
-
- def CreateAttrTable(self, dcmd, layer, params, propwin):
- """!Create attribute table"""
- if dcmd:
- cmd = utils.CmdToTuple(dcmd)
- ret = RunCommand(cmd[0], **cmd[1])
- if ret == 0:
- self.OnSelectionInput(None)
- return True
-
- for combo in (self.layerSelect, self.sourceColumn, self.fromColumn, self.toColumn):
- combo.SetValue("")
- combo.Disable()
- return False
-
- def LoadTable(self):
- """!Load table"""
- if self.colorTable:
- ColorTable.LoadTable(self, mapType = 'vector')
- else:
- self.LoadRulesFromColumn()
-
- def LoadRulesFromColumn(self):
- """!Load current column (GRASSRGB, size column)"""
-
- self.rulesPanel.Clear()
- if not self.properties['sourceColumn']:
- self.preview.EraseMap()
- return
-
- busy = wx.BusyInfo(message = _("Please wait, loading data from attribute table..."),
- parent = self)
- wx.Yield()
-
- columns = self.properties['sourceColumn']
- if self.properties['loadColumn']:
- columns += ',' + self.properties['loadColumn']
-
- if self.inmap:
- outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
- sep = '|'
- ret = RunCommand('v.db.select',
- quiet = True,
- flags = 'c',
- map = self.inmap,
- layer = self.properties['layer'],
- columns = columns,
- fs = sep,
- stdout = outFile)
- else:
- self.preview.EraseMap()
- busy.Destroy()
- return
-
- outFile.seek(0)
- i = 0
- minim = maxim = 0.0
- limit = 1000
-
- colvallist = []
- readvals = False
-
- while True:
- # os.linesep doesn't work here (MSYS)
- record = outFile.readline().replace('\n', '')
- if not record:
- break
- self.rulesPanel.ruleslines[i] = {}
-
- if not self.properties['loadColumn']:
- col1 = record
- col2 = None
- else:
- col1, col2 = record.split(sep)
-
- if float(col1) < minim:
- minim = float(col1)
- if float(col1) > maxim:
- maxim = float(col1)
-
- # color rules list should only have unique values of col1, not all records
- if col1 not in colvallist:
- self.rulesPanel.ruleslines[i]['value'] = col1
- self.rulesPanel.ruleslines[i][self.attributeType] = col2
- colvallist.append(col1)
- i += 1
-
- if i > limit and readvals == False:
- dlg = wx.MessageDialog (parent = self, message = _(
- "Number of loaded records reached %d, "
- "displaying all the records will be time-consuming "
- "and may lead to computer freezing, "
- "do you still want to continue?") % i,
- caption = _("Too many records"),
- style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
- if dlg.ShowModal() == wx.ID_YES:
- readvals = True
- dlg.Destroy()
- else:
- busy.Destroy()
- dlg.Destroy()
- self.updateColumn = False
- return
-
- self.rulesPanel.AddRules(i, start = True)
- ret = self.rulesPanel.LoadRules()
-
- self.properties['min'], self.properties['max'] = minim, maxim
- self.SetRangeLabel()
-
- if ret:
- self.OnPreview()
- else:
- self.rulesPanel.Clear()
-
- busy.Destroy()
-
- def SetRangeLabel(self):
- """!Set labels with info about attribute column range"""
-
- if self.properties['sourceColumn']:
- ctype = self.dbInfo.GetTableDesc(self.properties['table'])[self.properties['sourceColumn']]['ctype']
- else:
- ctype = int
-
- range = ''
- if self.properties['min'] or self.properties['max']:
- if ctype == float:
- range = _("(range: %.1f to %.1f)") % (self.properties['min'], self.properties['max'])
- elif ctype == int:
- range = _("(range: %d to %d)") % (self.properties['min'], self.properties['max'])
- if range:
- if self.colorTable:
- self.cr_label.SetLabel(_("Enter vector attribute values or percents %s:") % range)
- else:
- self.cr_label.SetLabel(_("Enter vector attribute values %s:") % range)
- else:
- if self.colorTable:
- self.cr_label.SetLabel(_("Enter vector attribute values or percents:"))
- else:
- self.cr_label.SetLabel(_("Enter vector attribute values:"))
-
- def OnFromColSelection(self, event):
- """!Selection in combobox (for loading values) changed"""
- self.properties['loadColumn'] = event.GetString()
-
- self.LoadTable()
-
- def OnToColSelection(self, event):
- """!Selection in combobox (for storing values) changed"""
- self.properties['storeColumn'] = event.GetString()
-
- def OnPreview(self, event = None, tmp = True):
- """!Update preview (based on computational region)"""
- if self.colorTable:
- self.OnTablePreview(tmp)
- else:
- self.OnColumnPreview()
-
- def OnTablePreview(self, tmp):
- """!Update preview (based on computational region)"""
- if not self.inmap:
- self.preview.EraseMap()
- return
-
- ltype = 'vector'
- cmdlist = ['d.vect',
- 'map=%s' % self.inmap]
-
- # find existing color table and copy to temp file
- try:
- name, mapset = self.inmap.split('@')
- except ValueError:
- name = self.inmap
- mapset = grass.find_file(self.inmap, element = 'cell')['mapset']
- if not mapset:
- return
-
- old_colrtable = None
- if mapset == grass.gisenv()['MAPSET']:
- old_colrtable = grass.find_file(name = 'colr', element = os.path.join('vector', name))['file']
- else:
- old_colrtable = grass.find_file(name = name, element = os.path.join('vcolr2', mapset))['file']
-
- if old_colrtable:
- colrtemp = utils.GetTempfile()
- shutil.copyfile(old_colrtable, colrtemp)
-
- ColorTable.DoPreview(self, ltype, cmdlist)
-
- # restore previous color table
- if tmp:
- if old_colrtable:
- shutil.copyfile(colrtemp, old_colrtable)
- os.remove(colrtemp)
- else:
- RunCommand('v.colors',
- parent = self,
- flags = 'r',
- map = self.inmap)
- def OnColumnPreview(self):
- """!Update preview (based on computational region)"""
- if not self.inmap or not self.properties['tmpColumn']:
- self.preview.EraseMap()
- return
-
- cmdlist = ['d.vect',
- 'map=%s' % self.inmap,
- 'type=point,line,boundary,area']
-
- if self.attributeType == 'color':
- cmdlist.append('flags=a')
- cmdlist.append('rgb_column=%s' % self.properties['tmpColumn'])
- elif self.attributeType == 'size':
- cmdlist.append('size_column=%s' % self.properties['tmpColumn'])
- elif self.attributeType == 'width':
- cmdlist.append('width_column=%s' % self.properties['tmpColumn'])
-
- ltype = 'vector'
-
- ColorTable.DoPreview(self, ltype, cmdlist)
-
- def OnHelp(self, event):
- """!Show GRASS manual page"""
- cmd = 'v.colors'
- ColorTable.RunHelp(self, cmd = cmd)
-
- def UseAttrColumn(self, useAttrColumn):
- """!Find layers and apply the changes in d.vect command"""
- layers = self.parent.curr_page.maptree.FindItemByData(key = 'name', value = self.inmap)
- if not layers:
- return
- for layer in layers:
- if self.parent.curr_page.maptree.GetPyData(layer)[0]['type'] != 'vector':
- continue
- cmdlist = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer'].GetCmd()
-
- if self.attributeType == 'color':
- if useAttrColumn:
- cmdlist[1].update({'flags': 'a'})
- cmdlist[1].update({'rgb_column': self.properties['storeColumn']})
- else:
- if 'flags' in cmdlist[1]:
- cmdlist[1]['flags'] = cmdlist[1]['flags'].replace('a', '')
- cmdlist[1].pop('rgb_column', None)
- elif self.attributeType == 'size':
- cmdlist[1].update({'size_column': self.properties['storeColumn']})
- elif self.attributeType == 'width':
- cmdlist[1].update({'width_column' :self.properties['storeColumn']})
- self.parent.curr_page.maptree.GetPyData(layer)[0]['cmd'] = cmdlist
-
- def CreateColorTable(self, tmp = False):
- """!Create color rules (color table or color column)"""
- if self.colorTable:
- ret = ColorTable.CreateColorTable(self)
- else:
- if self.updateColumn:
- ret = self.UpdateColorColumn(tmp)
- else:
- ret = True
- return ret
-
- def UpdateColorColumn(self, tmp):
- """!Creates color table
- @return True on success
- @return False on failure
- """
- rulestxt = ''
-
- for rule in self.rulesPanel.ruleslines.itervalues():
- if 'value' not in rule: # skip empty rules
- break
-
- if tmp:
- rgb_col = self.properties['tmpColumn']
- else:
- rgb_col = self.properties['storeColumn']
- if not self.properties['storeColumn']:
- GMessage(self.parent, message = _("Please select column to save values to."))
-
- rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
- rgb_col,
- rule[self.attributeType],
- rule['value'])
- if not rulestxt:
- return False
-
- gtemp = utils.GetTempfile()
- output = open(gtemp, "w")
- try:
- output.write(rulestxt)
- finally:
- output.close()
-
- RunCommand('db.execute',
- parent = self,
- input = gtemp)
- return True
-
- def OnCancel(self, event):
- """!Do not apply any changes and close the dialog"""
- self.DeleteTemporaryColumn()
- self.Map.Clean()
- self.Destroy()
- def OnApply(self, event):
- """!Apply selected color table
-
- @return True on success otherwise False
- """
- if self.colorTable:
- self.UseAttrColumn(False)
- else:
- self.UseAttrColumn(True)
-
- return ColorTable.OnApply(self, event)
-
- class ThematicVectorTable(VectorColorTable):
- def __init__(self, parent, vectorType, **kwargs):
- """!Dialog for interactively entering color/size rules
- for vector maps for thematic mapping in nviz"""
- self.vectorType = vectorType
- VectorColorTable.__init__(self, parent = parent, **kwargs)
-
- self.SetTitle(_("Thematic mapping for vector map in 3D view"))
-
- def _initLayer(self):
- """!Set initial layer when opening dialog"""
- self.inmap = self.parent.GetLayerData(nvizType = 'vector', nameOnly = True)
- self.selectionInput.SetValue(self.inmap)
- self.selectionInput.Disable()
-
- def OnApply(self, event):
- """!Apply selected color table
- @return True on success otherwise False
- """
- ret = self.CreateColorTable()
- if not ret:
- GMessage(parent = self, message = _("No valid color rules given."))
-
- data = self.parent.GetLayerData(nvizType = 'vector')
- data['vector']['points']['thematic']['layer'] = int(self.properties['layer'])
-
- value = None
- if self.properties['storeColumn']:
- value = self.properties['storeColumn']
-
- if not self.colorTable:
- if self.attributeType == 'color':
- data['vector'][self.vectorType]['thematic']['rgbcolumn'] = value
- else:
- data['vector'][self.vectorType]['thematic']['sizecolumn'] = value
- else:
- if self.attributeType == 'color':
- data['vector'][self.vectorType]['thematic']['rgbcolumn'] = None
- else:
- data['vector'][self.vectorType]['thematic']['sizecolumn'] = None
-
- data['vector'][self.vectorType]['thematic']['update'] = None
-
- event = wxUpdateProperties(data = data)
- wx.PostEvent(self.parent.mapWindow, event)
- self.parent.mapWindow.Refresh(False)
-
- return ret
-
- class BufferedWindow(wx.Window):
- """!A Buffered window class"""
- def __init__(self, parent, id,
- style = wx.NO_FULL_REPAINT_ON_RESIZE,
- Map = None, **kwargs):
-
- wx.Window.__init__(self, parent, id, style = style, **kwargs)
- self.parent = parent
- self.Map = Map
-
- # re-render the map from GRASS or just redraw image
- self.render = True
- # indicates whether or not a resize event has taken place
- self.resize = False
- #
- # event bindings
- #
- self.Bind(wx.EVT_PAINT, self.OnPaint)
- self.Bind(wx.EVT_IDLE, self.OnIdle)
- self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
- #
- # render output objects
- #
- # image file to be rendered
- self.mapfile = None
- # wx.Image object (self.mapfile)
- self.img = None
- self.pdc = wx.PseudoDC()
- # will store an off screen empty bitmap for saving to file
- self._Buffer = None
- # make sure that extents are updated at init
- self.Map.region = self.Map.GetRegion()
- self.Map.SetRegion()
- def Draw(self, pdc, img = None, pdctype = 'image'):
- """!Draws preview or clears window"""
- pdc.BeginDrawing()
- Debug.msg (3, "BufferedWindow.Draw(): pdctype=%s" % (pdctype))
- if pdctype == 'clear': # erase the display
- bg = wx.WHITE_BRUSH
- pdc.SetBackground(bg)
- pdc.Clear()
- self.Refresh()
- pdc.EndDrawing()
- return
- if pdctype == 'image' and img:
- bg = wx.TRANSPARENT_BRUSH
- pdc.SetBackground(bg)
- bitmap = wx.BitmapFromImage(img)
- w, h = bitmap.GetSize()
- pdc.DrawBitmap(bitmap, 0, 0, True) # draw the composite map
-
- pdc.EndDrawing()
- self.Refresh()
- def OnPaint(self, event):
- """!Draw pseudo DC to buffer"""
- self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
- dc = wx.BufferedPaintDC(self, self._Buffer)
-
- # use PrepareDC to set position correctly
- self.PrepareDC(dc)
-
- # we need to clear the dc BEFORE calling PrepareDC
- bg = wx.Brush(self.GetBackgroundColour())
- dc.SetBackground(bg)
- dc.Clear()
-
- # create a clipping rect from our position and size
- # and the Update Region
- rgn = self.GetUpdateRegion()
- r = rgn.GetBox()
-
- # draw to the dc using the calculated clipping rect
- self.pdc.DrawToDCClipped(dc, r)
-
- def OnSize(self, event):
- """!Init image size to match window size"""
- # set size of the input image
- self.Map.width, self.Map.height = self.GetClientSize()
- # Make new off screen bitmap: this bitmap will always have the
- # current drawing in it, so it can be used to save the image to
- # a file, or whatever.
- self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
- # get the image to be rendered
- self.img = self.GetImage()
- # update map display
- if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
- self.img = self.img.Scale(self.Map.width, self.Map.height)
- self.render = False
- self.UpdatePreview()
- # re-render image on idle
- self.resize = True
- def OnIdle(self, event):
- """!Only re-render a preview image from GRASS during
- idle time instead of multiple times during resizing.
- """
- if self.resize:
- self.render = True
- self.UpdatePreview()
- event.Skip()
- def GetImage(self):
- """!Converts files to wx.Image"""
- if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
- os.path.getsize(self.Map.mapfile):
- img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
- else:
- img = None
-
- return img
-
- def UpdatePreview(self, img = None):
- """!Update canvas if window changes geometry"""
- Debug.msg (2, "BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.render))
- oldfont = ""
- oldencoding = ""
-
- if self.render:
- # extent is taken from current map display
- try:
- self.Map.region = copy.deepcopy(self.parent.parent.curr_page.maptree.Map.region)
- except AttributeError:
- self.Map.region = self.Map.GetRegion()
- # render new map images
- self.mapfile = self.Map.Render(force = self.render)
- self.img = self.GetImage()
- self.resize = False
-
- if not self.img:
- return
-
- # paint images to PseudoDC
- self.pdc.Clear()
- self.pdc.RemoveAll()
- # draw map image background
- self.Draw(self.pdc, self.img, pdctype = 'image')
-
- self.resize = False
-
- def EraseMap(self):
- """!Erase preview"""
- self.Draw(self.pdc, pdctype = 'clear')
-
|