utils.py 28 KB

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