utils.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. """
  2. @package utils.py
  3. @brief Misc utilities for GRASS wxPython GUI
  4. (C) 2007-2008 by the GRASS Development Team
  5. This program is free software under the GNU General Public
  6. License (>=v2). Read the file COPYING that comes with GRASS
  7. for details.
  8. @author Martin Landa, Jachym Cepicky
  9. @date 2007-2008
  10. """
  11. import os
  12. import sys
  13. import platform
  14. import string
  15. import globalvar
  16. grassPath = os.path.join(globalvar.ETCDIR, "python")
  17. sys.path.append(grassPath)
  18. from grass.script import core as grass
  19. import gcmd
  20. try:
  21. import subprocess
  22. except:
  23. compatPath = os.path.join(globalvar.ETCWXDIR, "compat")
  24. sys.path.append(compatPath)
  25. import subprocess
  26. def normalize_whitespace(text):
  27. """!Remove redundant whitespace from a string"""
  28. return string.join( string.split(text), ' ')
  29. def GetTempfile(pref=None):
  30. """
  31. Creates GRASS temporary file using defined prefix.
  32. @todo Fix path on MS Windows/MSYS
  33. @param pref prefer the given path
  34. @return Path to file name (string) or None
  35. """
  36. import gcmd
  37. ret = gcmd.RunCommand('g.tempfile',
  38. read = True,
  39. pid = os.getpid())
  40. tempfile = ret.splitlines()[0].strip()
  41. # FIXME
  42. # ugly hack for MSYS (MS Windows)
  43. if platform.system() == 'Windows':
  44. tempfile = tempfile.replace("/", "\\")
  45. try:
  46. path, file = os.path.split(tempfile)
  47. if pref:
  48. return os.path.join(pref, file)
  49. else:
  50. return tempfile
  51. except:
  52. return None
  53. def GetLayerNameFromCmd(dcmd, fullyQualified=False, param=None,
  54. layerType=None):
  55. """!Get map name from GRASS command
  56. @param dcmd GRASS command (given as tuple)
  57. @param fullyQualified change map name to be fully qualified
  58. @param force parameter otherwise 'input'/'map'
  59. @param update change map name in command
  60. @param layerType check also layer type ('raster', 'vector', '3d-raster', ...)
  61. @return map name
  62. @return '' if no map name found in command
  63. """
  64. mapname = ''
  65. if not dcmd:
  66. return mapname
  67. if 'd.grid' == dcmd[0]:
  68. mapname = 'grid'
  69. elif 'd.geodesic' in dcmd[0]:
  70. mapname = 'geodesic'
  71. elif 'd.rhumbline' in dcmd[0]:
  72. mapname = 'rhumb'
  73. elif 'labels=' in dcmd[0]:
  74. mapname = dcmd[idx].split('=')[1]+' labels'
  75. else:
  76. for idx in range(len(dcmd)):
  77. if param and param in dcmd[idx]:
  78. break
  79. elif not param:
  80. if 'map=' in dcmd[idx] or \
  81. 'input=' in dcmd[idx] or \
  82. 'red=' in dcmd[idx] or \
  83. 'h_map=' in dcmd[idx] or \
  84. 'reliefmap' in dcmd[idx]:
  85. break
  86. if idx < len(dcmd):
  87. try:
  88. mapname = dcmd[idx].split('=')[1]
  89. except IndexError:
  90. return ''
  91. if fullyQualified and '@' not in mapname:
  92. if layerType in ('raster', 'vector', '3d-raster'):
  93. try:
  94. if layerType == 'raster':
  95. findType = 'cell'
  96. else:
  97. findType = layerType
  98. result = grass.find_file(mapname, element=findType)
  99. except AttributeError, e: # not found
  100. return ''
  101. if result:
  102. mapname = result['fullname']
  103. else:
  104. mapname += '@' + grass.gisenv()['MAPSET']
  105. else:
  106. mapname += '@' + grass.gisenv()['MAPSET']
  107. dcmd[idx] = dcmd[idx].split('=')[0] + '=' + mapname
  108. return mapname
  109. def GetValidLayerName(name):
  110. """!Make layer name SQL compliant, based on G_str_to_sql()
  111. @todo: Better use directly GRASS Python SWIG...
  112. """
  113. retName = str(name).strip()
  114. # check if name is fully qualified
  115. if '@' in retName:
  116. retName, mapset = retName.split('@')
  117. else:
  118. mapset = None
  119. for c in retName:
  120. # c = toascii(c)
  121. if not (c >= 'A' and c <= 'Z') and \
  122. not (c >= 'a' and c <= 'z') and \
  123. not (c >= '0' and c <= '9'):
  124. c = '_'
  125. if not (retName[0] >= 'A' and retName[0] <= 'Z') and \
  126. not (retName[0] >= 'a' and retName[0] <= 'z'):
  127. retName = 'x' + retName[1:]
  128. if mapset:
  129. retName = retName + '@' + mapset
  130. return retName
  131. def ListOfCatsToRange(cats):
  132. """!Convert list of category number to range(s)
  133. Used for example for d.vect cats=[range]
  134. @param cats category list
  135. @return category range string
  136. @return '' on error
  137. """
  138. catstr = ''
  139. try:
  140. cats = map(int, cats)
  141. except:
  142. return catstr
  143. i = 0
  144. while i < len(cats):
  145. next = 0
  146. j = i + 1
  147. while j < len(cats):
  148. if cats[i + next] == cats[j] - 1:
  149. next += 1
  150. else:
  151. break
  152. j += 1
  153. if next > 1:
  154. catstr += '%d-%d,' % (cats[i], cats[i + next])
  155. i += next + 1
  156. else:
  157. catstr += '%d,' % (cats[i])
  158. i += 1
  159. return catstr.strip(',')
  160. def ListOfMapsets(all=False):
  161. """!Get list of available/accessible mapsets
  162. @param all if True get list of all mapsets
  163. @return list of mapsets
  164. """
  165. mapsets = []
  166. if all:
  167. ret = gcmd.RunCommand('g.mapsets',
  168. read = True,
  169. flags = 'l',
  170. fs = ';')
  171. if ret:
  172. mapsets = ret.rstrip('\n').split(';')
  173. else:
  174. raise gcmd.CmdError(cmd = 'g.mapsets',
  175. message = _('Unable to get list of available mapsets.'))
  176. else:
  177. ret = gcmd.RunCommand('g.mapsets',
  178. read = True,
  179. flags = 'p',
  180. fs = ';')
  181. if ret:
  182. mapsets = ret.rstrip('\n').split(';')
  183. else:
  184. raise gcmd.CmdError(cmd = 'g.mapsets',
  185. message = _('Unable to get list of accessible mapsets.'))
  186. ListSortLower(mapsets)
  187. return mapsets
  188. def ListSortLower(list):
  189. """!Sort list items (not case-sensitive)"""
  190. list.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
  191. def GetVectorNumberOfLayers(vector):
  192. """!Get list of vector layers"""
  193. cmdlist = ['v.category',
  194. 'input=%s' % vector,
  195. 'option=report']
  196. layers = []
  197. ret = gcmd.RunCommand('v.category',
  198. read = True,
  199. input = vector,
  200. option = 'report')
  201. if not ret:
  202. return layers
  203. for line in ret.splitlines():
  204. if not 'Layer' in line:
  205. continue
  206. value = line.split(':')[1].strip()
  207. if '/' in value: # value/name
  208. layers.append(value.split('/')[0])
  209. else:
  210. layers.append(value)
  211. return layers
  212. def Deg2DMS(lon, lat, string = True, hemisphere = True, precision = 3):
  213. """!Convert deg value to dms string
  214. @param lon longitude (x)
  215. @param lat latitude (y)
  216. @param string True to return string otherwise tuple
  217. @param hemisphere print hemisphere
  218. @param precision seconds precision
  219. @return DMS string or tuple of values
  220. @return empty string on error
  221. """
  222. try:
  223. flat = float(lat)
  224. flon = float(lon)
  225. except ValueError:
  226. if string:
  227. return ''
  228. else:
  229. return None
  230. # fix longitude
  231. while flon > 180.0:
  232. flon -= 360.0
  233. while flon < -180.0:
  234. flon += 360.0
  235. # hemisphere
  236. if hemisphere:
  237. if flat < 0.0:
  238. flat = abs(flat)
  239. hlat = 'S'
  240. else:
  241. hlat = 'N'
  242. if flon < 0.0:
  243. hlon = 'W'
  244. flon = abs(flon)
  245. else:
  246. hlon = 'E'
  247. else:
  248. flat = abs(flat)
  249. flon = abs(flon)
  250. hlon = ''
  251. hlat = ''
  252. slat = __ll_parts(flat, precision = precision)
  253. slon = __ll_parts(flon, precision = precision)
  254. if string:
  255. return slon + hlon + '; ' + slat + hlat
  256. return (slon + hlon, slat + hlat)
  257. def DMS2Deg(lon, lat):
  258. """!Convert dms value to deg
  259. @param lon longitude (x)
  260. @param lat latitude (y)
  261. @return tuple of converted values
  262. @return ValueError on error
  263. """
  264. x = __ll_parts(lon, reverse = True)
  265. y = __ll_parts(lat, reverse = True)
  266. return (x, y)
  267. def __ll_parts(value, reverse = False, precision = 3):
  268. """!Converts deg to d:m:s string
  269. @param value value to be converted
  270. @param reverse True to convert from d:m:s to deg
  271. @param precision seconds precision (ignored if reverse is True)
  272. @return converted value (string/float)
  273. @return ValueError on error (reverse == True)
  274. """
  275. if not reverse:
  276. if value == 0.0:
  277. return '%s%.*f' % ('00:00:0', precision, 0.0)
  278. d = int(int(value))
  279. m = int((value - d) * 60)
  280. s = ((value - d) * 60 - m) * 60
  281. if m < 0:
  282. m = '00'
  283. elif m < 10:
  284. m = '0' + str(m)
  285. else:
  286. m = str(m)
  287. if s < 0:
  288. s = '00.0000'
  289. elif s < 10.0:
  290. s = '0%.*f' % (precision, s)
  291. else:
  292. s = '%.*f' % (precision, s)
  293. return str(d) + ':' + m + ':' + s
  294. else: # -> reverse
  295. try:
  296. d, m, s = value.split(':')
  297. hs = s[-1]
  298. s = s[:-1]
  299. except ValueError:
  300. try:
  301. d, m = value.split(':')
  302. hs = m[-1]
  303. m = m[:-1]
  304. s = '0.0'
  305. except ValueError:
  306. try:
  307. d = value
  308. hs = d[-1]
  309. d = d[:-1]
  310. m = '0'
  311. s = '0.0'
  312. except ValueError:
  313. raise ValueError
  314. if hs not in ('N', 'S', 'E', 'W'):
  315. raise ValueError
  316. coef = 1.0
  317. if hs in ('S', 'W'):
  318. coef = -1.0
  319. fm = int(m) / 60.0
  320. fs = float(s) / (60 * 60)
  321. return coef * (float(d) + fm + fs)
  322. def GetCmdString(cmd):
  323. """
  324. Get GRASS command as string.
  325. @param cmd GRASS command given as tuple
  326. @return command string
  327. """
  328. scmd = ''
  329. if not cmd:
  330. return ''
  331. scmd = cmd[0]
  332. for k, v in cmd[1].iteritems():
  333. scmd += ' %s=%s' % (k, v)
  334. return scmd
  335. def CmdToTuple(cmd):
  336. """!Convert command list to tuple for gcmd.RunCommand()"""
  337. if len(cmd) < 1:
  338. return None
  339. dcmd = {}
  340. for item in cmd[1:]:
  341. if '=' in item:
  342. key, value = item.split('=', 1)
  343. dcmd[str(key)] = str(value)
  344. else: # -> flags
  345. if not dcmd.has_key('flags'):
  346. dcmd['flags'] = ''
  347. dcmd['flags'] += item.replace('-', '')
  348. return (cmd[0],
  349. dcmd)
  350. def PathJoin(*args):
  351. """!Check path created by os.path.join"""
  352. path = os.path.join(*args)
  353. if platform.system() == 'Windows' and \
  354. '/' in path:
  355. return path[1].upper() + ':\\' + path[3:].replace('/', '\\')
  356. return path
  357. def reexec_with_pythonw():
  358. """!Re-execute Python on Mac OS"""
  359. if sys.platform == 'darwin' and \
  360. not sys.executable.endswith('MacOS/Python'):
  361. print >> sys.stderr, 're-executing using pythonw'
  362. os.execvp('pythonw', ['pythonw', __file__] + sys.argv[1:])
  363. def ReadEpsgCodes(path):
  364. """!Read EPSG code from the file
  365. @param path full path to the file with EPSG codes
  366. @return dictionary of EPSG code
  367. @return string on error
  368. """
  369. epsgCodeDict = dict()
  370. try:
  371. f = open(path, "r")
  372. i = 0
  373. code = None
  374. for line in f.readlines():
  375. line = line.strip()
  376. if len(line) < 1:
  377. continue
  378. if line[0] == '#':
  379. descr = line[1:].strip()
  380. elif line[0] == '<':
  381. code, params = line.split(" ", 1)
  382. code = int(code.replace('<', '').replace('>', ''))
  383. if code is not None:
  384. epsgCodeDict[code] = (descr, params)
  385. code = None
  386. i += 1
  387. f.close()
  388. except StandardError, e:
  389. return str(e)
  390. return epsgCodeDict
  391. def ReprojectCoordinates(coord, projOut, projIn = None, flags = ''):
  392. """!Reproject coordinates
  393. @param coord coordinates given as tuple
  394. @param projOut output projection
  395. @param projIn input projection (use location projection settings)
  396. @return reprojected coordinates (returned as tuple)
  397. """
  398. coors = gcmd.RunCommand('m.proj',
  399. flags = flags,
  400. input = '-',
  401. proj_input = projIn,
  402. proj_output = projOut,
  403. fs = ';',
  404. stdin = '%f;%f' % (coord[0], coord[1]),
  405. read = True)
  406. if coors:
  407. coors = coors.split(';')
  408. e = coors[0]
  409. n = coors[1]
  410. try:
  411. proj = projOut.split(' ')[0].split('=')[1]
  412. except IndexError:
  413. proj = ''
  414. if proj in ('ll', 'latlong', 'longlat') and 'd' not in flags:
  415. return (proj, (e, n))
  416. else:
  417. try:
  418. return (proj, (float(e), float(n)))
  419. except ValueError:
  420. return (None, None)
  421. return (None, None)