i.spectral.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #!/usr/bin/env python3
  2. ############################################################################
  3. #
  4. # MODULE: i.spectral
  5. # AUTHOR(S): Markus Neteler, 18. August 1998
  6. # Converted to Python by Glynn Clements
  7. # PURPOSE: Displays spectral response at user specified locations in
  8. # group or raster images
  9. # COPYRIGHT: (C) 1999-2013 by the GRASS Development Team
  10. #
  11. # This program is free software under the GNU General Public
  12. # License (>=v2). Read the file COPYING that comes with GRASS
  13. # for details.
  14. #
  15. #############################################################################
  16. #
  17. # this script needs gnuplot for pretty rendering
  18. # TODO: use PyPlot like the wxGUI Profiling tool
  19. #
  20. # written by Markus Neteler 18. August 1998
  21. # neteler geog.uni-hannover.de
  22. #
  23. # bugfix: 25. Nov.98/20. Jan. 1999
  24. # 3 March 2006: Added multiple images and group support by Francesco Pirotti - CIRGEO
  25. #
  26. # %Module
  27. # % description: Displays spectral response at user specified locations in group or images.
  28. # % keyword: imagery
  29. # % keyword: querying
  30. # % keyword: raster
  31. # % keyword: multispectral
  32. # %End
  33. # %option G_OPT_I_GROUP
  34. # % required : no
  35. # % guisection: Input
  36. # %end
  37. # %option G_OPT_I_SUBGROUP
  38. # % required : no
  39. # % guisection: Input
  40. # %end
  41. # %option G_OPT_R_INPUTS
  42. # % key: raster
  43. # % required : no
  44. # % guisection: Input
  45. # %end
  46. # %option G_OPT_M_COORDS
  47. # % multiple: yes
  48. # % required: yes
  49. # % guisection: Input
  50. # %end
  51. # %option G_OPT_F_OUTPUT
  52. # % key: output
  53. # % description: Name for output image is valid for -g (or text file for -t flag)
  54. # % guisection: Output
  55. # % required : no
  56. # %end
  57. # %Option
  58. # % key: format
  59. # % type: string
  60. # % description: Graphics format for output file (valid for -g flag)
  61. # % options: png,eps,svg
  62. # % answer: png
  63. # % multiple: no
  64. # % guisection: Output
  65. # %End
  66. # %flag
  67. # % key: c
  68. # % description: Show sampling coordinates instead of numbering in the legend (valid for -g flag)
  69. # %end
  70. # % flag
  71. # % key: g
  72. # % description: Use gnuplot for display
  73. # %end
  74. # % flag
  75. # % key: t
  76. # % description: output to text file
  77. # %end
  78. import os
  79. import atexit
  80. from grass.script.utils import try_rmdir
  81. from grass.script import core as gcore
  82. def cleanup():
  83. try_rmdir(tmp_dir)
  84. def write2textf(what, output):
  85. outf = open(output, 'w')
  86. i = 0
  87. for row in enumerate(what):
  88. i = i + 1
  89. outf.write("%d, %s\n" % (i, row))
  90. outf.close()
  91. def draw_gnuplot(what, xlabels, output, img_format, coord_legend):
  92. xrange = 0
  93. for i, row in enumerate(what):
  94. outfile = os.path.join(tmp_dir, 'data_%d' % i)
  95. outf = open(outfile, 'w')
  96. xrange = max(xrange, len(row) - 2)
  97. for j, val in enumerate(row[3:]):
  98. outf.write("%d %s\n" % (j + 1, val))
  99. outf.close()
  100. # build gnuplot script
  101. lines = []
  102. if output:
  103. if img_format == 'png':
  104. term_opts = "png truecolor large size 825,550"
  105. elif img_format == 'eps':
  106. term_opts = "postscript eps color solid size 6,4"
  107. elif img_format == 'svg':
  108. term_opts = "svg size 825,550 dynamic solid"
  109. else:
  110. gcore.fatal(_("Programmer error (%s)") % img_format)
  111. lines += [
  112. "set term " + term_opts,
  113. "set output '%s'" % output
  114. ]
  115. lines += [
  116. "set xtics (%s)" % xlabels,
  117. "set grid",
  118. "set title 'Spectral signatures'",
  119. "set xrange [0.5 : %d - 0.5]" % xrange,
  120. "set noclabel",
  121. "set xlabel 'Bands'",
  122. "set xtics rotate by -40",
  123. "set ylabel 'DN Value'",
  124. "set style data lines"
  125. ]
  126. cmd = []
  127. for i, row in enumerate(what):
  128. if not coord_legend:
  129. title = 'Pick ' + str(i + 1)
  130. else:
  131. title = str(tuple(row[0:2]))
  132. x_datafile = os.path.join(tmp_dir, 'data_%d' % i)
  133. cmd.append(" '%s' title '%s'" % (x_datafile, title))
  134. cmd = ','.join(cmd)
  135. cmd = ' '.join(['plot', cmd, "with linespoints pt 779"])
  136. lines.append(cmd)
  137. plotfile = os.path.join(tmp_dir, 'spectrum.gnuplot')
  138. plotf = open(plotfile, 'w')
  139. for line in lines:
  140. plotf.write(line + '\n')
  141. plotf.close()
  142. if output:
  143. gcore.call(['gnuplot', plotfile])
  144. else:
  145. gcore.call(['gnuplot', '-persist', plotfile])
  146. def draw_linegraph(what):
  147. yfiles = []
  148. xfile = os.path.join(tmp_dir, 'data_x')
  149. xf = open(xfile, 'w')
  150. for j, val in enumerate(what[0][3:]):
  151. xf.write("%d\n" % (j + 1))
  152. xf.close()
  153. for i, row in enumerate(what):
  154. yfile = os.path.join(tmp_dir, 'data_y_%d' % i)
  155. yf = open(yfile, 'w')
  156. for j, val in enumerate(row[3:]):
  157. yf.write("%s\n" % val)
  158. yf.close()
  159. yfiles.append(yfile)
  160. sienna = '#%02x%02x%02x' % (160, 82, 45)
  161. coral = '#%02x%02x%02x' % (255, 127, 80)
  162. gp_colors = ['red', 'green', 'blue', 'magenta', 'cyan', sienna, 'orange',
  163. coral]
  164. colors = gp_colors
  165. while len(what) > len(colors):
  166. colors += gp_colors
  167. colors = colors[0:len(what)]
  168. supported_monitors = ("cairo", "png", "ps")
  169. monitors = gcore.read_command("d.mon", flags="l", quiet=True)
  170. found = []
  171. for monitor in supported_monitors:
  172. if monitor in monitors:
  173. found.append(monitor)
  174. if not found:
  175. gcore.fatal(
  176. _(
  177. "Supported monitor isn't running. Please launch one of the"
  178. " monitors {}.".format(", ".join(supported_monitors))
  179. )
  180. )
  181. selected_monitor = gcore.read_command("d.mon", flags="p", quiet=True).replace(
  182. "\n", ""
  183. )
  184. if selected_monitor not in supported_monitors:
  185. gcore.fatal(
  186. _(
  187. "Supported monitor isn't selected. Please select one of the"
  188. " monitors {}.".format(", ".join(supported_monitors))
  189. )
  190. )
  191. with open(gcore.parse_command("d.mon", flags="g", quiet=True)["env"]) as f:
  192. for line in f.readlines():
  193. if "GRASS_RENDER_FILE=" in line:
  194. gcore.info(
  195. _(
  196. "{} monitor is used, output file {}".format(
  197. selected_monitor.capitalize(), line.split("=")[-1]
  198. )
  199. )
  200. )
  201. break
  202. gcore.run_command(
  203. "d.linegraph",
  204. x_file=xfile,
  205. y_file=yfiles,
  206. y_color=colors,
  207. title=_("Spectral signatures"),
  208. x_title=_("Bands"),
  209. y_title=_("DN Value"),
  210. )
  211. def main():
  212. group = options['group']
  213. subgroup = options['subgroup']
  214. raster = options['raster']
  215. output = options['output']
  216. coords = options['coordinates']
  217. img_fmt = options['format']
  218. coord_legend = flags['c']
  219. gnuplot = flags['g']
  220. textfile = flags['t']
  221. global tmp_dir
  222. tmp_dir = gcore.tempdir()
  223. if not group and not raster:
  224. gcore.fatal(_("Either group= or raster= is required"))
  225. if group and raster:
  226. gcore.fatal(_("group= and raster= are mutually exclusive"))
  227. # -t needs an output filename
  228. if textfile and not output:
  229. gcore.fatal(_("Writing to text file requires output=filename"))
  230. # check if gnuplot is present
  231. if gnuplot and not gcore.find_program('gnuplot', '-V'):
  232. gcore.fatal(_("gnuplot required, please install first"))
  233. # get data from group listing and set the x-axis labels
  234. if group:
  235. # Parse the group list output
  236. if subgroup:
  237. s = gcore.read_command('i.group', flags='g', group=group, subgroup=subgroup, quiet=True)
  238. else:
  239. s = gcore.read_command('i.group', flags='g', group=group, quiet=True)
  240. rastermaps = s.splitlines()
  241. else:
  242. # get data from list of files and set the x-axis labels
  243. rastermaps = raster.split(',')
  244. xlabels = ["'%s' %d" % (n, i + 1) for i, n in enumerate(rastermaps)]
  245. xlabels = ','.join(xlabels)
  246. # get y-data for gnuplot-data file
  247. what = []
  248. s = gcore.read_command('r.what', map=rastermaps, coordinates=coords,
  249. null='0', quiet=True)
  250. if len(s) == 0:
  251. gcore.fatal(_('No data returned from query'))
  252. for l in s.splitlines():
  253. f = l.split('|')
  254. for i, v in enumerate(f):
  255. if v in ['', '*']:
  256. f[i] = 0
  257. else:
  258. f[i] = float(v)
  259. what.append(f)
  260. # build data files
  261. if gnuplot:
  262. draw_gnuplot(what, xlabels, output, img_fmt, coord_legend)
  263. elif textfile:
  264. write2textf(what, output)
  265. else:
  266. draw_linegraph(what)
  267. if __name__ == "__main__":
  268. options, flags = gcore.parser()
  269. atexit.register(cleanup)
  270. main()