frame.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  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-2011 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. if __name__ == "__main__":
  19. sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
  20. from core import globalvar
  21. import wx
  22. from ctypes import *
  23. try:
  24. from grass.lib.imagery import *
  25. from grass.lib.vector import *
  26. haveIClass = True
  27. errMsg = ''
  28. except ImportError, e:
  29. haveIClass = False
  30. errMsg = _("Loading imagery lib failed.\n%s") % e
  31. import grass.script as grass
  32. from mapdisp import statusbar as sb
  33. from mapdisp.mapwindow import BufferedWindow
  34. from vdigit.toolbars import VDigitToolbar
  35. from gui_core.mapdisp import DoubleMapFrame
  36. from core.render import Map, MapLayer
  37. from core.gcmd import RunCommand, GMessage
  38. from gui_core.dialogs import SetOpacityDialog
  39. import grass.script as grass
  40. from iclass.digit import IClassVDigitWindow, IClassVDigit
  41. from iclass.toolbars import IClassMapToolbar, IClassMiscToolbar,\
  42. IClassToolbar, IClassMapManagerToolbar
  43. from iclass.statistics import Statistics, BandStatistics
  44. from iclass.dialogs import CategoryListCtrl, IClassCategoryManagerDialog,\
  45. IClassGroupDialog, IClassMapDialog, IClassSignatureFileDialog
  46. from iclass.plots import PlotPanel
  47. class IClassMapFrame(DoubleMapFrame):
  48. """! wxIClass main frame
  49. It has two map windows one for digitizing training areas and one for
  50. result preview.
  51. It generates histograms, raster maps and signature files using
  52. @c I_iclass_* functions from C imagery library.
  53. It is wxGUI counterpart of old i.class module.
  54. """
  55. def __init__(self, parent = None, title = _("Supervised Classification Tool"),
  56. toolbars = ["iClassMisc", "iClassMap", "vdigit", "iClass"],
  57. size = (800, 600), name = 'IClassWindow', **kwargs):
  58. """!
  59. @param parent (no parent is expected)
  60. @param title window title
  61. @param toolbars dictionary of active toolbars (defalult value represents all toolbars)
  62. @param size default size
  63. """
  64. DoubleMapFrame.__init__(self, parent = parent, title = title,
  65. name = name,
  66. firstMap = Map(), secondMap = Map(),
  67. **kwargs)
  68. self.firstMapWindow = IClassVDigitWindow(self, map = self.firstMap)
  69. self.secondMapWindow = BufferedWindow(self, Map = self.secondMap)
  70. self.MapWindow = self.firstMapWindow # current by default
  71. self._bindWindowsActivation()
  72. self.SetSize(size)
  73. #
  74. # Add toolbars
  75. #
  76. toolbarsCopy = toolbars[:]
  77. if sys.platform == 'win32':
  78. self.AddToolbar(toolbarsCopy.pop(1))
  79. toolbarsCopy.reverse()
  80. else:
  81. self.AddToolbar(toolbarsCopy.pop(0))
  82. for toolb in toolbarsCopy:
  83. self.AddToolbar(toolb)
  84. self.firstMapWindow.SetToolbar(self.toolbars['vdigit'])
  85. self.GetMapToolbar().GetActiveMapTool().Bind(wx.EVT_CHOICE, self.OnUpdateActive)
  86. #
  87. # Add statusbar
  88. #
  89. # items for choice
  90. self.statusbarItems = [sb.SbCoordinates,
  91. sb.SbRegionExtent,
  92. sb.SbCompRegionExtent,
  93. sb.SbShowRegion,
  94. sb.SbAlignExtent,
  95. sb.SbResolution,
  96. sb.SbDisplayGeometry,
  97. sb.SbMapScale,
  98. sb.SbGoTo,
  99. sb.SbProjection]
  100. self.statusbarItemsHiddenInNviz = (sb.SbAlignExtent,
  101. sb.SbDisplayGeometry,
  102. sb.SbShowRegion,
  103. sb.SbResolution,
  104. sb.SbMapScale)
  105. # create statusbar and its manager
  106. statusbar = self.CreateStatusBar(number = 4, style = 0)
  107. statusbar.SetStatusWidths([-5, -2, -1, -1])
  108. self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
  109. # fill statusbar manager
  110. self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
  111. self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
  112. self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
  113. self.statusbarManager.Update()
  114. self.trainingMapManager = MapManager(self, mapWindow = self.GetFirstWindow(),
  115. Map = self.GetFirstMap())
  116. self.previewMapManager = MapManager(self, mapWindow = self.GetSecondWindow(),
  117. Map = self.GetSecondMap())
  118. self.InitStatistics()
  119. self.changes = False
  120. # PyPlot init
  121. self.plotPanel = PlotPanel(self, statDict = self.statisticsDict,
  122. statList = self.statisticsList)
  123. self._addPanes()
  124. self._mgr.Update()
  125. self.trainingMapManager.SetToolbar(self.toolbars['iClassTrainingMapManager'])
  126. self.previewMapManager.SetToolbar(self.toolbars['iClassPreviewMapManager'])
  127. # default action
  128. self.OnPan(event = None)
  129. wx.CallAfter(self.AddTrainingAreaMap)
  130. #self.dialogs['category'] = None
  131. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  132. def OnCloseWindow(self, event):
  133. self.Destroy()
  134. def __del__(self):
  135. """! Frees C structs and removes vector map and all raster maps."""
  136. I_free_signatures(self.signatures)
  137. I_free_group_ref(self.refer)
  138. for st in self.cStatisticsDict.values():
  139. I_iclass_free_statistics(st)
  140. self.RemoveTempVector()
  141. for i in self.statisticsList:
  142. self.RemoveTempRaster(self.statisticsDict[i].rasterName)
  143. def OnHelp(self, event):
  144. """!Show help page"""
  145. grass.run_command('g.manual',
  146. entry = 'wxGUI.IClass')
  147. def CreateTempVector(self):
  148. """!Create temporary vector map for training areas"""
  149. vectorPath = grass.tempfile(create = False)
  150. vectorName = 'trAreas' + os.path.basename(vectorPath).replace('.','')
  151. cmd = ('v.edit', {'tool': 'create',
  152. 'map': vectorName})
  153. ret = RunCommand(prog = cmd[0],
  154. parent = self,
  155. overwrite = True,
  156. **cmd[1])
  157. if ret != 0:
  158. return False
  159. return vectorName
  160. def RemoveTempVector(self):
  161. """!Removes temporary vector map with training areas"""
  162. ret = RunCommand(prog = 'g.remove',
  163. parent = self,
  164. vect = self.trainingAreaVector)
  165. if ret != 0:
  166. return False
  167. return True
  168. def RemoveTempRaster(self, raster):
  169. """!Removes temporary raster maps"""
  170. ret = RunCommand(prog = 'g.remove',
  171. parent = self,
  172. rast = raster)
  173. if ret != 0:
  174. return False
  175. return True
  176. def AddToolbar(self, name):
  177. """!Add defined toolbar to the window
  178. Currently known toolbars are:
  179. - 'iClassMap' - basic map toolbar
  180. - 'iClass' - iclass tools
  181. - 'iClassMisc' - miscellaneous (help)
  182. - 'vdigit' - digitizer toolbar (areas)
  183. Toolbars 'iClassPreviewMapManager' are added in _addPanes().
  184. """
  185. if name == "iClassMap":
  186. self.toolbars[name] = IClassMapToolbar(self)
  187. self._mgr.AddPane(self.toolbars[name],
  188. wx.aui.AuiPaneInfo().
  189. Name(name).Caption(_("Map Toolbar")).
  190. ToolbarPane().Top().
  191. LeftDockable(False).RightDockable(False).
  192. BottomDockable(False).TopDockable(True).
  193. CloseButton(False).Layer(2).Row(1).
  194. BestSize((self.toolbars[name].GetBestSize())))
  195. if name == "iClass":
  196. self.toolbars[name] = IClassToolbar(self)
  197. self._mgr.AddPane(self.toolbars[name],
  198. wx.aui.AuiPaneInfo().
  199. Name(name).Caption(_("IClass Toolbar")).
  200. ToolbarPane().Top().
  201. LeftDockable(False).RightDockable(False).
  202. BottomDockable(False).TopDockable(True).
  203. CloseButton(False).Layer(2).Row(2).
  204. BestSize((self.toolbars[name].GetBestSize())))
  205. if name == "iClassMisc":
  206. self.toolbars[name] = IClassMiscToolbar(self)
  207. self._mgr.AddPane(self.toolbars[name],
  208. wx.aui.AuiPaneInfo().
  209. Name(name).Caption(_("IClass Misc Toolbar")).
  210. ToolbarPane().Top().
  211. LeftDockable(False).RightDockable(False).
  212. BottomDockable(False).TopDockable(True).
  213. CloseButton(False).Layer(2).Row(1).
  214. BestSize((self.toolbars[name].GetBestSize())))
  215. if name == "vdigit":
  216. self.toolbars[name] = VDigitToolbar(self, MapWindow = self.GetFirstWindow(),
  217. digitClass = IClassVDigit,
  218. tools = ['addArea', 'moveVertex', 'addVertex',
  219. 'removeVertex', 'editLine',
  220. 'moveLine', 'deleteArea', 'undo', 'redo'])
  221. self._mgr.AddPane(self.toolbars[name],
  222. wx.aui.AuiPaneInfo().
  223. Name(name).Caption(_("Digitization Toolbar")).
  224. ToolbarPane().Top().
  225. LeftDockable(False).RightDockable(False).
  226. BottomDockable(False).TopDockable(True).
  227. CloseButton(False).Layer(2).Row(2).
  228. BestSize((self.toolbars[name].GetBestSize())))
  229. def _addPanes(self):
  230. """!Add mapwindows and toolbars to aui manager"""
  231. if sys.platform == 'win32':
  232. self._addPaneMapWindow(name = 'training')
  233. self._addPaneToolbar(name = 'iClassTrainingMapManager')
  234. self._addPaneMapWindow(name = 'preview')
  235. self._addPaneToolbar(name = 'iClassPreviewMapManager')
  236. else:
  237. self._addPaneToolbar(name = 'iClassPreviewMapManager')
  238. self._addPaneMapWindow(name = 'preview')
  239. self._addPaneToolbar(name = 'iClassTrainingMapManager')
  240. self._addPaneMapWindow(name = 'training')
  241. self._mgr.AddPane(self.plotPanel, wx.aui.AuiPaneInfo().
  242. Name("plots").Caption(_("Plots")).
  243. Dockable(False).Floatable(False).CloseButton(False).
  244. Left().Layer(1).BestSize((400, -1)))
  245. def _addPaneToolbar(self, name):
  246. if name == 'iClassPreviewMapManager':
  247. parent = self.previewMapManager
  248. else:
  249. parent = self.trainingMapManager
  250. self.toolbars[name] = IClassMapManagerToolbar(self, parent)
  251. self._mgr.AddPane(self.toolbars[name],
  252. wx.aui.AuiPaneInfo().ToolbarPane().Movable().
  253. Name(name).
  254. CloseButton(False).Center().Layer(0).
  255. BestSize((self.toolbars[name].GetBestSize())))
  256. def _addPaneMapWindow(self, name):
  257. if name == 'preview':
  258. window = self.GetSecondWindow()
  259. caption = _("Preview Display")
  260. else:
  261. window = self.GetFirstWindow()
  262. caption = _("Training Areas Display")
  263. self._mgr.AddPane(window, wx.aui.AuiPaneInfo().
  264. Name(name).Caption(caption).
  265. Dockable(False).Floatable(False).CloseButton(False).
  266. Center().Layer(0))
  267. def IsStandalone(self):
  268. """!Check if Map display is standalone"""
  269. return True
  270. def OnUpdateActive(self, event):
  271. """!
  272. @todo move to DoubleMapFrame?
  273. """
  274. if self.GetMapToolbar().GetActiveMap() == 0:
  275. self.MapWindow = self.firstMapWindow
  276. self.Map = self.firstMap
  277. else:
  278. self.MapWindow = self.secondMapWindow
  279. self.Map = self.secondMap
  280. self.UpdateActive(self.MapWindow)
  281. # for wingrass
  282. if os.name == 'nt':
  283. self.MapWindow.SetFocus()
  284. def UpdateActive(self, win):
  285. """!
  286. @todo move to DoubleMapFrame?
  287. """
  288. mapTb = self.GetMapToolbar()
  289. # optionally disable tool zoomback tool
  290. mapTb.Enable('zoomback', enable = (len(self.MapWindow.zoomhistory) > 1))
  291. if mapTb.GetActiveMap() != (win == self.secondMapWindow):
  292. mapTb.SetActiveMap((win == self.secondMapWindow))
  293. self.StatusbarUpdate()
  294. def GetMapToolbar(self):
  295. """!Returns toolbar with zooming tools"""
  296. return self.toolbars['iClassMap']
  297. def OnZoomMenu(self, event):
  298. """!Popup Zoom menu """
  299. zoommenu = wx.Menu()
  300. # Add items to the menu
  301. zoomsource = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust Training Area Display to Preview Display'))
  302. zoommenu.AppendItem(zoomsource)
  303. self.Bind(wx.EVT_MENU, self.OnZoomToPreview, zoomsource)
  304. zoomtarget = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust Preview display to Training Area Display'))
  305. zoommenu.AppendItem(zoomtarget)
  306. self.Bind(wx.EVT_MENU, self.OnZoomToTraining, zoomtarget)
  307. # Popup the menu. If an item is selected then its handler
  308. # will be called before PopupMenu returns.
  309. self.PopupMenu(zoommenu)
  310. zoommenu.Destroy()
  311. def OnZoomToTraining(self, event):
  312. """!Set preview display to match extents of training display """
  313. if not self.MapWindow == self.GetSecondWindow():
  314. self.MapWindow = self.GetSecondWindow()
  315. self.Map = self.GetSecondMap()
  316. self.UpdateActive(self.GetSecondWindow())
  317. newreg = self.firstMap.GetCurrentRegion()
  318. self.GetSecondMap().region = copy.copy(newreg)
  319. self.Render(self.GetSecondWindow())
  320. def OnZoomToPreview(self, event):
  321. """!Set preview display to match extents of training display """
  322. if not self.MapWindow == self.GetFirstWindow():
  323. self.MapWindow = self.GetFirstWindow()
  324. self.Map = self.GetFirstMap()
  325. self.UpdateActive(self.GetFirstWindow())
  326. newreg = self.GetSecondMap().GetCurrentRegion()
  327. self.GetFirstMap().region = copy.copy(newreg)
  328. self.Render(self.GetFirstWindow())
  329. def OnAddBands(self, event):
  330. """!Add imagery group"""
  331. dlg = IClassGroupDialog(self, group = self.group)
  332. if dlg.ShowModal() == wx.ID_OK:
  333. group = grass.find_file(name = dlg.GetGroup(), element = 'group')
  334. if group['fullname']:
  335. self.group = group['fullname']
  336. dlg.Destroy()
  337. def OnCategoryManager(self, event):
  338. """!Show category management dialog"""
  339. dlg = IClassCategoryManagerDialog(self)
  340. dlg.Show()
  341. def CategoryChanged(self, currentCat):
  342. """!Updates everything which depends on current category.
  343. Updates number of stddev, histograms, layer in preview display.
  344. """
  345. nstd = self.statisticsDict[currentCat].nstd
  346. self.toolbars['iClass'].UpdateStddev(nstd)
  347. self.plotPanel.UpdateCategory(currentCat)
  348. self.plotPanel.OnPlotTypeSelected(None)
  349. name = self.statisticsDict[currentCat].rasterName
  350. name = self.previewMapManager.GetAlias(name)
  351. if name:
  352. self.previewMapManager.SelectLayer(name)
  353. def DeleteAreas(self, cats):
  354. """!Removes all training areas of given categories
  355. @param cats list of categories to be deleted
  356. """
  357. self.firstMapWindow.digit.DeleteAreasByCat(cats)
  358. self.firstMapWindow.UpdateMap(render=False, renderVector=True)
  359. def HighlightCategory(self, cats):
  360. """!Highlight araes given by category"""
  361. self.firstMapWindow.digit.GetDisplay().SetSelected(cats, layer = 1)
  362. self.firstMapWindow.UpdateMap(render=False, renderVector=True)
  363. def ZoomToAreasByCat(self, cat):
  364. """!Zoom to areas given by category"""
  365. n, s, w, e = self.GetFirstWindow().digit.GetDisplay().GetRegionSelected()
  366. self.GetFirstMap().GetRegion(n = n, s = s, w = w, e = e, update = True)
  367. self.GetFirstMap().AdjustRegion()
  368. self.GetFirstMap().AlignExtentFromDisplay()
  369. self.GetFirstWindow().UpdateMap(render = True, renderVector = True)
  370. def UpdateRasterName(self, newName, cat):
  371. """!Update alias of raster map when category name is changed"""
  372. origName = self.statisticsDict[cat].rasterName
  373. self.previewMapManager.SetAlias(origName, newName)
  374. def StddevChanged(self, cat, nstd):
  375. """!Standard deviation multiplier changed, rerender map, histograms"""
  376. stat = self.statisticsDict[cat]
  377. stat.nstd = nstd
  378. if not stat.IsReady():
  379. return
  380. raster = stat.rasterName
  381. cstat = self.cStatisticsDict[cat]
  382. I_iclass_statistics_set_nstd(cstat, nstd)
  383. I_iclass_create_raster(cstat, self.refer, raster)
  384. self.Render(self.GetSecondWindow())
  385. stat.SetBandStatistics(cstat)
  386. self.plotPanel.StddevChanged()
  387. def UpdateChangeState(self, changes):
  388. """!Informs if any important changes happened
  389. since last analysis computaiton.
  390. """
  391. self.changes = changes
  392. def AddRasterMap(self, name, firstMap = True, secondMap = True):
  393. """!Add raster map to Map"""
  394. cmdlist = ['d.rast', 'map=%s' % name]
  395. if firstMap:
  396. self.GetFirstMap().AddLayer(type='raster', command=cmdlist, l_active=True,
  397. name=name, l_hidden=False, l_opacity=1.0, l_render=False)
  398. self.Render(self.GetFirstWindow())
  399. if secondMap:
  400. self.GetSecondMap().AddLayer(type='raster', command=cmdlist, l_active=True,
  401. name=name, l_hidden=False, l_opacity=1.0, l_render=False)
  402. self.Render(self.GetSecondWindow())
  403. def AddTrainingAreaMap(self):
  404. """!Add vector map with training areas to Map (training
  405. sub-display)"""
  406. vname = self.CreateTempVector()
  407. if vname:
  408. self.trainingAreaVector = vname
  409. else:
  410. GMessage(parent = self, message = _("Failed to create temporary vector map."))
  411. return
  412. mapLayer = self.GetFirstMap().AddLayer(type = 'vector',
  413. command = ['d.vect', 'map=%s' % vname],
  414. name = vname, l_active = False)
  415. self.toolbars['vdigit'].StartEditing(mapLayer)
  416. self.Render(self.GetFirstWindow())
  417. def OnRunAnalysis(self, event):
  418. """!Run analysis and update plots"""
  419. if self.RunAnalysis():
  420. currentCat = self.GetCurrentCategoryIdx()
  421. self.plotPanel.UpdatePlots(group = self.group, currentCat = currentCat,
  422. statDict = self.statisticsDict,
  423. statList = self.statisticsList)
  424. def RunAnalysis(self):
  425. """!Run analysis
  426. Calls C functions to compute all statistics and creates raster maps.
  427. Signatures are created but signature file is not.
  428. """
  429. #self.firstMapWindow.digit.CloseMap()
  430. self.poMapInfo = self.firstMapWindow.digit.GetDisplay().poMapInfo
  431. if not self.CheckInput(group = self.group, vector = self.trainingAreaVector):
  432. return
  433. for statistic in self.cStatisticsDict.values():
  434. I_iclass_free_statistics(statistic)
  435. self.cStatisticsDict = {}
  436. # init Ref struct with the files in group */
  437. I_free_group_ref(self.refer)
  438. if (not I_iclass_init_group(self.group, self.refer)):
  439. return False
  440. I_free_signatures(self.signatures)
  441. I_iclass_init_signatures(self.signatures, self.refer)
  442. cats = self.statisticsList[:]
  443. for i in cats:
  444. stats = self.statisticsDict[i]
  445. statistics_obj = IClass_statistics()
  446. statistics = pointer(statistics_obj)
  447. I_iclass_init_statistics(statistics,
  448. stats.category,
  449. stats.name,
  450. stats.color,
  451. stats.nstd)
  452. if I_iclass_analysis(statistics, self.refer, self.poMapInfo, "1",
  453. self.group, stats.rasterName):
  454. # tests
  455. self.cStatisticsDict[i] = statistics
  456. stats.SetStatistics(statistics)
  457. stats.SetReady()
  458. self.statisticsDict[stats.category] = stats
  459. self.ConvertToNull(name = stats.rasterName)
  460. self.previewMapManager.AddLayer(name = stats.rasterName,
  461. alias = stats.name, resultsLayer = True)
  462. # write statistics
  463. I_iclass_add_signature(self.signatures, statistics)
  464. else:
  465. GMessage(parent = self, message = _("Analysis failed. "
  466. "Check training areas and their categories."))
  467. I_iclass_free_statistics(statistics)
  468. self.UpdateChangeState(changes = False)
  469. return True
  470. def OnSaveSigFile(self, event):
  471. """!Asks for signature file name and saves it."""
  472. if not self.group:
  473. GMessage(parent = self, message = _("No imagery group selected."))
  474. return
  475. if self.changes:
  476. qdlg = wx.MessageDialog(parent = self,
  477. message = _("Due to recent changes in classes, "
  478. "signatures can be outdated and should be recalculated. "
  479. "Do you still want to continue?") ,
  480. caption = _("Outdated signatures"),
  481. style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  482. if qdlg.ShowModal() == wx.ID_YES:
  483. qdlg.Destroy()
  484. else:
  485. qdlg.Destroy()
  486. return
  487. dlg = IClassSignatureFileDialog(self, group = self.group, file = self.sigFile)
  488. if dlg.ShowModal() == wx.ID_OK:
  489. if os.path.exists(dlg.GetFileName(fullPath = True)):
  490. qdlg = wx.MessageDialog(parent = self,
  491. message = _("A signature file named %s already exists.\n"
  492. "Do you want to replace it?") % dlg.GetFileName(),
  493. caption = _("File already exists"),
  494. style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  495. if qdlg.ShowModal() == wx.ID_YES:
  496. qdlg.Destroy()
  497. else:
  498. qdlg.Destroy()
  499. return
  500. self.sigFile = dlg.GetFileName()
  501. self.WriteSignatures(self.signatures, self.group, self.sigFile)
  502. dlg.Destroy()
  503. def InitStatistics(self):
  504. """!Initialize variables and c structures neccessary for
  505. computing statistics.
  506. """
  507. self.group = None
  508. self.sigFile = None
  509. self.statisticsDict = {}
  510. self.statisticsList = []
  511. self.cStatisticsDict = {}
  512. self.signatures_obj = Signature()
  513. self.signatures = pointer(self.signatures_obj)
  514. I_init_signatures(self.signatures, 0) # must be freed on exit
  515. refer_obj = Ref()
  516. self.refer = pointer(refer_obj)
  517. I_init_group_ref(self.refer) # must be freed on exit
  518. def WriteSignatures(self, signatures, group, filename):
  519. """!Writes current signatures to signature file
  520. @param signatures signature (c structure)
  521. @param group imagery group
  522. @param filename signature file name
  523. """
  524. I_iclass_write_signatures(signatures, group, group, filename)
  525. def CheckInput(self, group, vector):
  526. """!Check if input is valid"""
  527. # check if group is ok
  528. if not group:
  529. GMessage(parent = self,
  530. message = _("No imagery group selected. "
  531. "Operation canceled."))
  532. return False
  533. groupLayers = self.GetGroupLayers(group)
  534. nLayers = len(groupLayers)
  535. if nLayers <= 1:
  536. GMessage(parent = self,
  537. message = _("Group <%(group)s> does not have enough files "
  538. "(it has %(files)d files). Operation canceled.") % \
  539. { 'group' : group,
  540. 'files' : nLayers })
  541. return False
  542. #check if vector has any areas
  543. vectorInfo = grass.vector_info(vector)
  544. numAreas = Vect_get_num_areas(self.poMapInfo)
  545. if numAreas <= 0:
  546. GMessage(parent = self,
  547. message = _("No areas given. "
  548. "Operation canceled."))
  549. return False
  550. # check if vector is inside raster
  551. rasterInfo = grass.raster_info(groupLayers[0])
  552. if vectorInfo['north'] > rasterInfo['north'] or \
  553. vectorInfo['south'] < rasterInfo['south'] or \
  554. vectorInfo['east'] > rasterInfo['east'] or \
  555. vectorInfo['west'] < rasterInfo['west']:
  556. GMessage(parent = self,
  557. message = _("Vector features are outside raster layers. "
  558. "Operation canceled."))
  559. return False
  560. return True
  561. def GetGroupLayers(self, group):
  562. """! Get layers in group
  563. @todo consider moving this function to core module for convenient
  564. """
  565. res = RunCommand('i.group',
  566. flags = 'g',
  567. group = group,
  568. read = True).strip()
  569. if res.split('\n')[0]:
  570. return res.split('\n')
  571. return []
  572. def ConvertToNull(self, name):
  573. """! Sets value which represents null values for given raster map.
  574. @param name raster map name
  575. """
  576. RunCommand('r.null',
  577. map = name,
  578. setnull = 0)
  579. def GetCurrentCategoryIdx(self):
  580. """!Returns current category number"""
  581. return self.toolbars['iClass'].GetSelectedCategoryIdx()
  582. def OnZoomIn(self, event):
  583. """!Enable zooming for plots"""
  584. super(IClassMapFrame, self).OnZoomIn(event)
  585. self.plotPanel.EnableZoom(type = 1)
  586. def OnZoomOut(self, event):
  587. """!Enable zooming for plots"""
  588. super(IClassMapFrame, self).OnZoomOut(event)
  589. self.plotPanel.EnableZoom(type = -1)
  590. def OnPan(self, event):
  591. """!Enable paanning for plots"""
  592. super(IClassMapFrame, self).OnPan(event)
  593. self.plotPanel.EnablePan()
  594. class MapManager:
  595. """! Class for managing map renderer.
  596. It is connected with iClassMapManagerToolbar.
  597. """
  598. def __init__(self, frame, mapWindow, Map):
  599. """!
  600. It is expected that \a mapWindow is conected with \a Map.
  601. @param frame application main window
  602. @param mapWindow map window instance
  603. @param Map map renderer instance
  604. """
  605. self.map = Map
  606. self.frame = frame
  607. self.mapWindow = mapWindow
  608. self.toolbar = None
  609. self.layerName = {}
  610. def SetToolbar(self, toolbar):
  611. self.toolbar = toolbar
  612. def AddLayer(self, name, alias = None, resultsLayer = False):
  613. """!Adds layer to Map and update toolbar
  614. @param name layer (raster) name
  615. @param resultsLayer True if layer is temp. raster showing the results of computation
  616. """
  617. if (resultsLayer and
  618. name in [l.GetName() for l in self.map.GetListOfLayers(l_name = name)]):
  619. self.frame.Render(self.mapWindow)
  620. return
  621. cmdlist = ['d.rast', 'map=%s' % name]
  622. self.map.AddLayer(type = 'raster', command = cmdlist, l_active = True,
  623. name = name, l_hidden = False, l_opacity = 1.0, l_render = True)
  624. self.frame.Render(self.mapWindow)
  625. if alias is not None:
  626. alias = self._addSuffix(alias)
  627. self.layerName[alias] = name
  628. name = alias
  629. else:
  630. self.layerName[name] = name
  631. self.toolbar.choice.Insert(name, 0)
  632. self.toolbar.choice.SetSelection(0)
  633. def RemoveLayer(self, name, idx):
  634. """!Removes layer from Map and update toolbar"""
  635. self.map.GetListOfLayers(l_type = 'raster')
  636. name = self.layerName[name]
  637. self.map.RemoveLayer(name = name)
  638. del self.layerName[name]
  639. self.toolbar.choice.Delete(idx)
  640. if not self.toolbar.choice.IsEmpty():
  641. self.toolbar.choice.SetSelection(0)
  642. self.frame.Render(self.mapWindow)
  643. def SelectLayer(self, name):
  644. """!Moves selected layer to top"""
  645. layers = self.map.GetListOfLayers(l_type = 'raster')
  646. idx = None
  647. for i, layer in enumerate(layers):
  648. if self.layerName[name] == layer.GetName():
  649. idx = i
  650. break
  651. if idx is not None: # should not happen
  652. layers.append(layers.pop(idx))
  653. choice = self.toolbar.choice
  654. idx = choice.FindString(name)
  655. choice.Delete(idx)
  656. choice.Insert(name, 0)
  657. choice.SetSelection(0)
  658. #layers.reverse()
  659. self.map.ReorderLayers(layers)
  660. self.frame.Render(self.mapWindow)
  661. def SetOpacity(self, name):
  662. """!Sets opacity of layers."""
  663. name = self.layerName[name]
  664. layers = self.map.GetListOfLayers(l_name = name)
  665. if not layers:
  666. return
  667. # works for first layer only
  668. oldOpacity = layers[0].GetOpacity()
  669. dlg = SetOpacityDialog(self.frame, opacity = oldOpacity)
  670. if dlg.ShowModal() == wx.ID_OK:
  671. self.map.ChangeOpacity(layer = layers[0], l_opacity = dlg.GetOpacity())
  672. dlg.Destroy()
  673. self.frame.Render(self.mapWindow)
  674. def _addSuffix(self, name):
  675. suffix = _('results')
  676. return '_'.join((name, suffix))
  677. def GetAlias(self, name):
  678. """!Returns alias for layer"""
  679. name = [k for k, v in self.layerName.iteritems() if v == name]
  680. if name:
  681. return name[0]
  682. return None
  683. def SetAlias(self, original, alias):
  684. name = self.GetAlias(original)
  685. if name:
  686. self.layerName[self._addSuffix(alias)] = original
  687. del self.layerName[name]
  688. idx = self.toolbar.choice.FindString(name)
  689. if idx != wx.NOT_FOUND:
  690. self.toolbar.choice.SetString(idx, self._addSuffix(alias))
  691. def test():
  692. import gettext
  693. import core.render as render
  694. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  695. app = wx.PySimpleApp()
  696. wx.InitAllImageHandlers()
  697. frame = IClassMapFrame()
  698. frame.Show()
  699. app.MainLoop()
  700. if __name__ == "__main__":
  701. test()