m.proj.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #!/usr/bin/env python
  2. ############################################################################
  3. #
  4. # MODULE: m.proj
  5. # AUTHOR: M. Hamish Bowman, Dept. Marine Science, Otago Univeristy,
  6. # New Zealand
  7. # Converted to Python by Glynn Clements
  8. # PURPOSE: cs2cs reprojection frontend for a list of coordinates.
  9. # Replacement for m.proj2 from GRASS 5
  10. # COPYRIGHT: (c) 2006-2009 Hamish Bowman, and the GRASS Development Team
  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. # notes:
  17. # - cs2cs expects "x y" data so be sure to send it "lon lat" not "lat lon"
  18. # - if you send cs2cs a third data column, beware it might be treated as "z"
  19. # todo:
  20. # - `cut` away x,y columns into a temp file, feed to cs2cs, then `paste`
  21. # back to input file. see method in v.in.garmin.sh. that way additional
  22. # numeric and string columns would survive the trip, and 3rd column would
  23. # not be modified as z.
  24. #%Module
  25. #% description: Converts coordinates from one projection to another (cs2cs frontend).
  26. #% keywords: miscellaneous
  27. #% keywords: projection
  28. #%End
  29. #%option
  30. #% key: input
  31. #% type: string
  32. #% gisprompt: old_file,file,file
  33. #% description: Input coordinate file ('-' to read from stdin)
  34. #% answer: -
  35. #% required : yes
  36. #% key_desc : filename
  37. #% guisection: Files & format
  38. #%end
  39. #%option
  40. #% key: output
  41. #% type: string
  42. #% gisprompt: new_file,file,file
  43. #% description: Output coordinate file (omit to send to stdout)
  44. #% required : no
  45. #% key_desc : filename
  46. #% guisection: Files & format
  47. #%end
  48. #%option
  49. #% key: fs
  50. #% type: string
  51. #% label: Field separator (format: input[,output])
  52. #% description: Valid field separators are also "space", "tab", or "comma"
  53. #% required : no
  54. #% key_desc : string
  55. #% answer : |
  56. #% guisection: Files & format
  57. #%end
  58. #%option
  59. #% key: proj_input
  60. #% type: string
  61. #% description: Input projection parameters (PROJ.4 style)
  62. #% required : no
  63. #% guisection: Projections
  64. #%end
  65. #%option
  66. #% key: proj_output
  67. #% type: string
  68. #% description: Output projection parameters (PROJ.4 style)
  69. #% required : no
  70. #% guisection: Projections
  71. #%end
  72. #%flag
  73. #% key: i
  74. #% description: Use LL WGS84 as input and current location as output projection
  75. #% guisection: Projections
  76. #%end
  77. #%flag
  78. #% key: o
  79. #% description: Use current location as input and LL WGS84 as output projection
  80. #% guisection: Projections
  81. #%end
  82. #%flag
  83. #% key: d
  84. #% description: Output long/lat in decimal degrees, or other projections with many decimal places
  85. #% guisection: Files & format
  86. #%end
  87. #%flag
  88. #% key: e
  89. #% description: Include input coordinates in output file
  90. #% guisection: Files & format
  91. #%end
  92. #%flag
  93. #% key: c
  94. #% description: Include column names in output file
  95. #% guisection: Files & format
  96. #%end
  97. import sys
  98. import os
  99. from grass.script import core as grass
  100. def main():
  101. input = options['input']
  102. output = options['output']
  103. fs = options['fs']
  104. proj_in = options['proj_input']
  105. proj_out = options['proj_output']
  106. ll_in = flags['i']
  107. ll_out = flags['o']
  108. decimal = flags['d']
  109. copy_input = flags['e']
  110. include_header = flags['c']
  111. #### check for cs2cs
  112. if not grass.find_program('cs2cs'):
  113. grass.fatal(_("cs2cs program not found, install PROJ.4 first: http://proj.maptools.org"))
  114. #### check for overenthusiasm
  115. if proj_in and ll_in:
  116. grass.fatal(_("Choose only one input parameter method"))
  117. if proj_out and ll_out:
  118. grass.fatal(_("Choose only one output parameter method"))
  119. if ll_in and ll_out:
  120. grass.fatal(_("Choise only one auto-projection parameter method"))
  121. if output and not grass.overwrite() and os.path.exists(output):
  122. grass.fatal(_("Output file already exists"))
  123. #### parse field separator
  124. # FIXME: input_x,y needs to split on multiple whitespace between them
  125. if fs == ',':
  126. ifs = ofs = ','
  127. else:
  128. try:
  129. ifs, ofs = fs.split(',')
  130. except ValueError:
  131. ifs = ofs = fs
  132. ifs = ifs.lower()
  133. ofs = ofs.lower()
  134. if ifs in ('space', 'tab'):
  135. ifs = ' '
  136. elif ifs == 'comma':
  137. ifs = ','
  138. else:
  139. if len(ifs) > 1:
  140. grass.warning(_("Invalid field separator, using '%s'") % ifs[0])
  141. try:
  142. ifs = ifs[0]
  143. except IndexError:
  144. grass.fatal(_("Invalid field separator '%s'") % ifs)
  145. if ofs.lower() == 'space':
  146. ofs = ' '
  147. elif ofs.lower() == 'tab':
  148. ofs = '\t'
  149. elif ofs.lower() == 'comma':
  150. ofs = ','
  151. else:
  152. if len(ofs) > 1:
  153. grass.warning(_("Invalid field separator, using '%s'") % ofs[0])
  154. try:
  155. ofs = ofs[0]
  156. except IndexError:
  157. grass.fatal(_("Invalid field separator '%s'") % ifs)
  158. #### set up projection params
  159. s = grass.read_command("g.proj", flags='j')
  160. kv = grass.parse_key_val(s)
  161. if "XY location" in kv['+proj'] and (ll_in or ll_out):
  162. grass.fatal(_("Unable to project to or from a XY location"))
  163. in_proj = None
  164. if ll_in:
  165. in_proj = "+proj=longlat +datum=WGS84"
  166. grass.verbose("Assuming LL WGS84 as input, current projection as output ")
  167. if ll_out:
  168. in_proj = grass.read_command('g.proj', flags = 'jf')
  169. if proj_in:
  170. in_proj = proj_in
  171. if not in_proj:
  172. grass.verbose("Assuming current location as input")
  173. in_proj = grass.read_command('g.proj', flags = 'jf')
  174. in_proj = in_proj.strip()
  175. grass.verbose("Input parameters: '%s'" % in_proj)
  176. out_proj = None
  177. if ll_out:
  178. out_proj = "+proj=longlat +datum=WGS84"
  179. grass.verbose("Assuming current projection as input, LL WGS84 as output ")
  180. if ll_in:
  181. out_proj = grass.read_command('g.proj', flags = 'jf')
  182. if proj_out:
  183. out_proj = proj_out
  184. if not out_proj:
  185. grass.fatal(_("Missing output projection parameters "))
  186. out_proj = out_proj.strip()
  187. grass.verbose("Output parameters: '%s'" % out_proj)
  188. #### set up input file
  189. if input == '-':
  190. infile = None
  191. inf = sys.stdin
  192. else:
  193. infile = input
  194. if not os.path.exists(infile):
  195. grass.fatal(_("Unable to read input data"))
  196. inf = file(infile)
  197. grass.debug("input file=[%s]" % infile)
  198. #### set up output file
  199. if not output:
  200. outfile = None
  201. outf = sys.stdout
  202. else:
  203. outfile = output
  204. outf = open(outfile, 'w')
  205. grass.debug("output file=[%s]" % outfile)
  206. #### set up output style
  207. if not decimal:
  208. outfmt = ["-w5"]
  209. else:
  210. outfmt = ["-f", "%.8f"]
  211. if not copy_input:
  212. copyinp = []
  213. else:
  214. copyinp = ["-E"]
  215. #### do the conversion
  216. # Convert cs2cs DMS format to GRASS DMS format:
  217. # cs2cs | sed -e 's/d/:/g' -e "s/'/:/g" -e 's/"//g'
  218. cmd = ['cs2cs'] + copyinp + outfmt + in_proj.split() + ['+to'] + out_proj.split()
  219. p = grass.Popen(cmd, stdin = grass.PIPE, stdout = grass.PIPE)
  220. while True:
  221. line = inf.readline()
  222. if not line:
  223. break
  224. line = line.replace(ifs, ' ')
  225. p.stdin.write(line)
  226. p.stdin.flush()
  227. p.stdin.close()
  228. p.stdin = None
  229. exitcode = p.wait()
  230. if exitcode != 0:
  231. grass.warning(_("Projection transform probably failed, please investigate"))
  232. if not copy_input:
  233. if include_header:
  234. outf.write("x%sy%sz\n" % (ofs, ofs))
  235. for line in p.communicate()[0].splitlines():
  236. x, yz = line.split('\t')
  237. y, z = yz.split(' ')
  238. outf.write('%s%s%s%s%s\n' % \
  239. (x.strip(), ofs, y.strip(), ofs, z.strip()))
  240. else:
  241. if include_header:
  242. outf.write("input_x%sinput_y%sx%sy%sz\n" % (ofs, ofs, ofs, ofs))
  243. for line in p.communicate()[0].splitlines():
  244. inX, therest, z = line.split(' ')
  245. inY, x, y = therest.split('\t')
  246. outf.write('%s%s%s%s%s%s%s%s%s\n' % \
  247. (inX.strip(), ofs, inY.strip(), ofs, x.strip(), \
  248. ofs, y.strip(), ofs, z.strip()))
  249. if __name__ == "__main__":
  250. options, flags = grass.parser()
  251. main()