frame.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315
  1. """!
  2. @package iclass.frame
  3. @brief wxIClass frame with toolbar for digitizing training areas and
  4. for spectral signature analysis.
  5. Classes:
  6. - frame::IClassMapFrame
  7. - frame::MapManager
  8. (C) 2006-2013 by the GRASS Development Team
  9. This program is free software under the GNU General Public
  10. License (>=v2). Read the file COPYING that comes with GRASS
  11. for details.
  12. @author Vaclav Petras <wenzeslaus gmail.com>
  13. @author Anna Kratochvilova <kratochanna gmail.com>
  14. """
  15. import os
  16. import sys
  17. import copy
  18. import tempfile
  19. if __name__ == "__main__":
  20. gui_wx_path = os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython')
  21. if gui_wx_path not in sys.path:
  22. sys.path.append(gui_wx_path)
  23. from core import globalvar
  24. import wx
  25. from ctypes import *
  26. try:
  27. from grass.lib.imagery import *
  28. from grass.lib.vector import *
  29. haveIClass = True
  30. errMsg = ''
  31. except ImportError, e:
  32. haveIClass = False
  33. errMsg = _("Loading imagery lib failed.\n%s") % e
  34. import grass.script as grass
  35. from mapdisp import statusbar as sb
  36. from mapdisp.mapwindow import BufferedWindow
  37. from vdigit.toolbars import VDigitToolbar
  38. from gui_core.mapdisp import DoubleMapFrame
  39. from core.render import Map, MapLayer
  40. from core.gcmd import RunCommand, GMessage, GError, GWarning
  41. from gui_core.dialogs import SetOpacityDialog
  42. from dbmgr.vinfo import VectorDBInfo
  43. import grass.script as grass
  44. from iclass.digit import IClassVDigitWindow, IClassVDigit
  45. from iclass.toolbars import IClassMapToolbar, IClassMiscToolbar,\
  46. IClassToolbar, IClassMapManagerToolbar
  47. from iclass.statistics import Statistics, BandStatistics
  48. from iclass.dialogs import CategoryListCtrl, IClassCategoryManagerDialog,\
  49. IClassGroupDialog, IClassSignatureFileDialog,\
  50. IClassExportAreasDialog, IClassMapDialog
  51. from iclass.plots import PlotPanel
  52. class IClassMapFrame(DoubleMapFrame):
  53. """! wxIClass main frame
  54. It has two map windows one for digitizing training areas and one for
  55. result preview.
  56. It generates histograms, raster maps and signature files using
  57. @c I_iclass_* functions from C imagery library.
  58. It is wxGUI counterpart of old i.class module.
  59. """
  60. def __init__(self, parent = None, giface = None, title = _("GRASS GIS Supervised Classification Tool"),
  61. toolbars = ["iClassMisc", "iClassMap", "vdigit", "iClass"],
  62. size = (875, 600), name = 'IClassWindow', **kwargs):
  63. """!
  64. @param parent (no parent is expected)
  65. @param title window title
  66. @param toolbars dictionary of active toolbars (defalult value represents all toolbars)
  67. @param size default size
  68. """
  69. DoubleMapFrame.__init__(self, parent = parent, title = title,
  70. name = name,
  71. firstMap = Map(), secondMap = Map(),
  72. **kwargs)
  73. self._giface = giface
  74. self.firstMapWindow = IClassVDigitWindow(parent = self, giface = self._giface,
  75. map = self.firstMap, frame = self)
  76. self.secondMapWindow = BufferedWindow(parent = self, giface = self._giface,
  77. Map = self.secondMap, frame = self)
  78. self.MapWindow = self.firstMapWindow # current by default
  79. self._bindWindowsActivation()
  80. self._setUpMapWindow(self.firstMapWindow)
  81. self._setUpMapWindow(self.secondMapWindow)
  82. self.firstMapWindow.InitZoomHistory()
  83. self.secondMapWindow.InitZoomHistory()
  84. self.SetSize(size)
  85. #
  86. # Add toolbars
  87. #
  88. toolbarsCopy = toolbars[:]
  89. if sys.platform == 'win32':
  90. self.AddToolbar(toolbarsCopy.pop(1))
  91. toolbarsCopy.reverse()
  92. else:
  93. self.AddToolbar(toolbarsCopy.pop(0))
  94. for toolb in toolbarsCopy:
  95. self.AddToolbar(toolb)
  96. self.firstMapWindow.SetToolbar(self.toolbars['vdigit'])
  97. self.GetMapToolbar().GetActiveMapTool().Bind(wx.EVT_CHOICE, self.OnUpdateActive)
  98. #
  99. # Add statusbar
  100. #
  101. # items for choice
  102. self.statusbarItems = [sb.SbCoordinates,
  103. sb.SbRegionExtent,
  104. sb.SbCompRegionExtent,
  105. sb.SbShowRegion,
  106. sb.SbAlignExtent,
  107. sb.SbResolution,
  108. sb.SbDisplayGeometry,
  109. sb.SbMapScale,
  110. sb.SbGoTo,
  111. sb.SbProjection]
  112. # create statusbar and its manager
  113. statusbar = self.CreateStatusBar(number = 4, style = 0)
  114. statusbar.SetStatusWidths([-5, -2, -1, -1])
  115. self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
  116. # fill statusbar manager
  117. self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
  118. self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
  119. self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
  120. self.statusbarManager.Update()
  121. self.SetProperty('region', True) # show computation region by defaut
  122. self.trainingMapManager = MapManager(self, mapWindow = self.GetFirstWindow(),
  123. Map = self.GetFirstMap())
  124. self.previewMapManager = MapManager(self, mapWindow = self.GetSecondWindow(),
  125. Map = self.GetSecondMap())
  126. self.InitStatistics()
  127. self.changes = False
  128. self.exportVector = None
  129. # dialogs
  130. self.dialogs = dict()
  131. self.dialogs['classManager'] = None
  132. # just to make digitizer happy
  133. self.dialogs['attributes'] = None
  134. self.dialogs['category'] = None
  135. # PyPlot init
  136. self.plotPanel = PlotPanel(self, statDict = self.statisticsDict,
  137. statList = self.statisticsList)
  138. self._addPanes()
  139. self._mgr.Update()
  140. self.trainingMapManager.SetToolbar(self.toolbars['iClassTrainingMapManager'])
  141. self.previewMapManager.SetToolbar(self.toolbars['iClassPreviewMapManager'])
  142. # default action
  143. self.OnPan(event = None)
  144. wx.CallAfter(self.AddTrainingAreaMap)
  145. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  146. self.SendSizeEvent()
  147. def OnCloseWindow(self, event):
  148. self.GetFirstWindow().digit.GetDisplay().CloseMap()
  149. self.Destroy()
  150. def __del__(self):
  151. """! Frees C structs and removes vector map and all raster maps."""
  152. I_free_signatures(self.signatures)
  153. I_free_group_ref(self.refer)
  154. for st in self.cStatisticsDict.values():
  155. I_iclass_free_statistics(st)
  156. self.RemoveTempVector()
  157. for i in self.statisticsList:
  158. self.RemoveTempRaster(self.statisticsDict[i].rasterName)
  159. def OnHelp(self, event):
  160. """!Show help page"""
  161. self._giface.Help(entry = 'wxGUI.iclass')
  162. def CreateTempVector(self):
  163. """!Create temporary vector map for training areas"""
  164. vectorPath = grass.tempfile(create = False)
  165. vectorName = 'trAreas' + os.path.basename(vectorPath).replace('.','')
  166. cmd = ('v.edit', {'tool': 'create',
  167. 'map': vectorName})
  168. ret = RunCommand(prog = cmd[0],
  169. parent = self,
  170. overwrite = True,
  171. **cmd[1])
  172. if ret != 0:
  173. return False
  174. return vectorName
  175. def RemoveTempVector(self):
  176. """!Removes temporary vector map with training areas"""
  177. ret = RunCommand(prog = 'g.remove',
  178. parent = self,
  179. vect = self.trainingAreaVector)
  180. if ret != 0:
  181. return False
  182. return True
  183. def RemoveTempRaster(self, raster):
  184. """!Removes temporary raster maps"""
  185. self.GetFirstMap().Clean()
  186. self.GetSecondMap().Clean()
  187. ret = RunCommand(prog = 'g.remove',
  188. parent = self,
  189. rast = raster)
  190. if ret != 0:
  191. return False
  192. return True
  193. def AddToolbar(self, name):
  194. """!Add defined toolbar to the window
  195. Currently known toolbars are:
  196. - 'iClassMap' - basic map toolbar
  197. - 'iClass' - iclass tools
  198. - 'iClassMisc' - miscellaneous (help)
  199. - 'vdigit' - digitizer toolbar (areas)
  200. Toolbars 'iClassPreviewMapManager' are added in _addPanes().
  201. """
  202. if name == "iClassMap":
  203. self.toolbars[name] = IClassMapToolbar(self)
  204. self._mgr.AddPane(self.toolbars[name],
  205. wx.aui.AuiPaneInfo().
  206. Name(name).Caption(_("Map Toolbar")).
  207. ToolbarPane().Top().
  208. LeftDockable(False).RightDockable(False).
  209. BottomDockable(False).TopDockable(True).
  210. CloseButton(False).Layer(2).Row(1).
  211. BestSize((self.toolbars[name].GetBestSize())))
  212. if name == "iClass":
  213. self.toolbars[name] = IClassToolbar(self)
  214. self._mgr.AddPane(self.toolbars[name],
  215. wx.aui.AuiPaneInfo().
  216. Name(name).Caption(_("IClass Toolbar")).
  217. ToolbarPane().Top().
  218. LeftDockable(False).RightDockable(False).
  219. BottomDockable(False).TopDockable(True).
  220. CloseButton(False).Layer(2).Row(2).
  221. BestSize((self.toolbars[name].GetBestSize())))
  222. if name == "iClassMisc":
  223. self.toolbars[name] = IClassMiscToolbar(self)
  224. self._mgr.AddPane(self.toolbars[name],
  225. wx.aui.AuiPaneInfo().
  226. Name(name).Caption(_("IClass Misc Toolbar")).
  227. ToolbarPane().Top().
  228. LeftDockable(False).RightDockable(False).
  229. BottomDockable(False).TopDockable(True).
  230. CloseButton(False).Layer(2).Row(1).
  231. BestSize((self.toolbars[name].GetBestSize())))
  232. if name == "vdigit":
  233. self.toolbars[name] = VDigitToolbar(parent = self, MapWindow = self.GetFirstWindow(),
  234. digitClass = IClassVDigit, giface = self._giface,
  235. tools = ['addArea', 'moveVertex', 'addVertex',
  236. 'removeVertex', 'editLine', 'moveLine',
  237. 'deleteLine', 'deleteArea',
  238. 'undo', 'redo'])
  239. self._mgr.AddPane(self.toolbars[name],
  240. wx.aui.AuiPaneInfo().
  241. Name(name).Caption(_("Digitization Toolbar")).
  242. ToolbarPane().Top().
  243. LeftDockable(False).RightDockable(False).
  244. BottomDockable(False).TopDockable(True).
  245. CloseButton(False).Layer(2).Row(2).
  246. BestSize((self.toolbars[name].GetBestSize())))
  247. def _addPanes(self):
  248. """!Add mapwindows and toolbars to aui manager"""
  249. if sys.platform == 'win32':
  250. self._addPaneMapWindow(name = 'training')
  251. self._addPaneToolbar(name = 'iClassTrainingMapManager')
  252. self._addPaneMapWindow(name = 'preview')
  253. self._addPaneToolbar(name = 'iClassPreviewMapManager')
  254. else:
  255. self._addPaneToolbar(name = 'iClassPreviewMapManager')
  256. self._addPaneMapWindow(name = 'preview')
  257. self._addPaneToolbar(name = 'iClassTrainingMapManager')
  258. self._addPaneMapWindow(name = 'training')
  259. self._mgr.AddPane(self.plotPanel, wx.aui.AuiPaneInfo().
  260. Name("plots").Caption(_("Plots")).
  261. Dockable(False).Floatable(False).CloseButton(False).
  262. Left().Layer(1).BestSize((400, -1)))
  263. def _addPaneToolbar(self, name):
  264. if name == 'iClassPreviewMapManager':
  265. parent = self.previewMapManager
  266. else:
  267. parent = self.trainingMapManager
  268. self.toolbars[name] = IClassMapManagerToolbar(self, parent)
  269. self._mgr.AddPane(self.toolbars[name],
  270. wx.aui.AuiPaneInfo().ToolbarPane().Movable().
  271. Name(name).
  272. CloseButton(False).Center().Layer(0).
  273. BestSize((self.toolbars[name].GetBestSize())))
  274. def _addPaneMapWindow(self, name):
  275. if name == 'preview':
  276. window = self.GetSecondWindow()
  277. caption = _("Preview Display")
  278. else:
  279. window = self.GetFirstWindow()
  280. caption = _("Training Areas Display")
  281. self._mgr.AddPane(window, wx.aui.AuiPaneInfo().
  282. Name(name).Caption(caption).
  283. Dockable(False).Floatable(False).CloseButton(False).
  284. Center().Layer(0))
  285. def IsStandalone(self):
  286. """!Check if Map display is standalone"""
  287. return True
  288. def OnUpdateActive(self, event):
  289. """!
  290. @todo move to DoubleMapFrame?
  291. """
  292. if self.GetMapToolbar().GetActiveMap() == 0:
  293. self.MapWindow = self.firstMapWindow
  294. self.Map = self.firstMap
  295. else:
  296. self.MapWindow = self.secondMapWindow
  297. self.Map = self.secondMap
  298. self.UpdateActive(self.MapWindow)
  299. # for wingrass
  300. if os.name == 'nt':
  301. self.MapWindow.SetFocus()
  302. def UpdateActive(self, win):
  303. """!
  304. @todo move to DoubleMapFrame?
  305. """
  306. mapTb = self.GetMapToolbar()
  307. # optionally disable tool zoomback tool
  308. mapTb.Enable('zoomBack', enable = (len(self.MapWindow.zoomhistory) > 1))
  309. if mapTb.GetActiveMap() != (win == self.secondMapWindow):
  310. mapTb.SetActiveMap((win == self.secondMapWindow))
  311. self.StatusbarUpdate()
  312. def ActivateFirstMap(self, event=None):
  313. DoubleMapFrame.ActivateFirstMap(self, event)
  314. self.GetMapToolbar().Enable('zoomBack', enable=(len(self.MapWindow.zoomhistory) > 1))
  315. def ActivateSecondMap(self, event=None):
  316. DoubleMapFrame.ActivateSecondMap(self, event)
  317. self.GetMapToolbar().Enable('zoomBack', enable=(len(self.MapWindow.zoomhistory) > 1))
  318. def GetMapToolbar(self):
  319. """!Returns toolbar with zooming tools"""
  320. return self.toolbars['iClassMap']
  321. def GetClassColor(self, cat):
  322. """!Get class color as string
  323. @param cat class category
  324. @return 'R:G:B'
  325. """
  326. if cat in self.statisticsDict:
  327. return self.statisticsDict[cat].color
  328. return '0:0:0'
  329. def OnZoomMenu(self, event):
  330. """!Popup Zoom menu """
  331. zoommenu = wx.Menu()
  332. # Add items to the menu
  333. i = 0
  334. for label, handler in ((_('Zoom to computational region'), self.OnZoomToWind),
  335. (None, None),
  336. (_('Adjust Training Area Display to Preview Display'), self.OnZoomToPreview),
  337. (_('Adjust Preview display to Training Area Display'), self.OnZoomToTraining),
  338. (_("Display synchronization ON"), lambda event: self.SetBindRegions(True)),
  339. (_("Display synchronization OFF"), lambda event: self.SetBindRegions(False))):
  340. if label is None:
  341. zoommenu.AppendSeparator()
  342. continue
  343. item = wx.MenuItem(zoommenu, wx.ID_ANY, label)
  344. zoommenu.AppendItem(item)
  345. self.Bind(wx.EVT_MENU, handler, item)
  346. if i == 3:
  347. item.Enable(not self._bindRegions)
  348. elif i == 4:
  349. item.Enable(self._bindRegions)
  350. i += 1
  351. # Popup the menu. If an item is selected then its handler
  352. # will be called before PopupMenu returns.
  353. self.PopupMenu(zoommenu)
  354. zoommenu.Destroy()
  355. def OnZoomToTraining(self, event):
  356. """!Set preview display to match extents of training display """
  357. if not self.MapWindow == self.GetSecondWindow():
  358. self.MapWindow = self.GetSecondWindow()
  359. self.Map = self.GetSecondMap()
  360. self.UpdateActive(self.GetSecondWindow())
  361. newreg = self.firstMap.GetCurrentRegion()
  362. self.GetSecondMap().region = copy.copy(newreg)
  363. self.Render(self.GetSecondWindow())
  364. def OnZoomToPreview(self, event):
  365. """!Set preview display to match extents of training display """
  366. if not self.MapWindow == self.GetFirstWindow():
  367. self.MapWindow = self.GetFirstWindow()
  368. self.Map = self.GetFirstMap()
  369. self.UpdateActive(self.GetFirstWindow())
  370. newreg = self.GetSecondMap().GetCurrentRegion()
  371. self.GetFirstMap().region = copy.copy(newreg)
  372. self.Render(self.GetFirstWindow())
  373. def OnAddBands(self, event):
  374. """!Add imagery group"""
  375. dlg = IClassGroupDialog(self, group = self.group)
  376. if dlg.ShowModal() == wx.ID_OK:
  377. self.SetGroup(dlg.GetGroup())
  378. dlg.Destroy()
  379. def SetGroup(self, name):
  380. """!Set imagery group"""
  381. group = grass.find_file(name = name, element = 'group')
  382. if group['name']:
  383. self.group = group['name']
  384. else:
  385. GError(_("Group <%s> not found") % name, parent = self)
  386. def OnImportAreas(self, event):
  387. """!Import training areas"""
  388. # check if we have any changes
  389. if self.GetAreasCount() or self.statisticsList:
  390. qdlg = wx.MessageDialog(parent = self,
  391. message = _("All changes will be lost. "
  392. "Do you want to continue?") ,
  393. style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  394. if qdlg.ShowModal() == wx.ID_NO:
  395. qdlg.Destroy()
  396. return
  397. qdlg.Destroy()
  398. dlg = IClassMapDialog(self, title = _("Import vector map"), element = 'vector')
  399. if dlg.ShowModal() == wx.ID_OK:
  400. vName = dlg.GetMap()
  401. self.ImportAreas(vName)
  402. dlg.Destroy()
  403. def _checkImportedTopo(self, vector):
  404. """!Check if imported vector map has areas
  405. @param vector vector map name
  406. @return warning message (empty if topology is ok)
  407. """
  408. topo = grass.vector_info_topo(map = vector)
  409. warning = ''
  410. if topo['areas'] == 0:
  411. warning = _("No areas in vector map <%s>.\n" % vector)
  412. if topo['points'] or topo['lines']:
  413. warning +=_("Vector map <%s> contains points or lines, "
  414. "these features are ignored." % vector)
  415. return warning
  416. def ImportAreas(self, vector):
  417. """!Import training areas.
  418. If table connected, try load certain columns to class manager
  419. @param vector vector map name
  420. """
  421. warning = self._checkImportedTopo(vector)
  422. if warning:
  423. GMessage(parent = self, message = warning)
  424. return
  425. wx.BeginBusyCursor()
  426. wx.Yield()
  427. mapLayer = self.toolbars['vdigit'].mapLayer
  428. # set mapLayer temporarily to None
  429. # to avoid 'save changes' code in vdigit.toolbars
  430. self.toolbars['vdigit'].mapLayer = None
  431. ret = self.toolbars['vdigit'].StopEditing()
  432. if not ret:
  433. wx.EndBusyCursor()
  434. return False
  435. # check if attribute table really exists (fix broken connections)
  436. for layer in grass.vector_db(vector):
  437. if 0 != RunCommand('v.db.select', flags = 'c', map = vector, where = "0 = 1"):
  438. GWarning(_("Vector map <%s>: no attribute table found") % vector,
  439. parent = self)
  440. RunCommand('v.db.connect', flags = 'd', map = vector, layer = layer,
  441. quiet = True)
  442. if 0 != RunCommand('g.copy',
  443. vect = [vector, self.trainingAreaVector],
  444. overwrite = True, quiet = True,
  445. parent = self):
  446. wx.EndBusyCursor()
  447. return False
  448. ret = self.toolbars['vdigit'].StartEditing(mapLayer)
  449. if not ret:
  450. wx.EndBusyCursor()
  451. return False
  452. self.poMapInfo = self.GetFirstWindow().digit.GetDisplay().poMapInfo
  453. # remove temporary rasters
  454. for i in self.statisticsList:
  455. self.RemoveTempRaster(self.statisticsDict[i].rasterName)
  456. # clear current statistics
  457. self.statisticsDict.clear()
  458. del self.statisticsList[:] # not ...=[] !
  459. # reset plots
  460. self.plotPanel.Reset()
  461. self.GetFirstWindow().UpdateMap(render = False, renderVector = True)
  462. self.ImportClasses(vector)
  463. # should be saved in attribute table?
  464. self.toolbars['iClass'].UpdateStddev(1.5)
  465. wx.EndBusyCursor()
  466. return True
  467. def ImportClasses(self, vector):
  468. """!If imported map has table, try to import certain columns to class manager"""
  469. # check connection
  470. dbInfo = VectorDBInfo(vector)
  471. connected = (len(dbInfo.layers.keys()) > 0)
  472. # remove attribute table of temporary vector, we don't need it
  473. if connected:
  474. RunCommand('v.db.droptable',
  475. flags = 'f',
  476. map = self.trainingAreaVector)
  477. # we use first layer with table, TODO: user should choose
  478. layer = None
  479. for key in dbInfo.layers.keys():
  480. if dbInfo.GetTable(key):
  481. layer = key
  482. # get columns to check if we can use them
  483. # TODO: let user choose which columns mean what
  484. if layer is not None:
  485. columns = dbInfo.GetColumns(table = dbInfo.GetTable(layer))
  486. else:
  487. columns = []
  488. # get class manager
  489. if self.dialogs['classManager'] is None:
  490. self.dialogs['classManager'] = IClassCategoryManagerDialog(self)
  491. listCtrl = self.dialogs['classManager'].GetListCtrl()
  492. # unable to load data (no connection, table, right columns)
  493. if not connected or layer is None or \
  494. 'class' not in columns or \
  495. 'color' not in columns:
  496. # no table connected
  497. cats = RunCommand('v.category',
  498. input = vector,
  499. layer = 1, # set layer?
  500. # type = ['centroid', 'area'] ?
  501. option = "print",
  502. read = True)
  503. cats = map(int, cats.strip().split())
  504. cats = sorted(list(set(cats)))
  505. for cat in cats:
  506. listCtrl.AddCategory(cat = cat, name = 'class_%d' % cat, color = "0:0:0")
  507. # connection, table and columns exists
  508. else:
  509. columns = ['cat', 'class', 'color']
  510. ret = RunCommand('v.db.select',
  511. quiet = True,
  512. parent = self,
  513. flags = 'c',
  514. map = vector,
  515. layer = 1,
  516. columns = ','.join(columns),
  517. read = True)
  518. records = ret.strip().split('\n')
  519. for record in records:
  520. record = record.split('|')
  521. listCtrl.AddCategory(cat = int(record[0]), name = record[1], color = record[2])
  522. def OnExportAreas(self, event):
  523. """!Export training areas"""
  524. if self.GetAreasCount() == 0:
  525. GMessage(parent = self, message = _("No training areas to export."))
  526. return
  527. dlg = IClassExportAreasDialog(self, vectorName = self.exportVector)
  528. if dlg.ShowModal() == wx.ID_OK:
  529. vName = dlg.GetVectorName()
  530. self.exportVector = vName
  531. withTable = dlg.WithTable()
  532. dlg.Destroy()
  533. if self.ExportAreas(vectorName = vName, withTable = withTable):
  534. GMessage(_("%d training areas (%d classes) exported to vector map <%s>.") % \
  535. (self.GetAreasCount(), len(self.statisticsList),
  536. self.exportVector), parent = self)
  537. def ExportAreas(self, vectorName, withTable):
  538. """!Export training areas to new vector map (with attribute table).
  539. @param vectorName name of exported vector map
  540. @param withTable true if attribute table is required
  541. """
  542. wx.BeginBusyCursor()
  543. wx.Yield()
  544. # close, build, copy and open again the temporary vector
  545. displayDriver = self.GetFirstWindow().digit.GetDisplay()
  546. displayDriver.CloseMap()
  547. if '@' in vectorName:
  548. vectorName = vectorName.split('@')[0]
  549. if 0 != RunCommand('g.copy',
  550. vect = [self.trainingAreaVector, vectorName],
  551. overwrite = True, quiet = True, parent = self):
  552. wx.EndBusyCursor()
  553. return False
  554. # remove connection if exists
  555. dbinfo = grass.vector_db(vectorName)
  556. if dbinfo:
  557. for layer in dbinfo.keys():
  558. RunCommand('v.db.connect', flags = 'd', map = vectorName, layer = layer)
  559. mapset = grass.gisenv()['MAPSET']
  560. self.poMapInfo = displayDriver.OpenMap(name = self.trainingAreaVector, mapset = mapset)
  561. if not withTable:
  562. wx.EndBusyCursor()
  563. return False
  564. # add new table
  565. columns = ["class varchar(30)",
  566. "color varchar(11)",
  567. "n_cells integer",]
  568. nbands = len(self.GetGroupLayers(self.group))
  569. for statistic, format in (("min", "integer"), ("mean", "double precision"), ("max", "integer")):
  570. for i in range(nbands):
  571. # 10 characters limit?
  572. columns.append("band%(band)d_%(stat)s %(format)s" % {'band' : i + 1,
  573. 'stat' : statistic,
  574. 'format' : format})
  575. if 0 != RunCommand('v.db.addtable',
  576. map = vectorName,
  577. columns = columns,
  578. parent = self):
  579. wx.EndBusyCursor()
  580. return False
  581. # populate table
  582. for cat in self.statisticsList:
  583. stat = self.statisticsDict[cat]
  584. self._runDBUpdate(map = vectorName, column = "class", value = stat.name, cat = cat)
  585. self._runDBUpdate(map = vectorName, column = "color", value = stat.color, cat = cat)
  586. if not stat.IsReady():
  587. continue
  588. self._runDBUpdate(map = vectorName, column = "n_cells",value = stat.ncells, cat = cat)
  589. for i in range(nbands):
  590. self._runDBUpdate(map = vectorName, column = "band%d_min" % (i + 1), value = stat.bands[i].min, cat = cat)
  591. self._runDBUpdate(map = vectorName, column = "band%d_mean" % (i + 1), value = stat.bands[i].mean, cat = cat)
  592. self._runDBUpdate(map = vectorName, column = "band%d_max" % (i + 1), value = stat.bands[i].max, cat = cat)
  593. wx.EndBusyCursor()
  594. return True
  595. def _runDBUpdate(self, map, column, value, cat):
  596. """!Helper function for calling v.db.update.
  597. @param map vector map name
  598. @param column name of updated column
  599. @param value new value
  600. @param cat which category to update
  601. @return returncode (0 is OK)
  602. """
  603. ret = RunCommand('v.db.update',
  604. map = map,
  605. layer = 1,
  606. column = column,
  607. value = value,
  608. where = "cat = %d" % cat)
  609. return ret
  610. def OnCategoryManager(self, event):
  611. """!Show category management dialog"""
  612. if self.dialogs['classManager'] is None:
  613. dlg = IClassCategoryManagerDialog(self)
  614. dlg.Show()
  615. self.dialogs['classManager'] = dlg
  616. else:
  617. if not self.dialogs['classManager'].IsShown():
  618. self.dialogs['classManager'].Show()
  619. def CategoryChanged(self, currentCat):
  620. """!Updates everything which depends on current category.
  621. Updates number of stddev, histograms, layer in preview display.
  622. """
  623. nstd = self.statisticsDict[currentCat].nstd
  624. self.toolbars['iClass'].UpdateStddev(nstd)
  625. self.plotPanel.UpdateCategory(currentCat)
  626. self.plotPanel.OnPlotTypeSelected(None)
  627. name = self.statisticsDict[currentCat].rasterName
  628. name = self.previewMapManager.GetAlias(name)
  629. if name:
  630. self.previewMapManager.SelectLayer(name)
  631. def DeleteAreas(self, cats):
  632. """!Removes all training areas of given categories
  633. @param cats list of categories to be deleted
  634. """
  635. self.firstMapWindow.digit.DeleteAreasByCat(cats)
  636. self.firstMapWindow.UpdateMap(render=False, renderVector=True)
  637. def HighlightCategory(self, cats):
  638. """!Highlight araes given by category"""
  639. self.firstMapWindow.digit.GetDisplay().SetSelected(cats, layer = 1)
  640. self.firstMapWindow.UpdateMap(render=False, renderVector=True)
  641. def ZoomToAreasByCat(self, cat):
  642. """!Zoom to areas given by category"""
  643. n, s, w, e = self.GetFirstWindow().digit.GetDisplay().GetRegionSelected()
  644. self.GetFirstMap().GetRegion(n = n, s = s, w = w, e = e, update = True)
  645. self.GetFirstMap().AdjustRegion()
  646. self.GetFirstMap().AlignExtentFromDisplay()
  647. self.GetFirstWindow().UpdateMap(render = True, renderVector = True)
  648. def UpdateRasterName(self, newName, cat):
  649. """!Update alias of raster map when category name is changed"""
  650. origName = self.statisticsDict[cat].rasterName
  651. self.previewMapManager.SetAlias(origName, newName)
  652. def StddevChanged(self, cat, nstd):
  653. """!Standard deviation multiplier changed, rerender map, histograms"""
  654. stat = self.statisticsDict[cat]
  655. stat.nstd = nstd
  656. if not stat.IsReady():
  657. return
  658. raster = stat.rasterName
  659. cstat = self.cStatisticsDict[cat]
  660. I_iclass_statistics_set_nstd(cstat, nstd)
  661. I_iclass_create_raster(cstat, self.refer, raster)
  662. self.Render(self.GetSecondWindow())
  663. stat.SetBandStatistics(cstat)
  664. self.plotPanel.StddevChanged()
  665. def UpdateChangeState(self, changes):
  666. """!Informs if any important changes happened
  667. since last analysis computation.
  668. """
  669. self.changes = changes
  670. def AddRasterMap(self, name, firstMap = True, secondMap = True):
  671. """!Add raster map to Map"""
  672. cmdlist = ['d.rast', 'map=%s' % name]
  673. if firstMap:
  674. self.GetFirstMap().AddLayer(ltype='raster', command=cmdlist, active=True,
  675. name=name, hidden=False, opacity=1.0, render=False)
  676. self.Render(self.GetFirstWindow())
  677. if secondMap:
  678. self.GetSecondMap().AddLayer(ltype='raster', command=cmdlist, active=True,
  679. name=name, hidden=False, opacity=1.0, render=False)
  680. self.Render(self.GetSecondWindow())
  681. def AddTrainingAreaMap(self):
  682. """!Add vector map with training areas to Map (training
  683. sub-display)"""
  684. vname = self.CreateTempVector()
  685. if vname:
  686. self.trainingAreaVector = vname
  687. else:
  688. GMessage(parent = self, message = _("Failed to create temporary vector map."))
  689. return
  690. mapLayer = self.GetFirstMap().AddLayer(ltype = 'vector',
  691. command = ['d.vect', 'map=%s' % vname],
  692. name = vname, active = False)
  693. self.toolbars['vdigit'].StartEditing(mapLayer)
  694. self.poMapInfo = self.GetFirstWindow().digit.GetDisplay().poMapInfo
  695. self.Render(self.GetFirstWindow())
  696. def OnRunAnalysis(self, event):
  697. """!Run analysis and update plots"""
  698. if self.RunAnalysis():
  699. currentCat = self.GetCurrentCategoryIdx()
  700. self.plotPanel.UpdatePlots(group = self.group, currentCat = currentCat,
  701. statDict = self.statisticsDict,
  702. statList = self.statisticsList)
  703. def RunAnalysis(self):
  704. """!Run analysis
  705. Calls C functions to compute all statistics and creates raster maps.
  706. Signatures are created but signature file is not.
  707. """
  708. if not self.CheckInput(group = self.group, vector = self.trainingAreaVector):
  709. return
  710. for statistic in self.cStatisticsDict.values():
  711. I_iclass_free_statistics(statistic)
  712. self.cStatisticsDict = {}
  713. # init Ref struct with the files in group */
  714. I_free_group_ref(self.refer)
  715. # we expect the subgroup name to be the same as the group name
  716. subgroup = self.group
  717. if (not I_iclass_init_group(self.group, subgroup, self.refer)):
  718. return False
  719. I_free_signatures(self.signatures)
  720. I_iclass_init_signatures(self.signatures, self.refer)
  721. cats = self.statisticsList[:]
  722. for i in cats:
  723. stats = self.statisticsDict[i]
  724. statistics_obj = IClass_statistics()
  725. statistics = pointer(statistics_obj)
  726. I_iclass_init_statistics(statistics,
  727. stats.category,
  728. stats.name,
  729. stats.color,
  730. stats.nstd)
  731. ret = I_iclass_analysis(statistics, self.refer, self.poMapInfo, "1",
  732. self.group, stats.rasterName)
  733. if ret > 0:
  734. # tests
  735. self.cStatisticsDict[i] = statistics
  736. stats.SetStatistics(statistics)
  737. stats.SetReady()
  738. self.statisticsDict[stats.category] = stats
  739. self.ConvertToNull(name = stats.rasterName)
  740. self.previewMapManager.AddLayer(name = stats.rasterName,
  741. alias = stats.name, resultsLayer = True)
  742. # write statistics
  743. I_iclass_add_signature(self.signatures, statistics)
  744. elif ret == 0:
  745. GMessage(parent = self, message = _("No area in category %s. Category skipped.") % stats.category)
  746. I_iclass_free_statistics(statistics)
  747. else:
  748. GMessage(parent = self, message = _("Analysis failed."))
  749. I_iclass_free_statistics(statistics)
  750. self.UpdateChangeState(changes = False)
  751. return True
  752. def OnSaveSigFile(self, event):
  753. """!Asks for signature file name and saves it."""
  754. if not self.group:
  755. GMessage(parent = self, message = _("No imagery group selected."))
  756. return
  757. if self.changes:
  758. qdlg = wx.MessageDialog(parent = self,
  759. message = _("Due to recent changes in classes, "
  760. "signatures can be outdated and should be recalculated. "
  761. "Do you still want to continue?") ,
  762. caption = _("Outdated signatures"),
  763. style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  764. if qdlg.ShowModal() == wx.ID_YES:
  765. qdlg.Destroy()
  766. else:
  767. qdlg.Destroy()
  768. return
  769. dlg = IClassSignatureFileDialog(self, group = self.group, file = self.sigFile)
  770. if dlg.ShowModal() == wx.ID_OK:
  771. if os.path.exists(dlg.GetFileName(fullPath = True)):
  772. qdlg = wx.MessageDialog(parent = self,
  773. message = _("A signature file named %s already exists.\n"
  774. "Do you want to replace it?") % dlg.GetFileName(),
  775. caption = _("File already exists"),
  776. style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  777. if qdlg.ShowModal() == wx.ID_YES:
  778. qdlg.Destroy()
  779. else:
  780. qdlg.Destroy()
  781. return
  782. self.sigFile = dlg.GetFileName()
  783. self.WriteSignatures(self.signatures, self.group, self.sigFile)
  784. dlg.Destroy()
  785. def InitStatistics(self):
  786. """!Initialize variables and c structures neccessary for
  787. computing statistics.
  788. """
  789. self.group = None
  790. self.sigFile = None
  791. self.statisticsDict = {}
  792. self.statisticsList = []
  793. self.cStatisticsDict = {}
  794. self.signatures_obj = Signature()
  795. self.signatures = pointer(self.signatures_obj)
  796. I_init_signatures(self.signatures, 0) # must be freed on exit
  797. refer_obj = Ref()
  798. self.refer = pointer(refer_obj)
  799. I_init_group_ref(self.refer) # must be freed on exit
  800. def WriteSignatures(self, signatures, group, filename):
  801. """!Writes current signatures to signature file
  802. @param signatures signature (c structure)
  803. @param group imagery group
  804. @param filename signature file name
  805. """
  806. I_iclass_write_signatures(signatures, group, group, filename)
  807. def CheckInput(self, group, vector):
  808. """!Check if input is valid"""
  809. # check if group is ok
  810. if not group:
  811. GMessage(parent = self,
  812. message = _("No imagery group selected. "
  813. "Operation canceled."))
  814. return False
  815. groupLayers = self.GetGroupLayers(group)
  816. nLayers = len(groupLayers)
  817. if nLayers <= 1:
  818. GMessage(parent = self,
  819. message = _("Group <%(group)s> does not have enough files "
  820. "(it has %(files)d files). Operation canceled.") % \
  821. { 'group' : group,
  822. 'files' : nLayers })
  823. return False
  824. #check if vector has any areas
  825. if self.GetAreasCount() == 0:
  826. GMessage(parent = self,
  827. message = _("No areas given. "
  828. "Operation canceled."))
  829. return False
  830. # check if vector is inside raster
  831. regionBox = bound_box()
  832. Vect_get_map_box(self.poMapInfo, byref(regionBox))
  833. rasterInfo = grass.raster_info(groupLayers[0])
  834. if regionBox.N > rasterInfo['north'] or \
  835. regionBox.S < rasterInfo['south'] or \
  836. regionBox.E > rasterInfo['east'] or \
  837. regionBox.W < rasterInfo['west']:
  838. GMessage(parent = self,
  839. message = _("Vector features are outside raster layers. "
  840. "Operation canceled."))
  841. return False
  842. return True
  843. def GetAreasCount(self):
  844. """!Returns number of not dead areas"""
  845. count = 0
  846. numAreas = Vect_get_num_areas(self.poMapInfo)
  847. for i in range(numAreas):
  848. if Vect_area_alive(self.poMapInfo, i + 1):
  849. count += 1
  850. return count
  851. def GetGroupLayers(self, group):
  852. """!Get layers in subgroup (expecting same name for group and subgroup)
  853. @todo consider moving this function to core module for convenient
  854. """
  855. res = RunCommand('i.group',
  856. flags = 'g',
  857. group = group, subgroup = group,
  858. read = True).strip()
  859. if res.split('\n')[0]:
  860. return res.split('\n')
  861. return []
  862. def ConvertToNull(self, name):
  863. """! Sets value which represents null values for given raster map.
  864. @param name raster map name
  865. """
  866. RunCommand('r.null',
  867. map = name,
  868. setnull = 0)
  869. def GetCurrentCategoryIdx(self):
  870. """!Returns current category number"""
  871. return self.toolbars['iClass'].GetSelectedCategoryIdx()
  872. def OnZoomIn(self, event):
  873. """!Enable zooming for plots"""
  874. super(IClassMapFrame, self).OnZoomIn(event)
  875. self.plotPanel.EnableZoom(type = 1)
  876. def OnZoomOut(self, event):
  877. """!Enable zooming for plots"""
  878. super(IClassMapFrame, self).OnZoomOut(event)
  879. self.plotPanel.EnableZoom(type = -1)
  880. def OnPan(self, event):
  881. """!Enable panning for plots"""
  882. super(IClassMapFrame, self).OnPan(event)
  883. self.plotPanel.EnablePan()
  884. def OnPointer(self, event):
  885. """!Set pointer mode.
  886. @todo: pointers need refactoring
  887. """
  888. toolbar = self.GetMapToolbar()
  889. self.GetFirstWindow().mouse['use'] = 'pointer'
  890. def SwitchTool(self, toolbar, event):
  891. """!Calls UpdateTools to manage connected toolbars"""
  892. self.UpdateTools(event)
  893. super(IClassMapFrame, self).SwitchTool(toolbar, event)
  894. def UpdateTools(self, event):
  895. """!Method deals with relations of toolbars and other
  896. elements"""
  897. # untoggles button in other toolbars
  898. for toolbar in self.toolbars.itervalues():
  899. if hasattr(event, 'GetEventObject') == True:
  900. if event.GetEventObject() == toolbar:
  901. continue
  902. if toolbar.action:
  903. toolbar.ToggleTool(toolbar.action['id'], False)
  904. toolbar.action['id'] = -1
  905. toolbar.OnTool(None)
  906. class MapManager:
  907. """! Class for managing map renderer.
  908. It is connected with iClassMapManagerToolbar.
  909. """
  910. def __init__(self, frame, mapWindow, Map):
  911. """!
  912. It is expected that \a mapWindow is conected with \a Map.
  913. @param frame application main window
  914. @param mapWindow map window instance
  915. @param Map map renderer instance
  916. """
  917. self.map = Map
  918. self.frame = frame
  919. self.mapWindow = mapWindow
  920. self.toolbar = None
  921. self.layerName = {}
  922. def SetToolbar(self, toolbar):
  923. self.toolbar = toolbar
  924. def AddLayer(self, name, alias = None, resultsLayer = False):
  925. """!Adds layer to Map and update toolbar
  926. @param name layer (raster) name
  927. @param resultsLayer True if layer is temp. raster showing the results of computation
  928. """
  929. if (resultsLayer and
  930. name in [l.GetName() for l in self.map.GetListOfLayers(name = name)]):
  931. self.frame.Render(self.mapWindow)
  932. return
  933. cmdlist = ['d.rast', 'map=%s' % name]
  934. self.map.AddLayer(ltype = 'raster', command = cmdlist, active = True,
  935. name = name, hidden = False, opacity = 1.0, render = True)
  936. self.frame.Render(self.mapWindow)
  937. if alias is not None:
  938. alias = self._addSuffix(alias)
  939. self.layerName[alias] = name
  940. name = alias
  941. else:
  942. self.layerName[name] = name
  943. self.toolbar.choice.Insert(name, 0)
  944. self.toolbar.choice.SetSelection(0)
  945. def AddLayerRGB(self, cmd):
  946. """!Adds RGB layer and update toolbar.
  947. @param cmd d.rgb command as a list
  948. """
  949. name = []
  950. for param in cmd:
  951. if '=' in param:
  952. name.append(param.split('=')[1])
  953. name = ','.join(name)
  954. self.map.AddLayer(ltype = 'rgb', command = cmd, active = True,
  955. name = name, hidden = False, opacity = 1.0, render = True)
  956. self.frame.Render(self.mapWindow)
  957. self.layerName[name] = name
  958. self.toolbar.choice.Insert(name, 0)
  959. self.toolbar.choice.SetSelection(0)
  960. def RemoveTemporaryLayer(self, name):
  961. """!Removes temporary layer (if exists) from Map and and updates toolbar.
  962. @param name real name of layer
  963. """
  964. # check if layer is loaded
  965. layers = self.map.GetListOfLayers(ltype = 'raster')
  966. idx = None
  967. for i, layer in enumerate(layers):
  968. if name == layer.GetName():
  969. idx = i
  970. break
  971. if idx is None:
  972. return
  973. # remove it from Map
  974. self.map.RemoveLayer(name = name)
  975. # update inner list of layers
  976. alias = self.GetAlias(name)
  977. if alias not in self.layerName:
  978. return
  979. del self.layerName[alias]
  980. # update choice
  981. idx = self.toolbar.choice.FindString(alias)
  982. if idx != wx.NOT_FOUND:
  983. self.toolbar.choice.Delete(idx)
  984. if not self.toolbar.choice.IsEmpty():
  985. self.toolbar.choice.SetSelection(0)
  986. self.frame.Render(self.mapWindow)
  987. def RemoveLayer(self, name, idx):
  988. """!Removes layer from Map and update toolbar"""
  989. name = self.layerName[name]
  990. self.map.RemoveLayer(name = name)
  991. del self.layerName[name]
  992. self.toolbar.choice.Delete(idx)
  993. if not self.toolbar.choice.IsEmpty():
  994. self.toolbar.choice.SetSelection(0)
  995. self.frame.Render(self.mapWindow)
  996. def SelectLayer(self, name):
  997. """!Moves selected layer to top"""
  998. layers = self.map.GetListOfLayers(ltype = 'raster') + \
  999. self.map.GetListOfLayers(ltype = 'rgb')
  1000. idx = None
  1001. for i, layer in enumerate(layers):
  1002. if self.layerName[name] == layer.GetName():
  1003. idx = i
  1004. break
  1005. if idx is not None: # should not happen
  1006. layers.append(layers.pop(idx))
  1007. choice = self.toolbar.choice
  1008. idx = choice.FindString(name)
  1009. choice.Delete(idx)
  1010. choice.Insert(name, 0)
  1011. choice.SetSelection(0)
  1012. #layers.reverse()
  1013. self.map.ReorderLayers(layers)
  1014. self.frame.Render(self.mapWindow)
  1015. def SetOpacity(self, name):
  1016. """!Sets opacity of layers."""
  1017. name = self.layerName[name]
  1018. layers = self.map.GetListOfLayers(name = name)
  1019. if not layers:
  1020. return
  1021. # works for first layer only
  1022. oldOpacity = layers[0].GetOpacity()
  1023. dlg = SetOpacityDialog(self.frame, opacity = oldOpacity)
  1024. dlg.applyOpacity.connect(lambda value:
  1025. self._changeOpacity(layer=layers[0], opacity=value))
  1026. if dlg.ShowModal() == wx.ID_OK:
  1027. self._changeOpacity(layer=layers[0], opacity=dlg.GetOpacity())
  1028. dlg.Destroy()
  1029. def _changeOpacity(self, layer, opacity):
  1030. self.map.ChangeOpacity(layer=layer, opacity=opacity)
  1031. self.frame.Render(self.mapWindow)
  1032. def _addSuffix(self, name):
  1033. suffix = _('results')
  1034. return '_'.join((name, suffix))
  1035. def GetAlias(self, name):
  1036. """!Returns alias for layer"""
  1037. name = [k for k, v in self.layerName.iteritems() if v == name]
  1038. if name:
  1039. return name[0]
  1040. return None
  1041. def SetAlias(self, original, alias):
  1042. name = self.GetAlias(original)
  1043. if name:
  1044. self.layerName[self._addSuffix(alias)] = original
  1045. del self.layerName[name]
  1046. idx = self.toolbar.choice.FindString(name)
  1047. if idx != wx.NOT_FOUND:
  1048. self.toolbar.choice.SetString(idx, self._addSuffix(alias))
  1049. def test():
  1050. import gettext
  1051. import core.render as render
  1052. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  1053. app = wx.PySimpleApp()
  1054. wx.InitAllImageHandlers()
  1055. frame = IClassMapFrame()
  1056. frame.Show()
  1057. app.MainLoop()
  1058. if __name__ == "__main__":
  1059. test()