scatter.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. """!
  2. @package wxplot.scatter
  3. @brief Scatter plotting using PyPlot
  4. Classes:
  5. - scatter::ScatterFrame
  6. - scatter::ScatterToolbar
  7. (C) 2011 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Michael Barton, Arizona State University
  11. """
  12. import sys
  13. import wx
  14. import wx.lib.plot as plot
  15. import grass.script as grass
  16. from wxplot.base import BasePlotFrame
  17. from gui_core.toolbars import BaseToolbar
  18. from icons.icon import Icons
  19. from wxplot.dialogs import ScatterRasterDialog, PlotStatsFrame
  20. class ScatterFrame(BasePlotFrame):
  21. """!Mainframe for displaying bivariate scatter plot of two raster maps. Uses wx.lib.plot.
  22. """
  23. def __init__(self, parent, id, pos, style, size, rasterList = []):
  24. BasePlotFrame.__init__(self, parent)
  25. self.toolbar = ScatterToolbar(parent = self)
  26. self.SetToolBar(self.toolbar)
  27. self.SetLabel(_("GRASS Bivariate Scatterplot Tool"))
  28. #
  29. # Init variables
  30. #
  31. self.rasterList = rasterList
  32. self.plottype = 'scatter'
  33. self.ptitle = _('Bivariate Scatterplot') # title of window
  34. self.xlabel = _("Raster cell values") # default X-axis label
  35. self.ylabel = _("Raster cell values") # default Y-axis label
  36. self.maptype = 'raster' # default type of scatterplot
  37. self.scattertype = 'normal'
  38. self.bins = 255
  39. self.colorList = ["blue", "red", "black", "green", "yellow", "magenta", "cyan", \
  40. "aqua", "grey", "orange", "brown", "purple", "violet", \
  41. "indigo"]
  42. if len(self.rasterList) > 1: # set raster name(s) from layer manager if a map is selected
  43. self.InitRasterOpts(self.rasterList, 'scatter')
  44. self._initOpts()
  45. def _initOpts(self):
  46. """!Initialize plot options
  47. """
  48. self.InitPlotOpts('scatter')
  49. def OnCreateScatter(self, event):
  50. """!Main routine for creating a scatterplot. Uses r.stats to
  51. create a list of cell value pairs. This is passed to
  52. plot to create a scatterplot.
  53. """
  54. self.SetCursor(self.parent.cursors["default"])
  55. self.SetGraphStyle()
  56. self.SetupScatterplot()
  57. p = self.CreatePlotList()
  58. self.DrawPlot(p)
  59. def OnSelectRaster(self, event):
  60. """!Select raster map(s) to profile
  61. """
  62. dlg = ScatterRasterDialog(parent = self)
  63. if dlg.ShowModal() == wx.ID_OK:
  64. rlist = dlg.rasterList
  65. if rlist < 2:
  66. dlg.Destroy()
  67. return # need at least 2 rasters for scatterplot
  68. self.bins = dlg.bins # bins for r.stats with float and dcell maps
  69. self.scattertype = dlg.scattertype # scatterplot or bubbleplot
  70. self.rasterList = self.CreatePairs(rlist) # list of raster pairs (tuples)
  71. self.raster = self.InitRasterPairs(self.rasterList, 'scatter') # dictionary of raster pairs
  72. # plot histogram
  73. if len(self.rasterList) > 0:
  74. self.OnCreateScatter(event = None)
  75. dlg.Destroy()
  76. def CreatePairs(self, rlist):
  77. """!Transforms list of rasters into tuples of raster pairs
  78. """
  79. rasterList = []
  80. next = 'first'
  81. for r in rlist:
  82. if next == 'first':
  83. first = r
  84. next = 'second'
  85. else:
  86. second = r
  87. t = (first, second)
  88. rasterList.append(t)
  89. next = 'first'
  90. first = second = ''
  91. return rasterList
  92. def SetupScatterplot(self):
  93. """!Build data list for ploting each raster
  94. """
  95. #
  96. # initialize title string
  97. #
  98. self.ptitle = _('Bivariate Scatterplot of ')
  99. #
  100. # create a datalist for plotting for each raster pair
  101. #
  102. if len(self.rasterList) == 0: return # at least 1 pair of maps needed to plot
  103. for rpair in self.rasterList:
  104. self.raster[rpair]['datalist'] = self.CreateDatalist(rpair)
  105. # update title
  106. self.ptitle += '%s vs %s, ' % (rpair[0].split('@')[0], rpair[1].split('@')[0])
  107. self.ptitle = self.ptitle.strip(', ')
  108. #
  109. # set xlabel & ylabel based on raster maps of first pair to be plotted
  110. #
  111. units = self.raster[self.rasterList[0]][0]['units']
  112. if units != '' and units != '(none)' and units != None:
  113. self.xlabel = _('Raster cell values %s') % units
  114. else:
  115. self.xlabel = _('Raster cell values')
  116. units = self.raster[self.rasterList[0]][1]['units']
  117. if units != '' and units != '(none)' and units != None:
  118. self.ylabel = _('Raster cell values %s') % units
  119. else:
  120. self.ylabel = _('Raster cell values')
  121. def CreateDatalist(self, rpair):
  122. """!Build a list of cell value, frequency pairs for histogram
  123. frequency can be in cell counts, percents, or area
  124. """
  125. datalist = []
  126. if self.scattertype == 'bubble':
  127. freqflag = 'cn'
  128. else:
  129. freqflag = 'n'
  130. try:
  131. ret = gcmd.RunCommand("r.stats",
  132. parent = self,
  133. input = '%s,%s' % rpair,
  134. flags = freqflag,
  135. nsteps = self.bins,
  136. fs = ',',
  137. quiet = True,
  138. read = True)
  139. if not ret:
  140. return datalist
  141. for line in ret.splitlines():
  142. rast1, rast2 = line.strip().split(',')
  143. rast1 = rast1.strip()
  144. if '-' in rast1: rast1 = rast1.split('-')[0]
  145. rast2 = rast2.strip()
  146. if '-' in rast2: rast2 = rast2.split('-')[0]
  147. rast1 = rast1.encode('ascii', 'ignore')
  148. rast2 = rast2.encode('ascii', 'ignore')
  149. datalist.append((rast1,rast2))
  150. return datalist
  151. except gcmd.GException, e:
  152. gcmd.GError(parent = self,
  153. message = e.value)
  154. return None
  155. def CreatePlotList(self):
  156. """!Make list of elements to plot
  157. """
  158. # graph the cell value, frequency pairs for the histogram
  159. self.plotlist = []
  160. for rpair in self.rasterList:
  161. if 'datalist' not in self.raster[rpair] or \
  162. self.raster[rpair]['datalist'] == None: return
  163. if len(self.raster[rpair]['datalist']) > 0:
  164. col = wx.Color(self.raster[rpair]['pcolor'][0],
  165. self.raster[rpair]['pcolor'][1],
  166. self.raster[rpair]['pcolor'][2],
  167. 255)
  168. scatterpoints = plot.PolyMarker(self.raster[rpair]['datalist'],
  169. legend = ' ' + self.raster[rpair]['plegend'],
  170. colour = col,size = self.raster[rpair]['psize'],
  171. fillstyle = self.ptfilldict[self.raster[rpair]['pfill']],
  172. marker = self.raster[rpair]['ptype'])
  173. self.plotlist.append(scatterpoints)
  174. if len(self.plotlist) > 0:
  175. return self.plotlist
  176. else:
  177. return None
  178. def Update(self):
  179. """!Update histogram after changing options
  180. """
  181. self.SetGraphStyle()
  182. p = self.CreatePlotList()
  183. self.DrawPlot(p)
  184. def OnRegression(self, event):
  185. """!Displays regression information in messagebox
  186. """
  187. message = []
  188. title = _('Regression Statistics for Scatterplot(s)')
  189. for rpair in self.rasterList:
  190. if isinstance(rpair, tuple) == False: continue
  191. rast1, rast2 = rpair
  192. rast1 = rast1.split('@')[0]
  193. rast2 = rast2.split('@')[0]
  194. ret = grass.parse_command('r.regression.line',
  195. map1 = rast1,
  196. map2 = rast2,
  197. flags = 'g', quiet = True,
  198. parse = (grass.parse_key_val, { 'sep' : '=' }))
  199. eqtitle = _('Regression equation for %s vs. %s:\n\n') % (rast1, rast2)
  200. eq = _(' %s = %s + %s(%s)\n\n') % (rast2, ret['a'], ret['b'], rast1)
  201. num = _('N = %s\n') % ret['N']
  202. rval = _('R = %s\n') % ret['R']
  203. rsq = _('R-squared = %f\n') % pow(float(ret['R']), 2)
  204. ftest = _('F = %s\n') % ret['F']
  205. str = eqtitle + eq + num + rval + rsq + ftest
  206. message.append(str)
  207. stats = PlotStatsFrame(self, id = wx.ID_ANY, message = message,
  208. title = title)
  209. if stats.Show() == wx.ID_CLOSE:
  210. stats.Destroy()
  211. class ScatterToolbar(BaseToolbar):
  212. """!Toolbar for bivariate scatterplots of raster map pairs
  213. """
  214. def __init__(self, parent):
  215. BaseToolbar.__init__(self, parent)
  216. self.InitToolbar(self._toolbarData())
  217. # realize the toolbar
  218. self.Realize()
  219. def _toolbarData(self):
  220. """!Toolbar data"""
  221. icons = Icons['plot']
  222. return self._getToolbarData((('addraster', Icons['layerManager']["addRast"],
  223. self.parent.OnSelectRaster),
  224. (None, ),
  225. ('draw', icons["draw"],
  226. self.parent.OnCreateScatter),
  227. ('erase', Icons['displayWindow']["erase"],
  228. self.parent.OnErase),
  229. ('drag', Icons['displayWindow']['pan'],
  230. self.parent.OnDrag),
  231. ('zoom', Icons['displayWindow']['zoomIn'],
  232. self.parent.OnZoom),
  233. ('unzoom', Icons['displayWindow']['zoomBack'],
  234. self.parent.OnRedraw),
  235. (None, ),
  236. ('statistics', icons['statistics'],
  237. self.parent.OnRegression),
  238. ('image', Icons['displayWindow']["saveFile"],
  239. self.parent.SaveToFile),
  240. ('print', Icons['displayWindow']["print"],
  241. self.parent.PrintMenu),
  242. (None, ),
  243. ('settings', icons["options"],
  244. self.parent.PlotOptionsMenu),
  245. ('quit', icons["quit"],
  246. self.parent.OnQuit),
  247. ))