utils.py 35 KB

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