scatter.py 11 KB

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