123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- """!@package grass.script.vector
- @brief GRASS Python scripting module (vector functions)
- Vector related functions to be used in Python scripts.
- Usage:
- @code
- from grass.script import vector as grass
- grass.vector_db(map)
- ...
- @endcode
- (C) 2008-2010 by the GRASS Development Team
- This program is free software under the GNU General Public
- License (>=v2). Read the file COPYING that comes with GRASS
- for details.
- @author Glynn Clements
- @author Martin Landa <landa.martin gmail.com>
- """
- import os
- import types
- import copy
- import __builtin__
- from core import *
- # run "v.db.connect -g ..." and parse output
- def vector_db(map, **args):
- """!Return the database connection details for a vector map
- (interface to `v.db.connect -g'). Example:
-
- \code
- >>> grass.vector_db('lakes')
- {1: {'layer': 1, 'name': '',
- 'database': '/home/martin/grassdata/nc_spm_08/PERMANENT/dbf/',
- 'driver': 'dbf', 'key': 'cat', 'table': 'lakes'}}
- \endcode
-
- @param map vector map
- @param args other v.db.connect's arguments
-
- @return dictionary
- """
- s = read_command('v.db.connect', quiet = True, flags = 'g', map = map, sep = ';', **args)
- result = {}
-
- for l in s.splitlines():
- f = l.split(';')
- if len(f) != 5:
- continue
-
- if '/' in f[0]:
- f1 = f[0].split('/')
- layer = f1[0]
- name = f1[1]
- else:
- layer = f[0]
- name = ''
-
- result[int(layer)] = {
- 'layer' : int(layer),
- 'name' : name,
- 'table' : f[1],
- 'key' : f[2],
- 'database' : f[3],
- 'driver' : f[4] }
-
- return result
- def vector_layer_db(map, layer):
- """!Return the database connection details for a vector map layer.
- If db connection for given layer is not defined, fatal() is called.
-
- @param map map name
- @param layer layer number
-
- @return parsed output
- """
- try:
- f = vector_db(map)[int(layer)]
- except KeyError:
- fatal(_("Database connection not defined for layer %s") % layer)
- return f
- # run "v.info -c ..." and parse output
- def vector_columns(map, layer = None, getDict = True, **args):
- """!Return a dictionary (or a list) of the columns for the
- database table connected to a vector map (interface to `v.info
- -c').
- @code
- >>> vector_columns(urbanarea, getDict = True)
- {'UA_TYPE': {'index': 4, 'type': 'CHARACTER'}, 'UA': {'index': 2, 'type': 'CHARACTER'}, 'NAME': {'index': 3, 'type': 'CHARACTER'}, 'OBJECTID': {'index': 1, 'type': 'INTEGER'}, 'cat': {'index': 0, 'type': 'INTEGER'}}
- >>> vector_columns(urbanarea, getDict = False)
- ['cat', 'OBJECTID', 'UA', 'NAME', 'UA_TYPE']
- @endcode
-
- @param map map name
- @param layer layer number or name (None for all layers)
- @param getDict True to return dictionary of columns otherwise list of column names is returned
- @param args (v.info's arguments)
-
- @return dictionary/list of columns
- """
- s = read_command('v.info', flags = 'c', map = map, layer = layer, quiet = True, **args)
- if getDict:
- result = dict()
- else:
- result = list()
- i = 0
- for line in s.splitlines():
- ctype, cname = line.split('|')
- if getDict:
- result[cname] = { 'type' : ctype,
- 'index' : i }
- else:
- result.append(cname)
- i+=1
-
- return result
- # add vector history
- def vector_history(map):
- """!Set the command history for a vector map to the command used to
- invoke the script (interface to `v.support').
- @param map mapname
- @return v.support output
- """
- run_command('v.support', map = map, cmdhist = os.environ['CMDLINE'])
- # run "v.info -t" and parse output
- def vector_info_topo(map):
- """!Return information about a vector map (interface to `v.info
- -t'). Example:
- \code
- >>> grass.vector_info_topo('lakes')
- {'kernels': 0, 'lines': 0, 'centroids': 15279,
- 'boundaries': 27764, 'points': 0, 'faces': 0,
- 'primitives': 43043, 'islands': 7470, 'nodes': 35234, 'map3d': False, 'areas': 15279}
- \endcode
-
- @param map map name
- @return parsed output
- """
- s = read_command('v.info', flags = 't', map = map)
- ret = parse_key_val(s, val_type = int)
- if 'map3d' in ret:
- ret['map3d'] = bool(ret['map3d'])
-
- return ret
- # run "v.info -get ..." and parse output
- def vector_info(map):
- """!Return information about a vector map (interface to
- `v.info'). Example:
- \code
- >>> grass.vector_info('random_points')
- {'comment': '', 'projection': 'x,y', 'creator': 'soeren', 'holes': 0,
- 'primitives': 20, 'kernels': 0, 'scale': '1:1', 'title': '',
- 'west': 0.046125489999999998, 'top': 2376.133159, 'boundaries': 0,
- 'location': 'XYLocation', 'nodes': 0, 'east': 0.97305646000000001,
- 'source_date': 'Mon Aug 29 10:55:57 2011', 'north': 0.9589993,
- 'format': 'native', 'faces': 0, 'centroids': 0,
- 'digitization_threshold': '0.000000', 'islands': 0, 'level': 2,
- 'mapset': 'test', 'areas': 0, 'name': 'random_points',
- 'database': '/home/soeren/grassdata', 'bottom': 22.186596999999999,
- 'lines': 0, 'points': 20, 'map3d': True, 'volumes': 0, 'num_dblinks': 0,
- 'organization': '', 'south': 0.066047099999999997}
-
- \endcode
- @param map map name
-
- @return parsed vector info
- """
- s = read_command('v.info', flags = 'get', map = map)
-
- kv = parse_key_val(s)
- for k in ['north', 'south', 'east', 'west', 'top', 'bottom']:
- kv[k] = float(kv[k])
- for k in ['level', 'num_dblinks']:
- kv[k] = int(kv[k])
- for k in ['nodes', 'points', 'lines', 'boundaries', 'centroids', 'areas', 'islands', 'primitives']:
- kv[k] = int(kv[k])
- if 'map3d' in kv:
- kv['map3d'] = bool(int(kv['map3d']))
- if kv['map3d']:
- for k in ['faces', 'kernels', 'volumes', 'holes']:
- kv[k] = int(kv[k])
- return kv
- # interface for v.db.select
- def vector_db_select(map, layer = 1, **kwargs):
- """!Get attribute data of selected vector map layer.
- Function returns list of columns and dictionary of values ordered by
- key column value. Example:
- \code
- >>> print grass.vector_db_select('lakes')['columns']
- ['cat', 'AREA', 'PERIMETER', 'FULL_HYDRO', 'FULL_HYDR2', 'FTYPE', 'FCODE', 'NAME']
- >>> print grass.vector_db_select('lakes')['values'][3]
- ['3', '19512.86146', '708.44683', '4', '55652', 'LAKE/POND', '39000', '']
- >>> print grass.vector_db_select('lakes', columns = 'FTYPE')['values'][3]
- ['LAKE/POND']
- \endcode
- @param map map name
- @param layer layer number
- @param kwargs v.db.select options
- @return dictionary ('columns' and 'values')
- """
- try:
- key = vector_db(map = map)[layer]['key']
- except KeyError:
- error(_('Missing layer %(layer)d in vector map <%(map)s>') % \
- { 'layer' : layer, 'map' : map })
- return { 'columns' : [], 'values' : {} }
-
- if 'columns' in kwargs:
- if key not in kwargs['columns'].split(','):
- # add key column if missing
- debug("Adding key column to the output")
- kwargs['columns'] += ',' + key
-
- ret = read_command('v.db.select',
- map = map,
- layer = layer,
- **kwargs)
-
- if not ret:
- error(_('vector_db_select() failed'))
- return { 'columns' : [], 'values' : {} }
-
- columns = []
- values = {}
- for line in ret.splitlines():
- if not columns:
- columns = line.split('|')
- key_index = columns.index(key)
- continue
-
- value = line.split('|')
- key_value = int(value[key_index])
- values[key_value] = line.split('|')
-
- return { 'columns' : columns,
- 'values' : values }
- # interface to v.what
- def vector_what(map, coord, distance = 0.0, ttype = None):
- """!Query vector map at given locations
-
- To query one vector map at one location
- @code
- 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}]
- @endcode
- To query one vector map with multiple layers (no additional parameters required)
- @code
- for q in grass.vector_what(map = 'some_map', coord = (596532.357143,4920486.21429), distance = 100.0):
- print q['Map'], q['Layer'], q['Attributes']
- new_bug_sites 1 {'str1': 'Beetle_site', 'GRASSRGB': '', 'cat': '80'}
- new_bug_sites 2 {'cat': '80'}
- @endcode
- To query more vector maps at one location
- @code
- 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'}
- @endcode
- To query one vector map at more locations
- @code
- for q in grass.vector_what(map = 'archsites', coord = [(595743, 4925281), (597950, 4918898)],
- distance = 250):
- print q['Map'], q['Attributes']
- archsites {'str1': 'No_Name', 'cat': '8'}
- archsites {'str1': 'Bob_Miller', 'cat': '22'}
- @endcode
- @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)
- @return parsed list
- """
- if "LC_ALL" in os.environ:
- locale = os.environ["LC_ALL"]
- os.environ["LC_ALL"] = "C"
- if type(map) in (types.StringType, types.UnicodeType):
- map_list = [map]
- else:
- map_list = map
-
- layer_list = ['-1'] * len(map_list)
-
- coord_list = list()
- if type(coord) is types.TupleType:
- coord_list.append('%f,%f' % (coord[0], coord[1]))
- else:
- for e, n in coord:
- coord_list.append('%f,%f' % (e, n))
-
- cmdParams = dict(quiet = True,
- flags = 'ag',
- map = ','.join(map_list),
- layer = ','.join(layer_list),
- coordinates = ','.join(coord_list),
- distance = float(distance))
- if ttype:
- cmdParams['type'] = ','.join(ttype)
- ret = read_command('v.what',
- **cmdParams)
-
- if "LC_ALL" in os.environ:
- os.environ["LC_ALL"] = locale
-
- data = list()
- if not ret:
- return data
-
- dict_attrb = None
- dict_map = None
- dict_layer = None
- attr_pseudo_key = 'Attributes'
- for item in ret.splitlines():
- try:
- key, value = __builtin__.map(lambda x: x.strip(), item.split('=', 1))
- except ValueError:
- continue
- if key in ('East', 'North'):
- continue
-
- if key == 'Map':
- # attach the last one from the previous map
- if dict_map is not None:
- dict_main = copy.copy(dict_map)
- if dict_layer is not None:
- dict_main.update(dict_layer)
- data.append(dict_main)
- dict_map = { key : value }
- dict_layer = None
- dict_attrb = None
- elif key == 'Layer':
- # attach the last the previous Layer
- if dict_layer is not None:
- dict_main = copy.copy(dict_map)
- dict_main.update(dict_layer)
- data.append(dict_main)
- dict_layer = { key: int(value) }
- dict_attrb = None
- elif key == 'Key_column':
- dict_layer[key] = value
- dict_attrb = dict()
- dict_layer[attr_pseudo_key] = dict_attrb
- elif dict_attrb is not None:
- dict_attrb[key] = value
- elif dict_layer is not None:
- if key == 'Category':
- dict_layer[key] = int(value)
- else:
- dict_layer[key] = value
- else:
- dict_map[key] = value
- # TODO: there are some keys which has non-string values
- # examples: Sq_Meters, Hectares, Acres, Sq_Miles
-
- # attach the last one
- if dict_map is not None:
- dict_main = copy.copy(dict_map)
- if dict_layer:
- dict_main.update(dict_layer)
- data.append(dict_main)
-
- return data
|