[GRASS-dev] problem with parse_command python

Hi all, I'm working for a new module using python but I have a strange
problem with parse_command and v.what. When I have a "good dictionary"
(setting parse = (grass.parse_key_val, { 'sep' : ':' })) I can see
only the attributes of one feature, with a "bad dictionary" I see all
the features but I can't work with them

Here the code

coor='647220.0,5084994.0'
map_tiles='test_tile'

## this return all the features but the dictionary is bad

grass.parse_command('v.what',flags = 'a', map = map_tiles, east_north = coor)

100%
{'': None, 'Category: 2': None, 'location :
/grassdata/patUTM32/ortofoto_baldo/cellhd/22a_4597_tagliata': None,
'North: 5084994': None, 'Category: 4': None, 'cat : 1': None,
'location : /grassdata/patUTM32/ortofoto_baldo/cellhd/22_1684_tagliata':
None, 'cat : 2': None, 'cat : 5': None, 'Table: test_tile': None,
'location : /grassdata/patUTM32/ortofoto_baldo/cellhd/22a_4596_tagliata':
None, 'Acres: 571.248': None, 'location :
/grassdata/patUTM32/ortofoto_baldo/cellhd/22a_4598_tagliata': None,
'cat : 3': None, 'Key column: cat': None, 'Sq Miles: 0.8926': None,
'Sq Meters: 2311760.000': None, 'Hectares: 231.176': None, 'location :
/grassdata/patUTM32/ortofoto_baldo/cellhd/22_1683_tagliata': None,
'East: 647220': None, 'Layer: 2': None, 'Layer: 1': None, 'Mapset:
ortofoto_baldo': None, 'Category: 1': None, 'Category: 3': None,
'Driver: dbf': None, 'Category: 5': None, 'Map: test_tile': None,
'Type: Area': None, 'Database:
/grassdata/patUTM32/ortofoto_baldo/dbf/': None, 'cat : 4': None}

##this return only a features but the dictionary is good

grass.parse_command('v.what',flags = 'a', map = map_tiles, east_north = coor, parse = (grass.parse_key_val, { 'sep' : ':' }))

100%
{'': None, 'Map': ' test_tile ', 'Layer': ' 2', 'North': ' 5084994',
'Sq Miles': ' 0.8926', 'Database': '
/grassdata/patUTM32/ortofoto_baldo/dbf/', 'Type': ' Area', 'cat': '
5', 'Driver': ' dbf', 'Category': ' 5', 'Acres': ' 571.248',
'Hectares': ' 231.176', 'Sq Meters': ' 2311760.000', 'location': '
/grassdata/patUTM32/ortofoto_baldo/cellhd/22a_4598_tagliata', 'Table':
' test_tile', 'East': ' 647220', 'Mapset': ' ortofoto_baldo', 'Key
column': ' cat'}

I also try to use flags = 'ag', but I can only have the second
solution (with one features)

Is there a problem in parse_command or I do something wrong?

thanks
Luca

Luca Delucchi wrote:

Hi all, I'm working for a new module using python but I have a strange
problem with parse_command and v.what. When I have a "good dictionary"
(setting parse = (grass.parse_key_val, { 'sep' : ':' })) I can see
only the attributes of one feature, with a "bad dictionary" I see all
the features but I can't work with them

I also try to use flags = 'ag', but I can only have the second
solution (with one features)

Is there a problem in parse_command or I do something wrong?

parse_key_val returns a dictionary, so the keys must be unique. If you
have duplicate keys, only the last value will be returned.

For v.what, you'll need to parse the output yourself.

For the future, we should consider fixing v.what to use a sane (i.e.
non-modal) output format for -g.

--
Glynn Clements <glynn@gclements.plus.com>

2010/12/23 Glynn Clements <glynn@gclements.plus.com>:

Hi Glynn, thanks for the answer

parse_key_val returns a dictionary, so the keys must be unique. If you
have duplicate keys, only the last value will be returned.

For v.what, you'll need to parse the output yourself.

ok, I understood it

For the future, we should consider fixing v.what to use a sane (i.e.
non-modal) output format for -g.

I try to create a general function for v.what, it return a dictionary
of dictionary

def vector_what(map,x,y):
  """!Return the result of v.what using the map and coordinates passed

  @param map vector map name
  @param x x coordinate
  @param y y coordinate

  @return parsed output in dictionary

  """
  result = {}
  # create string for east_north param
  coor = str(x) + ',' + str(y)

  fields = grass.read_command('v.what',flags = 'a', map = map,
east_north = coor)
  # split the different features
  fields = fields.split('\n\n')
  # value for number of features
  value = 0
  # delete the first block, is only the coordinates to query
  del fields[0]
  # for each block
  for field in fields:
    # create a temporary dictionary
    temp_dic = {}
    # for each block split lines
    lines = field.splitlines()
    # for each line
    for line in lines:
      kv = line.split(':', 1)
      if len(kv) > 1:
  temp_dic[kv[0].strip()] = str(kv[1])
    # this is the firs block it contain general information about map
    if value == 0:
      result['general'] = temp_dic
    # features
    else:
      result['feature'+str(value)] = temp_dic
    value = value + 1
  return result

what do you think? maybe it isn't the best solution but it work fine...
the code is available to be integrated into vector.py

--
Glynn Clements <glynn@gclements.plus.com>

best regards
Luca

Luca Delucchi wrote:

I try to create a general function for v.what, it return a dictionary
of dictionary

  # create string for east_north param
  coor = str(x) + ',' + str(y)

This isn't necessary; you can pass numbers, lists and tuples to the
grass.*_command() functions and they will be converted automatically.

  fields = grass.read_command('v.what',flags = 'a', map = map, east_north = coor)

I suggest using the -g flag.

  # split the different features
  fields = fields.split('\n\n')

This isn't correct. Without -g, the blank line occurs betwen the
Layer: and Category: lines and the rest of the per-layer information.
You need to scan through the lines and start a new dictionary whenever
you find a "Layer" line.

--
Glynn Clements <glynn@gclements.plus.com>

2010/12/29 Glynn Clements <glynn@gclements.plus.com>:

# create string for east_north param
coor = str(x) + ',' + str(y)

This isn't necessary; you can pass numbers, lists and tuples to the
grass.*_command() functions and they will be converted automatically.

ok, I change them with a list

fields = grass.read_command('v.what',flags = 'a', map = map, east_north = coor)

I suggest using the -g flag.

ok, but read down....

# split the different features
fields = fields.split('\n\n')

This isn't correct. Without -g, the blank line occurs betwen the
Layer: and Category: lines and the rest of the per-layer information.

I don't know if this is correct, look the answer of "fields =
grass.read_command('v.what',flags = 'a', map = map_tiles, east_north =
pointslist)"

print fields

East: 636280
North: 5079748

Map: test_tile
Mapset: ortofoto_baldo
Type: Area
Sq Meters: 770560.000
Hectares: 77.056
Acres: 190.410
Sq Miles: 0.2975
Layer: 1
Category: 1

Driver: dbf
Database: /grassdata/patUTM32/ortofoto_baldo/dbf/
Table: test_tile
Key column: cat
cat : 1
location : /grassdata/patUTM32/ortofoto_baldo/cellhd/22_1683_tagliata
Layer: 1
Category: 13

Driver: dbf
Database: /grassdata/patUTM32/ortofoto_baldo/dbf/
Table: test_tile
Key column: cat
cat : 13
location : /grassdata/patUTM32/ortofoto_baldo/cellhd/24a_3001_tagliata
Layer: 2
Category: 2

I think the first word of new feature is Driver, I wrong?

You need to scan through the lines and start a new dictionary whenever
you find a "Layer" line.

ok, here the new code....

def vector_what(map,coor):
  """!Return the result of v.what using the map and coordinates passed

  @param map vector map name
  @param coor a list of x,y coordinates

  @return parsed output in dictionary

  """
  result = {}
  # create string for east_north param
  fields = grass.read_command('v.what',flags = 'ag', map = map,
east_north = coor)
  #split lines
  fields = fields.splitlines()
  # value for number of features
  value = 0
  # create a temporary dictionary
  temp_dic = {}
  # for each line
  for field in fields:
    # split key and value
    kv = field.split('=', 1)
    if len(kv) > 1:
      #Start the new feature
      if kv[0].strip() == 'Driver':
  # if value is 0 dictionary contain general information about map
  if value == 0:
    result['general'] = temp_dic
  # else features
  else:
    result['feature'+str(value)] = temp_dic
  # create a new temporary dictionary
  temp_dic = {}
  # add driver to the new dictionary
  temp_dic[kv[0].strip()] = str(kv[1])
  # value for the new feature
  value = value + 1
      else:
  # add value to the dictionary
  temp_dic[kv[0].strip()] = str(kv[1])
    # add the last feature
    result['feature'+str(value)] = temp_dic
  return result

--
Glynn Clements <glynn@gclements.plus.com>

thanks
Luca

www.lucadelu.org

Hi,

2010/12/29 Luca Delucchi <lucadeluge@gmail.com>:

[...]

take a look at vector_what() introduced in r44767.

Martin

--
Martin Landa <landa.martin gmail.com> * http://geo.fsv.cvut.cz/~landa

Luca Delucchi wrote:

> This isn't correct. Without -g, the blank line occurs betwen the
> Layer: and Category: lines and the rest of the per-layer information.

I don't know if this is correct, look the answer of "fields =
grass.read_command('v.what',flags = 'a', map = map_tiles, east_north =
pointslist)"

This breaks down as follows:

Header:

print fields

East: 636280
North: 5079748

Map: test_tile
Mapset: ortofoto_baldo
Type: Area
Sq Meters: 770560.000
Hectares: 77.056
Acres: 190.410
Sq Miles: 0.2975

First category:

Layer: 1
Category: 1

Driver: dbf
Database: /grassdata/patUTM32/ortofoto_baldo/dbf/
Table: test_tile
Key column: cat
cat : 1
location : /grassdata/patUTM32/ortofoto_baldo/cellhd/22_1683_tagliata

Second category:

Layer: 1
Category: 13

Driver: dbf
Database: /grassdata/patUTM32/ortofoto_baldo/dbf/
Table: test_tile
Key column: cat
cat : 13
location : /grassdata/patUTM32/ortofoto_baldo/cellhd/24a_3001_tagliata

Third category:

Layer: 2
Category: 2

The code which generates this output is in vector/v.what/what.c:

  if (Cats->n_cats > 0) {
      int j;
      char *formbuf1, *formbuf2;

      for (j = 0; j < Cats->n_cats; j++) {
    G_debug(2, "field = %d category = %d\n", Cats->field[j],
      Cats->cat[j]);
    if (script) {
        fprintf(stdout, "Layer=%d\nCategory=%d\n", Cats->field[j],
          Cats->cat[j]);
    }
    else {
        fprintf(stdout, _("Layer: %d\nCategory: %d\n"),
          Cats->field[j], Cats->cat[j]);
    }
    Fi = Vect_get_field(&(Map[i]), Cats->field[j]);
    if (Fi != NULL && showextra) {
        if (script) {
      fprintf(stdout,
        "Driver=%s\nDatabase=%s\nTable=%s\nKey_column=%s\n",
        Fi->driver, Fi->database, Fi->table, Fi->key);
        }
        else {
      fprintf(stdout,
        _("\nDriver: %s\nDatabase: %s\nTable: %s\nKey column: %s\n"),
        Fi->driver, Fi->database, Fi->table, Fi->key);
        }
        F_generate(Fi->driver, Fi->database, Fi->table,
             Fi->key, Cats->cat[j], &form);

        if (script) {
      formbuf1 = G_str_replace(form, " : ", "=");
      formbuf2 = G_str_replace(formbuf1, " ", "_");
      fprintf(stdout, "%s", formbuf2);
      G_free(formbuf1);
      G_free(formbuf2);
        }
        else
      fprintf(stdout, "%s", form);
        G_free(form);
        G_free(Fi);
    }
      }
  }

I think the first word of new feature is Driver, I wrong?

I think that you're getting confused by the fact that the second layer
doesn't appear to have attribute data attached, and the extra blank
line being inserted between the data from the map itself and the
associated attribute data.

--
Glynn Clements <glynn@gclements.plus.com>