4 @brief Misc utilities for wxGUI
6 (C) 2007-2009, 2011 by the GRASS Development Team
8 This program is free software under the GNU General Public License
9 (>=v2). Read the file COPYING that comes with GRASS for details.
11 @author Martin Landa <landa.martin gmail.com>
12 @author Jachym Cepicky
25 sys.path.append(os.path.join(ETCDIR,
"python"))
34 """!Remove redundant whitespace from a string"""
35 return string.join(string.split(text),
' ')
38 """!Platform spefic shlex.split"""
39 if sys.platform ==
"win32":
40 return shlex.split(s.replace(
'\\',
r'\\'))
45 """!Creates GRASS temporary file using defined prefix.
47 @todo Fix path on MS Windows/MSYS
49 @param pref prefer the given path
51 @return Path to file name (string) or None
57 tempfile = ret.splitlines()[0].strip()
61 if platform.system() ==
'Windows':
62 tempfile = tempfile.replace(
"/",
"\\")
64 path, file = os.path.split(tempfile)
66 return os.path.join(pref, file)
74 """!Get map name from GRASS command
76 Parameter dcmd can be modified when first parameter is not
79 @param dcmd GRASS command (given as list)
80 @param fullyQualified change map name to be fully qualified
81 @param param params directory
82 @param layerType check also layer type ('raster', 'vector', '3d-raster', ...)
84 @return tuple (name, found)
92 if 'd.grid' == dcmd[0]:
94 elif 'd.geodesic' in dcmd[0]:
96 elif 'd.rhumbline' in dcmd[0]:
98 elif 'labels=' in dcmd[0]:
99 mapname = dcmd[idx].
split(
'=')[1] +
' labels'
102 for idx
in range(len(dcmd)):
104 p, v = dcmd[idx].
split(
'=', 1)
109 params = [(idx, p, v)]
112 if p
in (
'map',
'input',
113 'red',
'blue',
'green',
114 'h_map',
's_map',
'i_map',
115 'reliefmap',
'labels'):
116 params.append((idx, p, v))
122 if '=' not in dcmd[i]
and not dcmd[i].startswith(
'-'):
123 task = gtask.parse_interface(dcmd[0])
125 p = task.get_options()[
'params'][0].get(
'name',
'')
126 params.append((i, p, dcmd[i]))
130 return mapname,
False
133 return mapname,
False
137 for i, p, v
in params:
140 if fullyQualified
and '@' not in mapname:
141 if layerType
in (
'raster',
'vector',
'3d-raster',
'rgb',
'his'):
143 if layerType
in (
'raster',
'rgb',
'his'):
147 mapset = grass.find_file(mapname, element = findType)[
'mapset']
148 except AttributeError, e:
153 mapset = grass.gisenv()[
'MAPSET']
157 for i, p, v
in params:
159 dcmd[i] = p +
'=' + v
162 if i
in mapsets
and mapsets[i]:
163 dcmd[i] +=
'@' + mapsets[i]
166 for i, p, v
in params:
170 maps.append(dcmd[i].
split(
'=', 1)[1])
171 mapname =
'\n'.join(maps)
173 return mapname, found
176 """!Make layer name SQL compliant, based on G_str_to_sql()
178 @todo: Better use directly Ctypes to reuse venerable libgis C fns...
180 retName = str(name).strip()
184 retName, mapset = retName.split(
'@')
189 retNameList = list(retName)
190 for c
in retNameList:
191 if not (c >=
'A' and c <=
'Z')
and \
192 not (c >=
'a' and c <=
'z')
and \
193 not (c >=
'0' and c <=
'9'):
194 retNameList[cIdx] =
'_'
196 retName =
''.join(retNameList)
198 if not (retName[0] >=
'A' and retName[0] <=
'Z')
and \
199 not (retName[0] >=
'a' and retName[0] <=
'z'):
200 retName =
'x' + retName[1:]
203 retName = retName +
'@' + mapset
208 """!Convert list of category number to range(s)
210 Used for example for d.vect cats=[range]
212 @param cats category list
214 @return category range string
221 cats = map(int, cats)
230 if cats[i + next] == cats[j] - 1:
237 catstr +=
'%d-%d,' % (cats[i], cats[i + next])
240 catstr +=
'%d,' % (cats[i])
243 return catstr.strip(
',')
246 """!Get list of available/accessible mapsets
248 @param get method ('all', 'accessible', 'ordered')
250 @return list of mapsets
251 @return None on error
255 if get ==
'all' or get ==
'ordered':
263 mapsets = ret.splitlines()
268 if get ==
'accessible' or get ==
'ordered':
275 if get ==
'accessible':
276 mapsets = ret.splitlines()
278 mapsets_accessible = ret.splitlines()
279 for mapset
in mapsets_accessible:
280 mapsets.remove(mapset)
281 mapsets = mapsets_accessible + mapsets
288 """!Sort list items (not case-sensitive)"""
289 list.sort(cmp=
lambda x, y: cmp(x.lower(), y.lower()))
292 """!Get list of vector layers
294 @param vector name of vector map
295 @param parent parent window (to show dialog) or None
301 fullname = grass.find_file(name = vector, element =
'vector')[
'fullname']
303 Debug.msg(5,
"utils.GetVectorNumberOfLayers(): vector map '%s' not found" % vector)
313 sys.stderr.write(_(
"Vector map <%(map)s>: %(msg)s\n") % {
'map' : fullname,
'msg' : msg })
316 Debug.msg(1,
"GetVectorNumberOfLayers(): ret %s" % ret)
318 for line
in out.splitlines():
320 layer = line.split(
';')[0]
322 layer = layer.split(
'/')[0]
327 Debug.msg(3,
"utils.GetVectorNumberOfLayers(): vector=%s -> %s" % \
328 (fullname,
','.join(layers)))
333 """!Returns list of all vector layers as strings.
335 @param vector name of vector map
341 fullname = grass.find_file(name = vector, element =
'vector')[
'fullname']
343 Debug.msg(3,
"utils.GetAllVectorLayers(): vector map <%s> not found" % vector)
354 sys.stderr.write(_(
"Vector map <%(map)s>: %(msg)s\n") % {
'map' : fullname,
'msg' : msg })
357 Debug.msg(1,
"utils.GetAllVectorLayers(): ret %s" % ret)
359 for layer
in out.splitlines():
362 Debug.msg(3,
"utils.GetAllVectorLayers(): vector=%s -> %s" % \
363 (fullname,
','.join(layers)))
367 def Deg2DMS(lon, lat, string = True, hemisphere = True, precision = 3):
368 """!Convert deg value to dms string
370 @param lon longitude (x)
371 @param lat latitude (y)
372 @param string True to return string otherwise tuple
373 @param hemisphere print hemisphere
374 @param precision seconds precision
376 @return DMS string or tuple of values
377 @return empty string on error
413 slat = __ll_parts(flat, precision = precision)
414 slon = __ll_parts(flon, precision = precision)
417 return slon + hlon +
'; ' + slat + hlat
419 return (slon + hlon, slat + hlat)
422 """!Convert dms value to deg
424 @param lon longitude (x)
425 @param lat latitude (y)
427 @return tuple of converted values
428 @return ValueError on error
430 x = __ll_parts(lon, reverse =
True)
431 y = __ll_parts(lat, reverse =
True)
435 def __ll_parts(value, reverse = False, precision = 3):
436 """!Converts deg to d:m:s string
438 @param value value to be converted
439 @param reverse True to convert from d:m:s to deg
440 @param precision seconds precision (ignored if reverse is True)
442 @return converted value (string/float)
443 @return ValueError on error (reverse == True)
447 return '%s%.*f' % (
'00:00:0', precision, 0.0)
450 m = int((value - d) * 60)
451 s = ((value - d) * 60 - m) * 60
461 s =
'0%.*f' % (precision, s)
463 s =
'%.*f' % (precision, s)
465 return str(d) +
':' + m +
':' + s
468 d, m, s = value.split(
':')
473 d, m = value.split(
':')
487 if hs
not in (
'N',
'S',
'E',
'W'):
495 fs = float(s) / (60 * 60)
497 return coef * (float(d) + fm + fs)
501 Get GRASS command as string.
503 @param cmd GRASS command given as dictionary
505 @return command string
513 if 'flags' in cmd[1]:
514 for flag
in cmd[1][
'flags']:
516 for flag
in (
'verbose',
'quiet',
'overwrite'):
517 if flag
in cmd[1]
and cmd[1][flag]
is True:
520 for k, v
in cmd[1].iteritems():
521 if k
in (
'flags',
'verbose',
'quiet',
'overwrite'):
523 scmd +=
' %s=%s' % (k, v)
528 """!Convert command list to tuple for gcmd.RunCommand()"""
535 key, value = item.split(
'=', 1)
536 dcmd[str(key)] = str(value)
537 elif item[:2] ==
'--':
539 if flag
in (
'verbose',
'quiet',
'overwrite'):
540 dcmd[str(flag)] =
True
541 elif len(item) == 2
and item[0] ==
'-':
542 if 'flags' not in dcmd:
544 dcmd[
'flags'] += item[1]
546 module = gtask.parse_interface(cmd[0])
547 dcmd[module.define_first()] = item
549 return (cmd[0], dcmd)
552 """!Check path created by os.path.join"""
553 path = os.path.join(*args)
554 if platform.system() ==
'Windows' and \
556 return path[1].upper() +
':\\' + path[3:].replace(
'/',
'\\')
561 """!Read EPSG code from the file
563 @param path full path to the file with EPSG codes
565 @return dictionary of EPSG code
566 @return string on error
568 epsgCodeDict = dict()
573 return _(
"failed to open '%s'" % path)
577 for line
in f.readlines():
583 descr = line[1:].strip()
585 code, params = line.split(
" ", 1)
587 code = int(code.replace(
'<',
'').replace(
'>',
''))
592 epsgCodeDict[code] = (descr, params)
597 except StandardError, e:
603 """!Reproject coordinates
605 @param coord coordinates given as tuple
606 @param projOut output projection
607 @param projIn input projection (use location projection settings)
609 @return reprojected coordinates (returned as tuple)
619 stdin =
'%f|%f' % (coord[0], coord[1]),
622 coors = coors.split(
'\t')
624 n = coors[1].
split(
' ')[0].strip()
626 proj = projOut.split(
' ')[0].
split(
'=')[1]
629 if proj
in (
'll',
'latlong',
'longlat')
and 'd' not in flags:
630 return (proj, (e, n))
633 return (proj, (float(e), float(n)))
640 """!Get list of GRASS locations in given dbase
642 @param dbase GRASS database path
644 @return list of locations (sorted)
646 listOfLocations = list()
649 for location
in glob.glob(os.path.join(dbase,
"*")):
651 if os.path.join(location,
"PERMANENT")
in glob.glob(os.path.join(location,
"*")):
652 listOfLocations.append(os.path.basename(location))
655 except UnicodeEncodeError, e:
660 return listOfLocations
663 """!Get list of mapsets in given GRASS location
665 @param dbase GRASS database path
666 @param location GRASS location
667 @param selectable True to get list of selectable mapsets, otherwise all
669 @return list of mapsets - sorted (PERMANENT first)
671 listOfMapsets = list()
683 for line
in ret.rstrip().splitlines():
684 listOfMapsets += line.split(
' ')
686 for mapset
in glob.glob(os.path.join(dbase, location,
"*")):
687 if os.path.isdir(mapset)
and \
688 os.path.isfile(os.path.join(dbase, location, mapset,
"WIND")):
689 listOfMapsets.append(os.path.basename(mapset))
695 """!Get list of color tables"""
702 return ret.splitlines()
705 """!Decode string using system encoding
707 @param string string to be decoded
709 @return decoded string
714 enc = locale.getdefaultlocale()[1]
716 Debug.msg(5,
"DecodeString(): enc=%s" % enc)
717 return string.decode(enc)
722 """!Return encoded string using system locales
724 @param string string to be encoded
726 @return encoded string
730 enc = locale.getdefaultlocale()[1]
732 Debug.msg(5,
"EncodeString(): enc=%s" % enc)
733 return string.encode(enc)
737 def _getGDALFormats():
738 """!Get dictionary of avaialble GDAL drivers"""
739 ret = grass.read_command(
'r.in.gdal',
743 return _parseFormats(ret)
745 def _getOGRFormats():
746 """!Get dictionary of avaialble OGR drivers"""
747 ret = grass.read_command(
'v.in.ogr',
751 return _parseFormats(ret)
753 def _parseFormats(output):
754 """!Parse r.in.gdal/v.in.ogr -f output"""
755 formats = {
'file' : list(),
763 for line
in output.splitlines():
764 format = line.strip().rsplit(
':', -1)[1].strip()
765 if format
in (
'Memory',
'Virtual Raster',
'In Memory Raster'):
767 if format
in (
'PostgreSQL',
'SQLite',
768 'ODBC',
'ESRI Personal GeoDatabase',
770 'PostGIS WKT Raster driver',
772 formats[
'database'].append(format)
773 elif format
in (
'GeoJSON',
774 'OGC Web Coverage Service',
775 'OGC Web Map Service',
778 'HTTP Fetching Wrapper'):
779 formats[
'protocol'].append(format)
781 formats[
'file'].append(format)
783 for items
in formats.itervalues():
791 """!Get GDAL/OGR formats"""
795 'gdal' : _getGDALFormats(),
796 'ogr' : _getOGRFormats()
802 """!Get full path to the settings directory
805 verFd = open(os.path.join(ETCDIR,
"VERSIONNUMBER"))
806 version = int(verFd.readlines()[0].
split(
' ')[0].
split(
'.')[0])
807 except (IOError, ValueError, TypeError, IndexError), e:
808 sys.exit(_(
"ERROR: Unable to determine GRASS version. Details: %s") % e)
814 if sys.platform ==
'win32':
815 return os.path.join(os.getenv(
'APPDATA'),
'GRASS%d' % version)
817 return os.path.join(os.getenv(
'HOME'),
'.grass%d' % version)
820 """!Store environmental variable
822 If value is not given (is None) then environmental variable is
826 @param value env value
827 @param envFile path to the environmental file (None for default location)
829 windows = sys.platform ==
'win32'
832 envFile = os.path.join(os.getenv(
'HOME'),
'.grass.bashrc')
834 gVersion = grass.version()[
'version'].
split(
'.', 1)[0]
835 envFile = os.path.join(os.getenv(
'APPDATA'),
'GRASS%s' % gVersion,
'env.bat')
840 if os.path.exists(envFile):
844 sys.stderr.write(_(
"Unable to open file '%s'\n") % envFile)
846 for line
in fd.readlines():
847 line = line.rstrip(os.linesep)
849 k, v = map(
lambda x: x.strip(), line.split(
' ', 1)[1].
split(
'=', 1))
850 except StandardError, e:
852 sys.stderr.write(_(
"%(env)s: line skipped - unable to parse "
853 "Reason: %(e)s\n") % {
'env': envFile,
854 'line': line,
'e': e})
855 lineSkipped.append(line)
858 sys.stderr.write(_(
"Duplicated key: %s\n") % k)
864 if value
is None and key
in environ:
871 fd = open(envFile,
'w')
873 sys.stderr.write(_(
"Unable to create file '%s'\n") % envFile)
880 for key, value
in environ.iteritems():
881 fd.write(
'%s %s=%s\n' % (expCmd, key, value))
884 for line
in lineSkipped:
885 fd.write(line + os.linesep)
890 """!Set default AddOn path
892 @addonPath path to addons (None for default)
894 gVersion = grass.version()[
'version'].
split(
'.', 1)[0]
897 if sys.platform !=
'win32':
898 addonPath = os.path.join(os.path.join(os.getenv(
'HOME'),
899 '.grass%s' % gVersion,
902 addonPath = os.path.join(os.path.join(os.getenv(
'APPDATA'),
903 'GRASS%s' % gVersion,
907 os.environ[
'GRASS_ADDON_PATH'] = addonPath