def get_interface_description(cmd): """Returns the XML description for the GRASS cmd (force text encoding to "utf-8"). The DTD must be located in $GISBASE/gui/xml/grass-interface.dtd, otherwise the parser will not succeed. :param cmd: command (name of GRASS module) """ try: p = Popen([cmd, "--interface-description"], stdout=PIPE, stderr=PIPE) cmdout, cmderr = p.communicate() # TODO: do it better (?) if not cmdout and sys.platform == "win32": # we in fact expect pure module name (without extension) # so, lets remove extension if cmd.endswith(".py"): cmd = os.path.splitext(cmd)[0] if cmd == "d.rast3d": sys.path.insert(0, os.path.join(os.getenv("GISBASE"), "gui", "scripts")) p = Popen( [sys.executable, get_real_command(cmd), "--interface-description"], stdout=PIPE, stderr=PIPE, ) cmdout, cmderr = p.communicate() if cmd == "d.rast3d": del sys.path[0] # remove gui/scripts from the path if p.returncode != 0: raise ScriptError( _( "Unable to fetch interface description for command '<{cmd}>'." "\n\nDetails: <{det}>" ).format(cmd=cmd, det=decode(cmderr)) ) except OSError as e: raise ScriptError( _( "Unable to fetch interface description for command '<{cmd}>'." "\n\nDetails: <{det}>" ).format(cmd=cmd, det=e) ) desc = convert_xml_to_utf8(cmdout) desc = desc.replace( b"grass-interface.dtd", os.path.join(os.getenv("GISBASE"), "gui", "xml", "grass-interface.dtd").encode( "utf-8" ), ) return desc
def _create_location_xy(database, location): """Create unprojected location Raise ScriptError on error. :param database: GRASS database where to create new location :param location: location name """ cur_dir = os.getcwd() try: os.chdir(database) os.mkdir(location) os.mkdir(os.path.join(location, 'PERMANENT')) # create DEFAULT_WIND and WIND files regioninfo = [ 'proj: 0', 'zone: 0', 'north: 1', 'south: 0', 'east: 1', 'west: 0', 'cols: 1', 'rows: 1', 'e-w resol: 1', 'n-s resol: 1', 'top: 1', 'bottom: 0', 'cols3: 1', 'rows3: 1', 'depths: 1', 'e-w resol3: 1', 'n-s resol3: 1', 't-b resol: 1' ] defwind = open(os.path.join(location, "PERMANENT", "DEFAULT_WIND"), 'w') for param in regioninfo: defwind.write(param + '%s' % os.linesep) defwind.close() shutil.copy(os.path.join(location, "PERMANENT", "DEFAULT_WIND"), os.path.join(location, "PERMANENT", "WIND")) os.chdir(cur_dir) except OSError as e: raise ScriptError(repr(e))
def parse_interface(name, parser=processTask, blackList=None): """Parse interface of given GRASS module The *name* is either GRASS module name (of a module on path) or a full or relative path to an executable. :param str name: name of GRASS module to be parsed :param parser: :param blackList: """ try: tree = etree.fromstring(get_interface_description(name)) except ETREE_EXCEPTIONS as error: raise ScriptError( _( "Cannot parse interface description of" "<{name}> module: {error}" ).format(name=name, error=error) ) task = parser(tree, blackList=blackList).get_task() # if name from interface is different than the originally # provided name, then the provided name is likely a full path needed # to actually run the module later # (processTask uses only the XML which does not contain the original # path used to execute the module) if task.name != name: task.path = name return task
def fatal(msg): """Display an error message using `g.message -e`, then abort Raise exception when raise_on_error is 'True'. :param str msg: error message to be displayed """ global raise_on_error if raise_on_error: raise ScriptError(msg) error(msg) sys.exit(1)
def make_command(prog, flags="", overwrite=False, quiet=False, verbose=False, **options): """Return a list of strings suitable for use as the args parameter to Popen() or call(). Example: >>> make_command("g.message", flags = 'w', message = 'this is a warning') ['g.message', '-w', 'message=this is a warning'] :param str prog: GRASS module :param str flags: flags to be used (given as a string) :param bool overwrite: True to enable overwriting the output (<tt>--o</tt>) :param bool quiet: True to run quietly (<tt>--q</tt>) :param bool verbose: True to run verbosely (<tt>--v</tt>) :param options: module's parameters :return: list of arguments """ args = [prog] if overwrite: args.append("--o") if quiet: args.append("--q") if verbose: args.append("--v") if flags: if '-' in flags: raise ScriptError("'-' is not a valid flag") args.append("-%s" % flags) for opt, val in options.iteritems(): if val != None: if opt.startswith('_'): opt = opt[1:] warning( _("To run the module add underscore at the end" " of the option <%s> to avoid conflict with Python" " keywords. Underscore at the beginning is" " depreciated in GRASS GIS 7.0 and will be removed" " in version 7.1.") % opt) elif opt.endswith('_'): opt = opt[:-1] args.append("%s=%s" % (opt, _make_val(val))) return args
def test_get_value(self): error = ScriptError('error') self.assertEqual('error', error.value)
def test_str(self): error = ScriptError('error') self.assertEqual('error', str(error))
def test_get_value(self): error = ScriptError("error") self.assertEqual("error", error.value)
def test_str(self): error = ScriptError("error") self.assertEqual("error", str(error))
def vector_what( map, coord, distance=0.0, ttype=None, encoding=None, skip_attributes=False, layer=None, multiple=False, env=None, ): """Query vector map at given locations To query one vector map at one location :: print grass.vector_what(map='archsites', coord=(595743, 4925281), distance=250) [{'Category': 8, 'Map': 'archsites', 'Layer': 1, 'Key_column': 'cat', 'Database': '/home/martin/grassdata/spearfish60/PERMANENT/dbf/', 'Mapset': 'PERMANENT', 'Driver': 'dbf', 'Attributes': {'str1': 'No_Name', 'cat': '8'}, 'Table': 'archsites', 'Type': 'Point', 'Id': 8}] To query one vector map with multiple layers (no additional parameters required) :: for q in grass.vector_what(map='some_map', distance=100.0, coord=(596532.357143,4920486.21429)): print q['Map'], q['Layer'], q['Attributes'] new_bug_sites 1 {'str1': 'Beetle_site', 'GRASSRGB': '', 'cat': '80'} new_bug_sites 2 {'cat': '80'} To query more vector maps at one location :: for q in grass.vector_what(map=('archsites', 'roads'), coord=(595743, 4925281), distance=250): print q['Map'], q['Attributes'] archsites {'str1': 'No_Name', 'cat': '8'} roads {'label': 'interstate', 'cat': '1'} To query one vector map at more locations :: for q in grass.vector_what(map='archsites', distance=250, coord=[(595743, 4925281), (597950, 4918898)]): print q['Map'], q['Attributes'] archsites {'str1': 'No_Name', 'cat': '8'} archsites {'str1': 'Bob_Miller', 'cat': '22'} :param map: vector map(s) to query given as string or list/tuple :param coord: coordinates of query given as tuple (easting, northing) or list of tuples :param distance: query threshold distance (in map units) :param ttype: list of topology types (default of v.what are point, line, area, face) :param encoding: attributes encoding :param skip_attributes: True to skip quering attributes :param layer: layer number or list of layers (one for each vector), if None, all layers (-1) are used :param multiple: find multiple features within threshold distance :param env: environment :return: parsed list """ if not env: env = os.environ.copy() if "LC_ALL" in env: env["LC_ALL"] = "C" if isinstance(map, (bytes, unicode)): map_list = [map] else: map_list = map if layer: if isinstance(layer, (tuple, list)): layer_list = [str(item) for item in layer] else: layer_list = [str(layer)] if len(layer_list) != len(map_list): raise ScriptError( _("Number of given vector maps ({m}) " "differs from number of layers ({l})").format( m=len(map_list), l=len(layer_list))) else: layer_list = ["-1"] * len(map_list) coord_list = list() if isinstance(coord, tuple): coord_list.append("%f,%f" % (coord[0], coord[1])) else: for e, n in coord: coord_list.append("%f,%f" % (e, n)) flags = "j" if not skip_attributes: flags += "a" if multiple: flags += "m" cmdParams = dict( quiet=True, flags=flags, map=",".join(map_list), layer=",".join(layer_list), coordinates=",".join(coord_list), distance=float(distance), ) if ttype: cmdParams["type"] = ",".join(ttype) try: ret = read_command("v.what", env=env, **cmdParams).strip() except CalledModuleError as e: raise ScriptError(e.msg) data = list() if not ret: return data # lazy import global json global orderedDict if json is None: import json if orderedDict is None: try: from collections import OrderedDict orderedDict = OrderedDict except ImportError: orderedDict = dict kwargs = {} if encoding: kwargs["encoding"] = encoding if sys.version_info[0:2] > (2, 6): kwargs["object_pairs_hook"] = orderedDict try: result = json.loads(ret, **kwargs) except ValueError: raise ScriptError( _("v.what output is not valid JSON format:\n {ret}").format( ret=ret)) if multiple: for vmap in result["Maps"]: features = vmap.pop("Features", None) if features: for feature in features: cats = feature.pop("Categories", None) if cats: for cat in cats: tmp = feature.copy() tmp.update(cat) tmp2 = vmap.copy() tmp2.update(tmp) data.append(tmp2) else: for vmap in result["Maps"]: cats = vmap.pop("Categories", None) if cats: for cat in cats: tmp = vmap.copy() tmp.update(cat) data.append(tmp) else: data.append(vmap) return data
def create_location(dbase, location, epsg=None, proj4=None, filename=None, wkt=None, datum=None, datum_trans=None, desc=None, overwrite=False): """Create new location Raise ScriptError on error. :param str dbase: path to GRASS database :param str location: location name to create :param epsg: if given create new location based on EPSG code :param proj4: if given create new location based on Proj4 definition :param str filename: if given create new location based on georeferenced file :param str wkt: if given create new location based on WKT definition (path to PRJ file) :param datum: GRASS format datum code :param datum_trans: datum transformation parameters (used for epsg and proj4) :param desc: description of the location (creates MYNAME file) :param bool overwrite: True to overwrite location if exists(WARNING: ALL DATA from existing location ARE DELETED!) """ gisdbase = None if epsg or proj4 or filename or wkt: # FIXME: changing GISDBASE mid-session is not background-job safe gisdbase = gisenv()['GISDBASE'] run_command('g.gisenv', set='GISDBASE=%s' % dbase) # create dbase if not exists if not os.path.exists(dbase): os.mkdir(dbase) # check if location already exists if os.path.exists(os.path.join(dbase, location)): if not overwrite: warning( _("Location <%s> already exists. Operation canceled.") % location) return else: warning( _("Location <%s> already exists and will be overwritten") % location) shutil.rmtree(os.path.join(dbase, location)) kwargs = dict() if datum: kwargs['datum'] = datum if datum_trans: kwargs['datum_trans'] = datum_trans if epsg: ps = pipe_command('g.proj', quiet=True, flags='t', epsg=epsg, location=location, stderr=PIPE, **kwargs) elif proj4: ps = pipe_command('g.proj', quiet=True, flags='t', proj4=proj4, location=location, stderr=PIPE, **kwargs) elif filename: ps = pipe_command('g.proj', quiet=True, georef=filename, location=location, stderr=PIPE) elif wkt: ps = pipe_command('g.proj', quiet=True, wkt=wkt, location=location, stderr=PIPE) else: _create_location_xy(dbase, location) if epsg or proj4 or filename or wkt: error = ps.communicate()[1] run_command('g.gisenv', set='GISDBASE=%s' % gisdbase) if ps.returncode != 0 and error: raise ScriptError(repr(error)) try: fd = codecs.open(os.path.join(dbase, location, 'PERMANENT', 'MYNAME'), encoding='utf-8', mode='w') if desc: fd.write(desc + os.linesep) else: fd.write(os.linesep) fd.close() except OSError as e: raise ScriptError(repr(e))