utils.py 31 KB

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