frame.py 52 KB

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