frame.py 51 KB

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