utils.py 33 KB

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