plots.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. """!
  2. @package iclass.plots
  3. @brief wxIClass plots (histograms, coincidence plots).
  4. Classes:
  5. - plots::PlotPanel
  6. (C) 2006-2011 by the GRASS Development Team
  7. This program is free software under the GNU General Public
  8. License (>=v2). Read the file COPYING that comes with GRASS
  9. for details.
  10. @author Vaclav Petras <wenzeslaus gmail.com>
  11. @author Anna Kratochvilova <kratochanna gmail.com>
  12. """
  13. import wx
  14. import wx.lib.plot as plot
  15. import wx.lib.scrolledpanel as scrolled
  16. from core.utils import _
  17. class PlotPanel(scrolled.ScrolledPanel):
  18. """!Panel for drawing multiple plots.
  19. There are two types of plots: histograms and coincidence plots.
  20. Histograms show frequency of cell category values in training areas
  21. for each band and for one category. Coincidence plots show min max range
  22. of classes for each band.
  23. """
  24. def __init__(self, parent, stats_data):
  25. scrolled.ScrolledPanel.__init__(self, parent)
  26. self.SetupScrolling(scroll_x = False, scroll_y = True)
  27. self.parent = parent
  28. self.canvasList = []
  29. self.bandList = []
  30. self.stats_data = stats_data
  31. self.currentCat = None
  32. self.mainSizer = wx.BoxSizer(wx.VERTICAL)
  33. self._createControlPanel()
  34. self.SetSizer(self.mainSizer)
  35. self.mainSizer.Fit(self)
  36. self.Layout()
  37. def _createControlPanel(self):
  38. self.plotSwitch = wx.Choice(self, id = wx.ID_ANY,
  39. choices = [_("Histograms"),
  40. _("Coincident plots")])
  41. self.mainSizer.Add(self.plotSwitch, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
  42. self.plotSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
  43. def OnPlotTypeSelected(self, event):
  44. """!Plot type selected"""
  45. if self.currentCat is None:
  46. return
  47. if self.plotSwitch.GetSelection() == 0:
  48. stat = self.stats_data.GetStatistics(self.currentCat)
  49. if not stat.IsReady():
  50. self.ClearPlots()
  51. return
  52. self.DrawHistograms(stat)
  53. else:
  54. self.DrawCoincidencePlots()
  55. def StddevChanged(self):
  56. """!Standard deviation multiplier changed, redraw histograms"""
  57. if self.plotSwitch.GetSelection() == 0:
  58. stat = self.stats_data.GetStatistics(self.currentCat)
  59. self.UpdateRanges(stat)
  60. def EnableZoom(self, type, enable = True):
  61. for canvas in self.canvasList:
  62. canvas.SetEnableZoom(enable)
  63. #canvas.zoom = type
  64. def EnablePan(self, enable = True):
  65. for canvas in self.canvasList:
  66. canvas.SetEnableDrag(enable)
  67. def DestroyPlots(self):
  68. """!Destroy all plot canvases"""
  69. for panel in self.canvasList:
  70. panel.Destroy()
  71. self.canvasList = []
  72. def ClearPlots(self):
  73. """!Clears plot canvases"""
  74. for bandIdx in range(len(self.bandList)):
  75. self.canvasList[bandIdx].Clear()
  76. def Reset(self):
  77. """!Reset plots (when new map imported)"""
  78. self.currentCat = None
  79. self.ClearPlots()
  80. # bands are still the same
  81. def CreatePlotCanvases(self):
  82. """!Create plot canvases according to the number of bands"""
  83. for band in self.bandList:
  84. canvas = plot.PlotCanvas(self)
  85. canvas.SetMinSize((-1, 140))
  86. canvas.SetFontSizeTitle(10)
  87. canvas.SetFontSizeAxis(8)
  88. self.canvasList.append(canvas)
  89. self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
  90. self.SetVirtualSize(self.GetBestVirtualSize())
  91. self.Layout()
  92. def UpdatePlots(self, group, subgroup, currentCat, stats_data):
  93. """!Update plots after new analysis
  94. @param group imagery group
  95. @param subgroup imagery group
  96. @param currentCat currently selected category (class)
  97. @param stats_data StatisticsData instance (defined in statistics.py)
  98. """
  99. self.stats_data = stats_data
  100. self.currentCat = currentCat
  101. self.bandList = self.parent.GetGroupLayers(group, subgroup)
  102. graphType = self.plotSwitch.GetSelection()
  103. stat = self.stats_data.GetStatistics(currentCat)
  104. if not stat.IsReady() and graphType == 0:
  105. return
  106. self.DestroyPlots()
  107. self.CreatePlotCanvases()
  108. self.OnPlotTypeSelected(None)
  109. def UpdateCategory(self, cat):
  110. self.currentCat = cat
  111. def DrawCoincidencePlots(self):
  112. """!Draw coincidence plots"""
  113. for bandIdx in range(len(self.bandList)):
  114. self.canvasList[bandIdx].SetYSpec(type = 'none')
  115. lines = []
  116. level = 0.5
  117. lines.append(self.DrawInvisibleLine(level))
  118. cats = self.stats_data.GetCategories()
  119. for i, cat in enumerate(cats):
  120. stat = self.stats_data.GetStatistics(cat)
  121. if not stat.IsReady():
  122. continue
  123. color = stat.color
  124. level = i + 1
  125. line = self.DrawCoincidenceLine(level, color, stat.bands[bandIdx])
  126. lines.append(line)
  127. # invisible
  128. level += 0.5
  129. lines.append(self.DrawInvisibleLine(level))
  130. plotGraph = plot.PlotGraphics(lines, title = self.bandList[bandIdx])
  131. self.canvasList[bandIdx].Draw(plotGraph)
  132. def DrawCoincidenceLine(self, level, color, bandValues):
  133. """!Draw line between band min and max values
  134. @param level y coordinate of line
  135. @param color class color
  136. @param bandValues BandStatistics instance
  137. """
  138. minim = bandValues.min
  139. maxim = bandValues.max
  140. points = [(minim, level), (maxim, level)]
  141. color = wx.Colour(*map(int, color.split(':')))
  142. return plot.PolyLine(points, colour = color, width = 4)
  143. def DrawInvisibleLine(self, level):
  144. """!Draw white line to achieve better margins"""
  145. points = [(100, level), (101, level)]
  146. return plot.PolyLine(points, colour = wx.WHITE, width = 1)
  147. def DrawHistograms(self, statistics):
  148. """!Draw histograms for one class
  149. @param statistics statistics for one class
  150. """
  151. self.histogramLines = []
  152. for bandIdx in range(len(self.bandList)):
  153. self.canvasList[bandIdx].Clear()
  154. self.canvasList[bandIdx].SetYSpec(type = 'auto')
  155. histgramLine = self.CreateHistogramLine(bandValues = statistics.bands[bandIdx])
  156. meanLine = self.CreateMean(bandValues = statistics.bands[bandIdx])
  157. minLine = self.CreateMin(bandValues = statistics.bands[bandIdx])
  158. maxLine = self.CreateMax(bandValues = statistics.bands[bandIdx])
  159. self.histogramLines.append([histgramLine, meanLine, minLine, maxLine])
  160. maxRangeLine = self.CreateMaxRange(bandValues = statistics.bands[bandIdx])
  161. minRangeLine = self.CreateMinRange(bandValues = statistics.bands[bandIdx])
  162. plotGraph = plot.PlotGraphics(self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
  163. title = self.bandList[bandIdx])
  164. self.canvasList[bandIdx].Draw(plotGraph)
  165. def CreateMinRange(self, bandValues):
  166. maxVal = max(bandValues.histo)
  167. rMin = bandValues.rangeMin
  168. points = [(rMin, 0), (rMin, maxVal)]
  169. return plot.PolyLine(points, colour = wx.RED, width = 1)
  170. def CreateMaxRange(self, bandValues):
  171. maxVal = max(bandValues.histo)
  172. rMax = bandValues.rangeMax
  173. points = [(rMax, 0), (rMax, maxVal)]
  174. return plot.PolyLine(points, colour = wx.RED, width = 1)
  175. def CreateMean(self, bandValues):
  176. maxVal = max(bandValues.histo)
  177. mean = bandValues.mean
  178. points = [(mean, 0), (mean, maxVal)]
  179. return plot.PolyLine(points, colour = wx.BLUE, width = 1)
  180. def CreateMin(self, bandValues):
  181. maxVal = max(bandValues.histo)
  182. minim = bandValues.min
  183. points = [(minim, 0), (minim, maxVal)]
  184. return plot.PolyLine(points, colour = wx.Colour(200, 200, 200), width = 1)
  185. def CreateMax(self, bandValues):
  186. maxVal = max(bandValues.histo)
  187. maxim = bandValues.max
  188. points = [(maxim, 0), (maxim, maxVal)]
  189. return plot.PolyLine(points, colour = wx.Colour(200, 200, 200), width = 1)
  190. def CreateHistogramLine(self, bandValues):
  191. points = []
  192. for cellCat, count in enumerate(bandValues.histo):
  193. if cellCat < bandValues.min - 5:
  194. continue
  195. if cellCat > bandValues.max + 5:
  196. break
  197. points.append((cellCat, count))
  198. return plot.PolyLine(points, colour = wx.BLACK, width = 1)
  199. def UpdateRanges(self, statistics):
  200. """!Redraw ranges lines in histograms when std dev multiplier changes
  201. @param statistics python Statistics instance
  202. """
  203. for bandIdx in range(len(self.bandList)):
  204. self.canvasList[bandIdx].Clear()
  205. maxRangeLine = self.CreateMaxRange(bandValues = statistics.bands[bandIdx])
  206. minRangeLine = self.CreateMinRange(bandValues = statistics.bands[bandIdx])
  207. plotGraph = plot.PlotGraphics(self.histogramLines[bandIdx] + [minRangeLine, maxRangeLine],
  208. title = self.bandList[bandIdx])
  209. self.canvasList[bandIdx].Draw(plotGraph)