utils.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. """!
  2. @package core.utils
  3. @brief Misc utilities for wxGUI
  4. (C) 2007-2009, 2011-2012 by the GRASS Development Team
  5. This program is free software under the GNU General Public License
  6. (>=v2). Read the file COPYING that comes with GRASS for details.
  7. @author Martin Landa <landa.martin gmail.com>
  8. @author Jachym Cepicky
  9. """
  10. import os
  11. import sys
  12. import platform
  13. import string
  14. import glob
  15. import shlex
  16. import re
  17. from core.globalvar import ETCDIR
  18. sys.path.append(os.path.join(ETCDIR, "python"))
  19. from grass.script import core as grass
  20. from grass.script import task as gtask
  21. from core.gcmd import RunCommand
  22. from core.debug import Debug
  23. # from core.settings import UserSettings
  24. def normalize_whitespace(text):
  25. """!Remove redundant whitespace from a string"""
  26. return string.join(string.split(text), ' ')
  27. def split(s):
  28. """!Platform spefic shlex.split"""
  29. if sys.version_info >= (2, 6):
  30. return shlex.split(s, posix = (sys.platform != "win32"))
  31. elif sys.platform == "win32":
  32. return shlex.split(s.replace('\\', r'\\'))
  33. else:
  34. return shlex.split(s)
  35. def GetTempfile(pref=None):
  36. """!Creates GRASS temporary file using defined prefix.
  37. @todo Fix path on MS Windows/MSYS
  38. @param pref prefer the given path
  39. @return Path to file name (string) or None
  40. """
  41. ret = RunCommand('g.tempfile',
  42. read = True,
  43. pid = os.getpid())
  44. tempfile = ret.splitlines()[0].strip()
  45. # FIXME
  46. # ugly hack for MSYS (MS Windows)
  47. if platform.system() == 'Windows':
  48. tempfile = tempfile.replace("/", "\\")
  49. try:
  50. path, file = os.path.split(tempfile)
  51. if pref:
  52. return os.path.join(pref, file)
  53. else:
  54. return tempfile
  55. except:
  56. return None
  57. def GetLayerNameFromCmd(dcmd, fullyQualified = False, param = None,
  58. layerType = None):
  59. """!Get map name from GRASS command
  60. Parameter dcmd can be modified when first parameter is not
  61. defined.
  62. @param dcmd GRASS command (given as list)
  63. @param fullyQualified change map name to be fully qualified
  64. @param param params directory
  65. @param layerType check also layer type ('raster', 'vector', '3d-raster', ...)
  66. @return tuple (name, found)
  67. """
  68. mapname = ''
  69. found = True
  70. if len(dcmd) < 1:
  71. return mapname, False
  72. if 'd.grid' == dcmd[0]:
  73. mapname = 'grid'
  74. elif 'd.geodesic' in dcmd[0]:
  75. mapname = 'geodesic'
  76. elif 'd.rhumbline' in dcmd[0]:
  77. mapname = 'rhumb'
  78. else:
  79. params = list()
  80. for idx in range(len(dcmd)):
  81. try:
  82. p, v = dcmd[idx].split('=', 1)
  83. except ValueError:
  84. continue
  85. if p == param:
  86. params = [(idx, p, v)]
  87. break
  88. if p in ('map', 'input', 'layer',
  89. 'red', 'blue', 'green',
  90. 'h_map', 's_map', 'i_map',
  91. 'reliefmap', 'labels'):
  92. params.append((idx, p, v))
  93. if len(params) < 1:
  94. if len(dcmd) > 1:
  95. i = 1
  96. while i < len(dcmd):
  97. if '=' not in dcmd[i] and not dcmd[i].startswith('-'):
  98. task = gtask.parse_interface(dcmd[0])
  99. # this expects the first parameter to be the right one
  100. p = task.get_options()['params'][0].get('name', '')
  101. params.append((i, p, dcmd[i]))
  102. break
  103. i += 1
  104. else:
  105. return mapname, False
  106. if len(params) < 1:
  107. return mapname, False
  108. # need to add mapset for all maps
  109. mapsets = {}
  110. for i, p, v in params:
  111. if p == 'layer':
  112. continue
  113. mapname = v
  114. mapset = ''
  115. if fullyQualified and '@' not in mapname:
  116. if layerType in ('raster', 'vector', '3d-raster', 'rgb', 'his'):
  117. try:
  118. if layerType in ('raster', 'rgb', 'his'):
  119. findType = 'cell'
  120. else:
  121. findType = layerType
  122. mapset = grass.find_file(mapname, element = findType)['mapset']
  123. except AttributeError: # not found
  124. return '', False
  125. if not mapset:
  126. found = False
  127. else:
  128. mapset = grass.gisenv()['MAPSET']
  129. mapsets[i] = mapset
  130. # update dcmd
  131. for i, p, v in params:
  132. if p == 'layer':
  133. continue
  134. dcmd[i] = p + '=' + v
  135. if i in mapsets and mapsets[i]:
  136. dcmd[i] += '@' + mapsets[i]
  137. maps = list()
  138. ogr = False
  139. for i, p, v in params:
  140. if v.lower().rfind('@ogr') > -1:
  141. ogr = True
  142. if p == 'layer' and not ogr:
  143. continue
  144. maps.append(dcmd[i].split('=', 1)[1])
  145. mapname = '\n'.join(maps)
  146. return mapname, found
  147. def GetValidLayerName(name):
  148. """!Make layer name SQL compliant, based on G_str_to_sql()
  149. @todo: Better use directly GRASS Python SWIG...
  150. """
  151. retName = str(name).strip()
  152. # check if name is fully qualified
  153. if '@' in retName:
  154. retName, mapset = retName.split('@')
  155. else:
  156. mapset = None
  157. cIdx = 0
  158. retNameList = list(retName)
  159. for c in retNameList:
  160. if not (c >= 'A' and c <= 'Z') and \
  161. not (c >= 'a' and c <= 'z') and \
  162. not (c >= '0' and c <= '9'):
  163. retNameList[cIdx] = '_'
  164. cIdx += 1
  165. retName = ''.join(retNameList)
  166. if not (retName[0] >= 'A' and retName[0] <= 'Z') and \
  167. not (retName[0] >= 'a' and retName[0] <= 'z'):
  168. retName = 'x' + retName[1:]
  169. if mapset:
  170. retName = retName + '@' + mapset
  171. return retName
  172. def ListOfCatsToRange(cats):
  173. """!Convert list of category number to range(s)
  174. Used for example for d.vect cats=[range]
  175. @param cats category list
  176. @return category range string
  177. @return '' on error
  178. """
  179. catstr = ''
  180. try:
  181. cats = map(int, cats)
  182. except:
  183. return catstr
  184. i = 0
  185. while i < len(cats):
  186. next = 0
  187. j = i + 1
  188. while j < len(cats):
  189. if cats[i + next] == cats[j] - 1:
  190. next += 1
  191. else:
  192. break
  193. j += 1
  194. if next > 1:
  195. catstr += '%d-%d,' % (cats[i], cats[i + next])
  196. i += next + 1
  197. else:
  198. catstr += '%d,' % (cats[i])
  199. i += 1
  200. return catstr.strip(',')
  201. def ListOfMapsets(get = 'ordered'):
  202. """!Get list of available/accessible mapsets
  203. @param get method ('all', 'accessible', 'ordered')
  204. @return list of mapsets
  205. @return None on error
  206. """
  207. mapsets = []
  208. if get == 'all' or get == 'ordered':
  209. ret = RunCommand('g.mapsets',
  210. read = True,
  211. quiet = True,
  212. flags = 'l',
  213. sep = 'newline')
  214. if ret:
  215. mapsets = ret.splitlines()
  216. ListSortLower(mapsets)
  217. else:
  218. return None
  219. if get == 'accessible' or get == 'ordered':
  220. ret = RunCommand('g.mapsets',
  221. read = True,
  222. quiet = True,
  223. flags = 'p',
  224. sep = 'newline')
  225. if ret:
  226. if get == 'accessible':
  227. mapsets = ret.splitlines()
  228. else:
  229. mapsets_accessible = ret.splitlines()
  230. for mapset in mapsets_accessible:
  231. mapsets.remove(mapset)
  232. mapsets = mapsets_accessible + mapsets
  233. else:
  234. return None
  235. return mapsets
  236. def ListSortLower(list):
  237. """!Sort list items (not case-sensitive)"""
  238. list.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
  239. def GetVectorNumberOfLayers(vector):
  240. """!Get list of vector layers"""
  241. layers = list()
  242. if not vector:
  243. return layers
  244. fullname = grass.find_file(name = vector, element = 'vector')['fullname']
  245. if not fullname:
  246. Debug.msg(5, "utils.GetVectorNumberOfLayers(): vector map '%s' not found" % vector)
  247. return layers
  248. ret, out, msg = RunCommand('v.db.connect',
  249. getErrorMsg = True,
  250. read = True,
  251. flags = 'g',
  252. map = fullname,
  253. sep = ';')
  254. if ret != 0:
  255. sys.stderr.write(_("Vector map <%(map)s>: %(msg)s\n") % { 'map' : fullname, 'msg' : msg })
  256. return layers
  257. else:
  258. Debug.msg(1, "GetVectorNumberOfLayers(): ret %s" % ret)
  259. for line in ret.splitlines():
  260. try:
  261. layer = line.split(';')[0]
  262. if '/' in layer:
  263. layer = layer.split('/')[0]
  264. layers.append(layer)
  265. except IndexError:
  266. pass
  267. Debug.msg(3, "utils.GetVectorNumberOfLayers(): vector=%s -> %s" % \
  268. (fullname, ','.join(layers)))
  269. return layers
  270. def Deg2DMS(lon, lat, string = True, hemisphere = True, precision = 3):
  271. """!Convert deg value to dms string
  272. @param lon longitude (x)
  273. @param lat latitude (y)
  274. @param string True to return string otherwise tuple
  275. @param hemisphere print hemisphere
  276. @param precision seconds precision
  277. @return DMS string or tuple of values
  278. @return empty string on error
  279. """
  280. try:
  281. flat = float(lat)
  282. flon = float(lon)
  283. except ValueError:
  284. if string:
  285. return ''
  286. else:
  287. return None
  288. # fix longitude
  289. while flon > 180.0:
  290. flon -= 360.0
  291. while flon < -180.0:
  292. flon += 360.0
  293. # hemisphere
  294. if hemisphere:
  295. if flat < 0.0:
  296. flat = abs(flat)
  297. hlat = 'S'
  298. else:
  299. hlat = 'N'
  300. if flon < 0.0:
  301. hlon = 'W'
  302. flon = abs(flon)
  303. else:
  304. hlon = 'E'
  305. else:
  306. flat = abs(flat)
  307. flon = abs(flon)
  308. hlon = ''
  309. hlat = ''
  310. slat = __ll_parts(flat, precision = precision)
  311. slon = __ll_parts(flon, precision = precision)
  312. if string:
  313. return slon + hlon + '; ' + slat + hlat
  314. return (slon + hlon, slat + hlat)
  315. def DMS2Deg(lon, lat):
  316. """!Convert dms value to deg
  317. @param lon longitude (x)
  318. @param lat latitude (y)
  319. @return tuple of converted values
  320. @return ValueError on error
  321. """
  322. x = __ll_parts(lon, reverse = True)
  323. y = __ll_parts(lat, reverse = True)
  324. return (x, y)
  325. def __ll_parts(value, reverse = False, precision = 3):
  326. """!Converts deg to d:m:s string
  327. @param value value to be converted
  328. @param reverse True to convert from d:m:s to deg
  329. @param precision seconds precision (ignored if reverse is True)
  330. @return converted value (string/float)
  331. @return ValueError on error (reverse == True)
  332. """
  333. if not reverse:
  334. if value == 0.0:
  335. return '%s%.*f' % ('00:00:0', precision, 0.0)
  336. d = int(int(value))
  337. m = int((value - d) * 60)
  338. s = ((value - d) * 60 - m) * 60
  339. if m < 0:
  340. m = '00'
  341. elif m < 10:
  342. m = '0' + str(m)
  343. else:
  344. m = str(m)
  345. if s < 0:
  346. s = '00.0000'
  347. elif s < 10.0:
  348. s = '0%.*f' % (precision, s)
  349. else:
  350. s = '%.*f' % (precision, s)
  351. return str(d) + ':' + m + ':' + s
  352. else: # -> reverse
  353. try:
  354. d, m, s = value.split(':')
  355. hs = s[-1]
  356. s = s[:-1]
  357. except ValueError:
  358. try:
  359. d, m = value.split(':')
  360. hs = m[-1]
  361. m = m[:-1]
  362. s = '0.0'
  363. except ValueError:
  364. try:
  365. d = value
  366. hs = d[-1]
  367. d = d[:-1]
  368. m = '0'
  369. s = '0.0'
  370. except ValueError:
  371. raise ValueError
  372. if hs not in ('N', 'S', 'E', 'W'):
  373. raise ValueError
  374. coef = 1.0
  375. if hs in ('S', 'W'):
  376. coef = -1.0
  377. fm = int(m) / 60.0
  378. fs = float(s) / (60 * 60)
  379. return coef * (float(d) + fm + fs)
  380. def GetCmdString(cmd):
  381. """!Get GRASS command as string.
  382. @param cmd GRASS command given as tuple
  383. @return command string
  384. """
  385. return ' '.join(CmdTupleToList(cmd))
  386. def CmdTupleToList(cmd):
  387. """!Convert command tuple to list.
  388. @param cmd GRASS command given as tuple
  389. @return command in list
  390. """
  391. cmdList = []
  392. if not cmd:
  393. return cmdList
  394. cmdList.append(cmd[0])
  395. if 'flags' in cmd[1]:
  396. for flag in cmd[1]['flags']:
  397. cmdList.append('-' + flag)
  398. for flag in ('verbose', 'quiet', 'overwrite'):
  399. if flag in cmd[1] and cmd[1][flag] is True:
  400. cmdList.append('--' + flag)
  401. for k, v in cmd[1].iteritems():
  402. if k in ('flags', 'verbose', 'quiet', 'overwrite'):
  403. continue
  404. cmdList.append('%s=%s' % (k, v))
  405. return cmdList
  406. def CmdToTuple(cmd):
  407. """!Convert command list to tuple for gcmd.RunCommand()"""
  408. if len(cmd) < 1:
  409. return None
  410. dcmd = {}
  411. for item in cmd[1:]:
  412. if '=' in item: # params
  413. key, value = item.split('=', 1)
  414. dcmd[str(key)] = str(value).replace('"', '')
  415. elif item[:2] == '--': # long flags
  416. flag = item[2:]
  417. if flag in ('verbose', 'quiet', 'overwrite'):
  418. dcmd[str(flag)] = True
  419. elif len(item) == 2 and item[0] == '-': # -> flags
  420. if 'flags' not in dcmd:
  421. dcmd['flags'] = ''
  422. dcmd['flags'] += item[1]
  423. else: # unnamed parameter
  424. module = gtask.parse_interface(cmd[0])
  425. dcmd[module.define_first()] = item
  426. return (cmd[0], dcmd)
  427. def PathJoin(*args):
  428. """!Check path created by os.path.join"""
  429. path = os.path.join(*args)
  430. if platform.system() == 'Windows' and \
  431. '/' in path:
  432. return path[1].upper() + ':\\' + path[3:].replace('/', '\\')
  433. return path
  434. def ReadEpsgCodes(path):
  435. """!Read EPSG code from the file
  436. @param path full path to the file with EPSG codes
  437. @return dictionary of EPSG code
  438. @return string on error
  439. """
  440. epsgCodeDict = dict()
  441. try:
  442. try:
  443. f = open(path, "r")
  444. except IOError:
  445. return _("failed to open '%s'" % path)
  446. code = None
  447. for line in f.readlines():
  448. line = line.strip()
  449. if len(line) < 1:
  450. continue
  451. if line[0] == '#':
  452. descr = line[1:].strip()
  453. elif line[0] == '<':
  454. code, params = line.split(" ", 1)
  455. try:
  456. code = int(code.replace('<', '').replace('>', ''))
  457. except ValueError, e:
  458. return e
  459. if code is not None:
  460. epsgCodeDict[code] = (descr, params)
  461. code = None
  462. f.close()
  463. except StandardError, e:
  464. return e
  465. return epsgCodeDict
  466. def ReprojectCoordinates(coord, projOut, projIn = None, flags = ''):
  467. """!Reproject coordinates
  468. @param coord coordinates given as tuple
  469. @param projOut output projection
  470. @param projIn input projection (use location projection settings)
  471. @return reprojected coordinates (returned as tuple)
  472. """
  473. coors = RunCommand('m.proj',
  474. flags = flags,
  475. input = '-',
  476. proj_input = projIn,
  477. proj_output = projOut,
  478. sep = ';',
  479. stdin = '%f;%f' % (coord[0], coord[1]),
  480. read = True)
  481. if coors:
  482. coors = coors.split(';')
  483. e = coors[0]
  484. n = coors[1]
  485. try:
  486. proj = projOut.split(' ')[0].split('=')[1]
  487. except IndexError:
  488. proj = ''
  489. if proj in ('ll', 'latlong', 'longlat') and 'd' not in flags:
  490. return (proj, (e, n))
  491. else:
  492. try:
  493. return (proj, (float(e), float(n)))
  494. except ValueError:
  495. return (None, None)
  496. return (None, None)
  497. def GetListOfLocations(dbase):
  498. """!Get list of GRASS locations in given dbase
  499. @param dbase GRASS database path
  500. @return list of locations (sorted)
  501. """
  502. listOfLocations = list()
  503. try:
  504. for location in glob.glob(os.path.join(dbase, "*")):
  505. try:
  506. if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")):
  507. listOfLocations.append(os.path.basename(location))
  508. except:
  509. pass
  510. except UnicodeEncodeError, e:
  511. raise e
  512. ListSortLower(listOfLocations)
  513. return listOfLocations
  514. def GetListOfMapsets(dbase, location, selectable = False):
  515. """!Get list of mapsets in given GRASS location
  516. @param dbase GRASS database path
  517. @param location GRASS location
  518. @param selectable True to get list of selectable mapsets, otherwise all
  519. @return list of mapsets - sorted (PERMANENT first)
  520. """
  521. listOfMapsets = list()
  522. if selectable:
  523. ret = RunCommand('g.mapset',
  524. read = True,
  525. flags = 'l',
  526. location = location,
  527. gisdbase = dbase)
  528. if not ret:
  529. return listOfMapsets
  530. for line in ret.rstrip().splitlines():
  531. listOfMapsets += line.split(' ')
  532. else:
  533. for mapset in glob.glob(os.path.join(dbase, location, "*")):
  534. if os.path.isdir(mapset) and \
  535. os.path.isfile(os.path.join(dbase, location, mapset, "WIND")):
  536. listOfMapsets.append(os.path.basename(mapset))
  537. ListSortLower(listOfMapsets)
  538. return listOfMapsets
  539. def GetColorTables():
  540. """!Get list of color tables"""
  541. ret = RunCommand('r.colors',
  542. read = True,
  543. flags = 'l')
  544. if not ret:
  545. return list()
  546. return ret.splitlines()
  547. def _getGDALFormats():
  548. """!Get dictionary of avaialble GDAL drivers"""
  549. try:
  550. ret = grass.read_command('r.in.gdal',
  551. quiet = True,
  552. flags = 'f')
  553. except:
  554. ret = None
  555. return _parseFormats(ret), _parseFormats(ret, writableOnly = True)
  556. def _getOGRFormats():
  557. """!Get dictionary of avaialble OGR drivers"""
  558. try:
  559. ret = grass.read_command('v.in.ogr',
  560. quiet = True,
  561. flags = 'f')
  562. except:
  563. ret = None
  564. return _parseFormats(ret), _parseFormats(ret, writableOnly = True)
  565. def _parseFormats(output, writableOnly = False):
  566. """!Parse r.in.gdal/v.in.ogr -f output"""
  567. formats = { 'file' : list(),
  568. 'database' : list(),
  569. 'protocol' : list()
  570. }
  571. if not output:
  572. return formats
  573. patt = None
  574. if writableOnly:
  575. patt = re.compile('\(rw\+?\)$', re.IGNORECASE)
  576. for line in output.splitlines():
  577. key, name = map(lambda x: x.strip(), line.strip().rsplit(':', -1))
  578. if writableOnly and not patt.search(key):
  579. continue
  580. if name in ('Memory', 'Virtual Raster', 'In Memory Raster'):
  581. continue
  582. if name in ('PostgreSQL', 'SQLite',
  583. 'ODBC', 'ESRI Personal GeoDatabase',
  584. 'Rasterlite',
  585. 'PostGIS WKT Raster driver',
  586. 'PostGIS Raster driver',
  587. 'CouchDB',
  588. 'MSSQLSpatial'):
  589. formats['database'].append(name)
  590. elif name in ('GeoJSON',
  591. 'OGC Web Coverage Service',
  592. 'OGC Web Map Service',
  593. 'WFS',
  594. 'GeoRSS',
  595. 'HTTP Fetching Wrapper'):
  596. formats['protocol'].append(name)
  597. else:
  598. formats['file'].append(name)
  599. for items in formats.itervalues():
  600. items.sort()
  601. return formats
  602. formats = None
  603. def GetFormats(writableOnly = False):
  604. """!Get GDAL/OGR formats"""
  605. global formats
  606. if not formats:
  607. gdalAll, gdalWritable = _getGDALFormats()
  608. ogrAll, ogrWritable = _getOGRFormats()
  609. formats = {
  610. 'all' : {
  611. 'gdal' : gdalAll,
  612. 'ogr' : ogrAll,
  613. },
  614. 'writable' : {
  615. 'gdal' : gdalWritable,
  616. 'ogr' : ogrWritable,
  617. },
  618. }
  619. if writableOnly:
  620. return formats['writable']
  621. return formats['all']
  622. def GetSettingsPath():
  623. """!Get full path to the settings directory
  624. """
  625. try:
  626. verFd = open(os.path.join(ETCDIR, "VERSIONNUMBER"))
  627. version = int(verFd.readlines()[0].split(' ')[0].split('.')[0])
  628. except (IOError, ValueError, TypeError, IndexError), e:
  629. sys.exit(_("ERROR: Unable to determine GRASS version. Details: %s") % e)
  630. verFd.close()
  631. # keep location of settings files rc and wx in sync with lib/init/grass.py
  632. if sys.platform == 'win32':
  633. return os.path.join(os.getenv('APPDATA'), 'GRASS%d' % version)
  634. return os.path.join(os.getenv('HOME'), '.grass%d' % version)
  635. def StoreEnvVariable(key, value = None, envFile = None):
  636. """!Store environmental variable
  637. If value is not given (is None) then environmental variable is
  638. unset.
  639. @param key env key
  640. @param value env value
  641. @param envFile path to the environmental file (None for default location)
  642. """
  643. windows = sys.platform == 'win32'
  644. if not envFile:
  645. gVersion = grass.version()['version'].split('.', 1)[0]
  646. if not windows:
  647. envFile = os.path.join(os.getenv('HOME'), '.grass%s' % gVersion, 'bashrc')
  648. else:
  649. envFile = os.path.join(os.getenv('APPDATA'), 'GRASS%s' % gVersion, 'env.bat')
  650. # read env file
  651. environ = dict()
  652. lineSkipped = list()
  653. if os.path.exists(envFile):
  654. try:
  655. fd = open(envFile)
  656. except IOError, e:
  657. sys.stderr.write(_("Unable to open file '%s'\n") % envFile)
  658. return
  659. for line in fd.readlines():
  660. line = line.rstrip(os.linesep)
  661. try:
  662. k, v = map(lambda x: x.strip(), line.split(' ', 1)[1].split('=', 1))
  663. except StandardError, e:
  664. sys.stderr.write(_("%s: line skipped - unable to parse '%s'\n"
  665. "Reason: %s\n") % (envFile, line, e))
  666. lineSkipped.append(line)
  667. continue
  668. if k in environ:
  669. sys.stderr.write(_("Duplicated key: %s\n") % k)
  670. environ[k] = v
  671. fd.close()
  672. # update environmental variables
  673. if value is None and key in environ:
  674. del environ[key]
  675. else:
  676. environ[key] = value
  677. # write update env file
  678. try:
  679. fd = open(envFile, 'w')
  680. except IOError, e:
  681. sys.stderr.write(_("Unable to create file '%s'\n") % envFile)
  682. return
  683. if windows:
  684. expCmd = 'set'
  685. else:
  686. expCmd = 'export'
  687. for key, value in environ.iteritems():
  688. fd.write('%s %s=%s\n' % (expCmd, key, value))
  689. # write also skipped lines
  690. for line in lineSkipped:
  691. fd.write(line + os.linesep)
  692. fd.close()
  693. def SetAddOnPath(addonPath = None, key = 'PATH'):
  694. """!Set default AddOn path
  695. @addonPath path to addons (None for default)
  696. @key env key - 'PATH' or 'BASE'
  697. """
  698. gVersion = grass.version()['version'].split('.', 1)[0]
  699. # update env file
  700. if not addonPath:
  701. if sys.platform != 'win32':
  702. addonPath = os.path.join(os.path.join(os.getenv('HOME'),
  703. '.grass%s' % gVersion,
  704. 'addons'))
  705. else:
  706. addonPath = os.path.join(os.path.join(os.getenv('APPDATA'),
  707. 'GRASS%s' % gVersion,
  708. 'addons'))
  709. StoreEnvVariable(key = 'GRASS_ADDON_' + key, value = addonPath)
  710. os.environ['GRASS_ADDON_' + key] = addonPath
  711. # update path
  712. if addonPath not in os.environ['PATH']:
  713. os.environ['PATH'] = addonPath + os.pathsep + os.environ['PATH']
  714. # From lib/gis/col_str.c, except purple which is mentioned
  715. # there but not given RGB values
  716. str2rgb = {'aqua': (100, 128, 255),
  717. 'black': (0, 0, 0),
  718. 'blue': (0, 0, 255),
  719. 'brown': (180, 77, 25),
  720. 'cyan': (0, 255, 255),
  721. 'gray': (128, 128, 128),
  722. 'green': (0, 255, 0),
  723. 'grey': (128, 128, 128),
  724. 'indigo': (0, 128, 255),
  725. 'magenta': (255, 0, 255),
  726. 'orange': (255, 128, 0),
  727. 'purple': (128, 0, 128),
  728. 'red': (255, 0, 0),
  729. 'violet': (128, 0, 255),
  730. 'white': (255, 255, 255),
  731. 'yellow': (255, 255, 0)}
  732. rgb2str = {}
  733. for (s,r) in str2rgb.items():
  734. rgb2str[ r ] = s
  735. def color_resolve(color):
  736. if len(color) > 0 and color[0] in "0123456789":
  737. rgb = tuple(map(int, color.split(':')))
  738. label = color
  739. else:
  740. # Convert color names to RGB
  741. try:
  742. rgb = str2rgb[color]
  743. label = color
  744. except KeyError:
  745. rgb = (200, 200, 200)
  746. label = _('Select Color')
  747. return (rgb, label)